Skip to content

License Manager Agent Reference

License Server Parsers

lm_agent.parsing.flexlm

Parser for FlexLM

parse

parse(server_output: str) -> Dict

Parse the FlexLM Output, using regext to match the lines we need: - feature line: info about the license - data line: info about the users using the license

parse_feature_line

parse_feature_line(line: str) -> Optional[Dict]

Parse the feature line in the FlexLM output. Data we need: - feature: license name - total: total amount of licenses - used: quantity of licenses being used

parse_usage_line

parse_usage_line(line: str) -> Optional[LicenseUsesItem]

Parse the usage line in the FlexLM output. Data we need: - username: user who booked the license - lead_host: host using the license - booked: quantity of licenses being used

There can be multiple formats for the data line, so we need to check which one matches.

lm_agent.parsing.rlm

Parser for RLM

parse

parse(server_output: str) -> dict

Parse the output from the RLM server. Data we need: - feature: license name - count: total amount of licenses - in_use: quantity of licenses being use

parse_count_line

parse_count_line(line: str) -> Optional[dict]

Parse the count line in the RLM output. Data we need: - count: total amount of licenses - in_use: quantity of licenses being use

parse_feature_line

parse_feature_line(line: str) -> Optional[str]

Parse the feature line in the RLM output. Data we need: - feature: license name

parse_usage_line

parse_usage_line(line: str) -> Optional[dict]

Parse the usage line in the RLM output. Data we need: - feature: license name - username: user name - lead_host: lead host - license_used_by_host: quantity of licenses being use

lm_agent.parsing.lsdyna

Parser for LS-Dyna

parse

parse(s: str)

Parse the LS-Dyna output, using regex to match the lines we need: - program line: info about each license - usage line: info about users using licenses - total line: info about the pool, which affects all licenses

Since the usage line doesn't have the name of the license in use, we're saving each parsed license in a list. This way, we can find which license the user is using by checking the last parsed license in this list.

Also, all the licenses in the server will have their used value filled with the group used value, since it'll reflect the correct amount of free licenses in the pool.

parse_program_line

parse_program_line(line: str)

Parse the program line in the LS-Dyna output. Data we need: - program: license name - max: total amount of licenses in the server The usedvalue will be evaluated later.

parse_total_line

parse_total_line(line: str)

Parse the total line in the LS-Dyna output. Data we need: - used: quantity of licenses being used in the server

Obs: all LS-Dyna licenses in the server share the same license pool, where the amount of available licenses can be consumed by any license in the server.

The used value is the sum of all licenses in use in the pool. Since we calculate the available quantity as available = total - used, we'll use the group used value for all licenses, so the available will reflect the correct amount of free licenses in the pool.

parse_usage_line

parse_usage_line(line: str)

Parse the usage line in the LS-Dyna output. Data we need: - username: user who booked the license - lead_host: host using the license - booked: quantity of licenses being used

Obs: this line doesn't include the license name. The license in use is the last one parsed before this line.

lm_agent.parsing.lmx

Parser for LM-X

parse

parse(server_output: str) -> dict

Parse the LM-X output using regex to match the lines we need: -feature line: info about each license -in use line: info about licenses in use -usage line: info about users using licenses

Since the in use and usage line don't have the name of the license in use, we're saving each parsed license in a list. This way, we can find which license is being used by checking the last parsed license in the list.

parse_feature_line

parse_feature_line(line: str) -> Optional[str]

Parse the feature line in the LM-X output. Data we need: - feature: license name

parse_in_use_line

parse_in_use_line(line: str) -> Optional[dict]

Parse the in use line in the LM-X output. Data we need: - in_use: quantity of licenses being use - total: total amount of licenses

Obs: this line doesn't include the license name. The license in use is the last one parsed before this line.

parse_usage_line

parse_usage_line(line: str) -> Optional[LicenseUsesItem]

Parse the usage line in the LS-Dyna output. Data we need: -username: user who booked the license -lead_host: host using the license -booked: quantity of licenses being used

Obs: this line also doesn't incluse the license name. The license in use is the last one parsed before this line.

lm_agent.parsing.olicense

Parser for OLicense

parse

parse(server_output: str) -> dict

Parse the OLicense output using regex to match the lines we need: -feature line: info about each license -in use line: info about licenses in use -usage line: info about users using licenses

Since the "in use" and "usage" lines don't have the name of the license in use, we're saving each parsed license in a list. This way, we can find which license is being used by checking the last parsed license in the list.

If a feature has more than one license associated with it, the total amount of licenses is the sum of all licenses with the same name.

Example of output

... ftire_adams; FreeFloating; 3; 2022-12-31 23:59:59; 1 FloatsLockedBy: sbhyma@RD0087712 #1 ... ftire_adams; FreeFloating; 1; 2023-02-28 23:59:00;

This would be parsed as

"ftire_adams": {"total": 4, "used" 1, "uses": { "user_name": "sbhyma", "lead_host": "RD0087712", "booked": 1 }}

parse_feature_line

parse_feature_line(line: str) -> Optional[dict]

Parse the "feature" line in the OLicense output. Data we need: - feature: license name - total: total amount of licenses

Obs: OLicense supports more than one license with the same feature name. In case a feature has more than one license associated with it, the total amount of licenses is the sum of all licenses with the same name.

parse_in_use_line

parse_in_use_line(line: str) -> Optional[int]

Parse the "in use" line in the Olicense output. Data we need: - in_use: quantity of licenses being used by each user

Obs: this line doesn't include the license name. The license in use is the last one parsed before this line. It also doesn't include the user name. The user using the license is the next "usage" line parsed after this line. The total amount of licenses being used is the sum of all "in use" lines.

parse_usage_line

parse_usage_line(line: str) -> Optional[LicenseUsesItem]

Parse the usage line in the Olicense output. Data we need: -username: user who booked the license -lead_host: host using the license -booked: quantity of licenses booked by the user

Obs: this line also doesn't include the license name. The license in use is the last one parsed before this line.

lm_agent.parsing.dsls

Parser for DSLS.

parse

parse(server_output: str) -> dict[str, ParsedFeatureItem]

Parse the output from the DSLS server. Data we need: - feature: license name - count: total amount of licenses - in_use: quantity of licenses being use - uses: list of users using the license

Obs: if the feature is used by multiple users, each usage will result in a line with the same feature information, just changing the usage data.

parse_feature_dict

parse_feature_dict(feature_dict: dict[str, str]) -> Optional[ParsedFeatureItem]

Parse the feature dcit in the DSLS output.

Data we need: - feature: license name - total: total amount of licenses - used: quantity of licenses being used

parse_usage_dict

parse_usage_dict(usage_dict: dict[str, str]) -> Optional[LicenseUsesItem]

Parse the usage dict in the DSLS output.

Data we need: - username: user who booked the license - lead_host: host using the license - booked: quantity of licenses being used

License Server Interfaces

lm_agent.server_interfaces.flexlm

FlexLM license server interface.

FlexLMLicenseServer

Bases: LicenseServerInterface

Extract license information from FlexLM license server.

get_commands_list
get_commands_list() -> typing.List[typing.List[str]]

Generate a list of commands with the available license server hosts.

get_output_from_server async
get_output_from_server(product_feature: str)

Override abstract method to get output from FlexLM license server.

get_report_item async
get_report_item(feature_id: int, product_feature: str)

Override abstract method to parse FlexLM license server output into License Report Item.

lm_agent.server_interfaces.rlm

RLM license server interface.

RLMLicenseServer

Bases: LicenseServerInterface

Extract license information from RLM license server.

get_commands_list
get_commands_list() -> typing.List[typing.List[str]]

Generate a list of commands with the available license server hosts.

get_output_from_server async
get_output_from_server()

Override abstract method to get output from RLM license server.

get_report_item async
get_report_item(feature_id: int, product_feature: str)

Override abstract method to parse RLM license server output into License Report Item.

lm_agent.server_interfaces.lsdyna

LS-Dyna license server interface.

LSDynaLicenseServer

Bases: LicenseServerInterface

Extract license information from LS-Dyna license server.

__init__
__init__(license_servers: typing.List[LicenseServerSchema])

Initialize the license server instance with the license server host and parser.

get_commands_list
get_commands_list() -> typing.List[typing.List[str]]

Generate a list of commands with the available license server hosts.

get_output_from_server async
get_output_from_server()

Override abstract method to get output from Ls-Dyna license server.

get_report_item async
get_report_item(feature_id: int, product_feature: str)

Override abstract method to parse LS-Dyna license server output into License Report Item.

lm_agent.server_interfaces.lmx

LM-X license server interface.

LMXLicenseServer

Bases: LicenseServerInterface

Extract license information from LM-X license server.

__init__
__init__(license_servers: typing.List[LicenseServerSchema])

Initialize the license server instance with the license server host and parser.

get_commands_list
get_commands_list() -> typing.List[typing.List[str]]

Generate a list of commands with the available license server hosts.

get_output_from_server async
get_output_from_server()

Override abstract method to get output from LM-X license server.

get_report_item async
get_report_item(feature_id: int, product_feature: str)

Override abstract method to parse LM-X license server output into License Report Item.

lm_agent.server_interfaces.olicense

OLicense license server interface.

OLicenseLicenseServer

Bases: LicenseServerInterface

Extract license information from OLicense license server.

__init__
__init__(license_servers: typing.List[LicenseServerSchema])

Initialize the license server instance with the license server host and parser.

get_commands_list
get_commands_list() -> typing.List[typing.List[str]]

Generate a list of commands with the available license server hosts.

get_output_from_server async
get_output_from_server()

Override abstract method to get output from OLicense license server.

get_report_item async
get_report_item(feature_id: int, product_feature: str)

Override abstract method to parse OLicense license server output into License Report Item.

lm_agent.server_interfaces.dsls

DSLS license server interface.

DSLSLicenseServer

Bases: LicenseServerInterface

Extract license information from DSLS license server.

__init__
__init__(license_servers: typing.List[LicenseServerSchema])

Initialize the license server instance with the license server host and parser.

get_commands_list
get_commands_list() -> typing.List[typing.Dict]

Generate a list of commands with the available license server hosts.

get_output_from_server async
get_output_from_server()

Override abstract method to get output from DSLS license server.

get_report_item async
get_report_item(feature_id: int, product_feature: str)

Override abstract method to parse DSLS license server output into License Report Item.

License Server Services

lm_agent.services.clean_jobs_and_bookings

Service to clean jobs and bookings that are no longer needed.

clean_bookings_by_usage async

clean_bookings_by_usage(cluster_jobs: List[JobSchema], license_report: List[LicenseReportItem])

Delete bookings if they match with a usage line in the license report.

The bookings and the usage lines are mapped by a unique key composed by: * feature_id * username * lead_host * quantity

This will group all bookings and all usages with the same information.

The bookings can be deleted if the number of usages with the key matches the number of bookings with the key.

  • If there are more than one booking matching with the same usage, there's no way of knowning which booking relates to the usage. In this case, the bookings should be deleted by the grace time clean up.

  • If there are more than one usage matching the same booking, the booking could relate to any of the usages. In this case, the booking should be deleted by the grace time clean up.

If there's an equal amount of usages and bookings, all the bookings have checked out their licenses from the license server, which means the bookings can be safely deleted by this clean up.

clean_jobs_and_bookings async

clean_jobs_and_bookings(cluster_configurations: List[ConfigurationSchema], cluster_jobs: List[JobSchema], squeue_result: List[Dict], license_report: List[LicenseReportItem])

Clean the jobs and bookings that are no longer needed.

The jobs can be deleted by: * Deleting the jobs that don't have any bookings. * Deleting the jobs that are no longer running. * Deleting the jobs that are running longer than the grace time.

The bookings can be deleted by: * Deleting the bookings that have checked out their licenses from the license server.

clean_jobs_by_grace_time async

clean_jobs_by_grace_time(cluster_jobs: List[JobSchema], squeue_result: List[Dict], grace_times: Dict[int, int]) -> List[JobSchema]

Clean the jobs where running time is greater than the grace_time.

If the job has more than one booking, it will be deleted once the running time is greater than the greatest grace_time for any of the bookings.

clean_jobs_no_longer_running async

clean_jobs_no_longer_running(cluster_jobs: List[JobSchema], squeue_result: List[Dict]) -> List[JobSchema]

Clean the jobs that aren't running along with its bookings.

clean_jobs_without_bookings async

clean_jobs_without_bookings(cluster_jobs: List[JobSchema]) -> List[JobSchema]

Clean the jobs that don't have any bookings.

extract_bookings_from_job

extract_bookings_from_job(job: JobSchema) -> List[ExtractedBookingSchema]

Extract all the bookings information from a job.

extract_usages_from_report

extract_usages_from_report(report_item: LicenseReportItem) -> List[ExtractedUsageSchema]

Extract all the the usage information from a feature report

Note that the lead_host from the license server comes with the full domain, but the lead_host from the job comes without the domain. This is why the lead_host is split by the dot and only the first part is used.

get_bookings_mapping

get_bookings_mapping(cluster_jobs: List[JobSchema]) -> Dict[Tuple[int, str, str, int], List[ExtractedBookingSchema]]

Map the bookings by creating a key with the required information for the matching.

get_cluster_grace_times

get_cluster_grace_times(cluster_configurations: List[ConfigurationSchema]) -> Dict[int, int]

Get the grace time for each feature_id in the cluster.

get_greatest_grace_time_for_job

get_greatest_grace_time_for_job(grace_times: Dict[int, int], job_bookings: List[BookingSchema]) -> int

Find the greatest grace time among the features booked by the given job_id.

If the job has more than one booking, only the greatest grace time will be returned.

get_usages_mapping

get_usages_mapping(license_report: List[LicenseReportItem]) -> Dict[Tuple[int, str, str, int], List[ExtractedUsageSchema]]

Map the usages by creating a key with the required information for the matching.

lm_agent.services.license_report

Invoke license stat tools to build a view of license token counts.

get_local_license_configurations

get_local_license_configurations(license_configurations: typing.List[ConfigurationSchema], local_licenses: typing.List[str]) -> typing.List[ConfigurationSchema]

Return the license configurations from the backend that are configured on the cluster.

report async

report() -> typing.List[LicenseReportItem]

Get stat counts using a license stat tool.

This function iterates over the available license_servers and associated features configured via LICENSE_SERVER_FEATURES and generates a report by requesting license information from the license_server_type.

The return from the license server is used to reconcile license-manager's view of what features are available with what actually exists in the license server database.

update_features async

update_features() -> typing.List[LicenseReportItem]

Send the license data collected from the cluster to the backend.

lm_agent.services.reconciliation

Reconciliation functionality live here.

reconcile async

reconcile()

Generate the report and reconcile the license feature token usage.

License Server Backend Utils

lm_agent.backend_utils.utils

Provide utilities that communicate with the backend.

AsyncBackendClient

Bases: AsyncClient

Extends the httpx.AsyncClient class with automatic token acquisition for requests. The token is acquired lazily on the first httpx request issued.

This client should be used for most agent actions.

acquire_token

acquire_token() -> str

Retrieves a token from OIDC based on the app settings.

check_backend_health async

check_backend_health()

Hit the API's health-check endpoint to make sure the API is available.

get_all_features_bookings_sum async

get_all_features_bookings_sum() -> Dict[str, int]

Get booking sum for a license's bookings in all clusters.

Note: a license can be configured in multiple clusters, having the same name but different configurations.

The booking sum is the sum of all bookings for a license in all clusters.

get_all_features_from_backend async

get_all_features_from_backend() -> List[FeatureSchema]

Return the job with its bookings for the given job_id in the cluster.

get_cluster_configs_from_backend async

get_cluster_configs_from_backend() -> List[ConfigurationSchema]

Get all configs from the backend for the cluster.

get_cluster_jobs_from_backend async

get_cluster_jobs_from_backend() -> List[JobSchema]

Get all jobs for the cluster with its bookings from the backend.

make_booking_request async

make_booking_request(lbr: LicenseBookingRequest) -> bool

Create a job and its bookings on the backend for each license booked.

make_feature_update async

make_feature_update(features_to_update: List[Dict])

Update the feature with its current counters.

remove_booking async

remove_booking(booking_id: int)

Remove the booking with the given id.

remove_job_by_slurm_job_id async

remove_job_by_slurm_job_id(slurm_job_id: str)

Remove the job with its bookings for the given slurm_job_id in the cluster.

If the job doesn't exist, the request will be ignored.

report_cluster_status async

report_cluster_status()

Report the cluster status to the backend.

lm_agent.models

BookingSchema pydantic-model

Bases: BaseModel

Represents the booking of a feature.

Fields:

  • id (int)
  • job_id (int)
  • feature_id (int)
  • quantity (int)

ConfigurationSchema pydantic-model

Bases: BaseModel

Represents the configuration for a set of features.

Fields:

  • id (int)
  • name (str)
  • cluster_client_id (str)
  • features (List[FeatureSchema])
  • license_servers (List[LicenseServerSchema])
  • grace_time (int)
  • type (LicenseServerType)

ExtractedBookingSchema pydantic-model

Bases: BaseModel

Represents the booking from a job with the job information extracted.

Fields:

  • booking_id (int)
  • job_id (int)
  • slurm_job_id (str)
  • username (str)
  • lead_host (str)
  • feature_id (int)
  • quantity (int)

ExtractedUsageSchema pydantic-model

Bases: BaseModel

Representes the usage lines extracted from a feature report.

Fields:

  • feature_id (int)
  • username (str)
  • lead_host (str)
  • quantity (int)

FeatureSchema pydantic-model

Bases: BaseModel

Represents the features in a feature configuration.

Fields:

  • id (int)
  • name (str)
  • product (ProductSchema)
  • config_id (int)
  • reserved (int)
  • total (int)
  • used (int)
  • booked_total (int)

JobSchema pydantic-model

Bases: BaseModel

Represents the jobs submitted in a cluster.

Fields:

  • id (int)
  • slurm_job_id (str)
  • cluster_client_id (str)
  • username (str)
  • lead_host (str)
  • bookings (List[BookingSchema])

LicenseBooking pydantic-model

Bases: BaseModel

Structure to represent a license booking.

Fields:

  • product_feature (str)
  • quantity (int)

LicenseBookingRequest pydantic-model

Bases: BaseModel

Structure to represent a list of license bookings.

Fields:

  • slurm_job_id (str)
  • username (str)
  • lead_host (str)
  • bookings (List[LicenseBooking])

LicenseReportItem pydantic-model

Bases: BaseModel

An item in a LicenseReport, a count of tokens for one product/feature.

Fields:

  • feature_id (int)
  • product_feature (str)
  • used (int)
  • total (int)
  • uses (List[LicenseUsesItem])

LicenseServerSchema pydantic-model

Bases: BaseModel

License server response from the database.

Fields:

  • id (int)
  • config_id (int)
  • host (str)
  • port (PositiveInt)

LicenseUsesItem pydantic-model

Bases: BaseModel

A list of usage information for a license.

Fields:

  • username (str)
  • lead_host (str)
  • booked (int)

ParsedFeatureItem pydantic-model

Bases: BaseModel

A report of the parsed license server output.

Fields:

ProductSchema pydantic-model

Bases: BaseModel

Represents a feature's product.

Fields:

  • id (int)
  • name (str)