Skip to main content

Call the admission API

If something other than Slurm, LSF, or Kubernetes starts your jobs, such as another scheduler, a portal, a CI system, or a launcher script, call the LicOps admission API yourself. Any tool that can send an HTTP POST with a JSON body can do it, and you install nothing on the LicOps side.

Start from the integration kit​

The integration kit from OpenLM has two ready clients that already handle the ordering and every failure path.

Wrap any command​

To wrap any command, use bash/openlm-wrap.sh. It holds one seat for as long as the command runs, and releases it however the command ends:

export OL_ROUTER_URL=http://<licops-host>:8100
./bash/openlm-wrap.sh MLM MATLAB -- matlab -batch mysim

It needs curl and jq. OL_COUNT sets the number of seats. OL_FAIL_MODE decides what happens when LicOps is unreachable, and defaults to closed: the command does not run.

Call from Python​

From Python, use python/openlm_licops.py. It needs only the Python 3.7 standard library:

import os
from openlm_licops import LicOpsClient

ol = LicOpsClient("http://<licops-host>:8100")

with ol.seat(vendor="MLM", feature="MATLAB", count=1) as seat:
target = seat.dispatch(node_ip="10.42.7.31", job_id="918273")
os.environ["LM_LICENSE_FILE"] = target.license_file
run_the_application()
# The seat is released on the way out, including when the block raises.

To build a client in another language, the kit has an OpenAPI 3.0 description of the three endpoints, openapi/openlm-licops-admission.yaml. Import it into an API tool, or generate a client from it.

The three requests​

All three are POST requests with a JSON body, under http://<licops-host>:8100/api/v1/admission-controller. For every field and response, see API reference.

Admit, before the job is queued​

Generate a session ID first, a UUID, and keep it until the job ends. You pass the same ID to all three requests, and it is what makes a retry safe: an admit repeated with the same ID returns the original answer instead of counting a second seat, as long as the session is still valid. A denied admit is not stored, so repeating it asks again. Reuse an ID only with the same vendor, feature, and seat count: anything else is refused with 400. Use a new ID for every new job.

curl -s -X POST http://<licops-host>:8100/api/v1/admission-controller/admit \
-H 'Content-Type: application/json' \
-d '{"sessionId":"8e29c1b2-6d3a-4f0e-9d51-7c2a4b6e8f10","vendor":"MLM","feature":"MATLAB","user":"jdoe","host":"login01","totalQuantity":1}'
{ "decision": "Admit", "sessionId": "8e29c1b2-6d3a-4f0e-9d51-7c2a4b6e8f10", "expiresAt": "2026-10-15T11:42:19Z", "reason": null }

A 200 means LicOps reached a decision, not that it admitted the job. Read decision:

  • Admit: the seat is counted for this session until expiresAt, 15 minutes by default, unless you dispatch first.
  • Deny: nothing was counted. reason says why. See Deny reasons. Do not start the job. You can ask again later.

You never name a server. LicOps chooses it, applying your admission rules.

Dispatch, when the job starts​

Call dispatch on the node that runs the job, as late as you can, and use the server it returns:

curl -s -X POST http://<licops-host>:8100/api/v1/admission-controller/dispatch \
-H 'Content-Type: application/json' \
-d '{"sessionId":"8e29c1b2-6d3a-4f0e-9d51-7c2a4b6e8f10","executionNodeIp":"10.42.7.31","jobId":"918273"}'
{
"sessionId": "8e29c1b2-6d3a-4f0e-9d51-7c2a4b6e8f10",
"hostName": "lic01.example.com",
"port": "27000",
"licenseFile": "27000@lic01.example.com",
"fallbackTargets": [
{ "hostName": "lic02.example.com", "port": "27000", "licenseFile": "27000@lic02.example.com" }
]
}

Set LM_LICENSE_FILE, or the application's own variable such as MLM_LICENSE_FILE, to licenseFile, exactly as returned. For a three-server redundant license server, it already lists all three machines. To give the application other servers to fall back on, join fallbackTargets behind it with : on Linux or ; on Windows:

export LM_LICENSE_FILE=27000@lic01.example.com:27000@lic02.example.com

The server can differ from the one LicOps planned at admit time: if that server failed in between, dispatch moves the seat to a healthy one. Always use what dispatch returns. Dispatch also extends the session to its running timeout, 2 hours by default.

Release, when the job ends​

To release the seat, run this command:

curl -s -X POST http://<licops-host>:8100/api/v1/admission-controller/release \
-H 'Content-Type: application/json' \
-d '{"sessionId":"8e29c1b2-6d3a-4f0e-9d51-7c2a4b6e8f10"}'

Release always answers 200, so it is safe to call from a trap or finally block that might run twice, or for a job that was never admitted. Set it up before you admit, so no path out of the job skips it. If LicOps is unreachable at release, log it and continue: never fail a job because its release failed. LicOps reclaims the seat when the session times out.

Handle the answers that are not errors​

The following table lists the answers that are not errors, what they mean, and what to do:

AnswerWhat it meansWhat to do
Admit 503 with Retry-AfterLicOps started moments ago and has not caught up with OpenLM Platform.Wait for the Retry-After seconds, 10 by default, and admit again with the same session ID. Do not fail the job.
Admit 400The body is invalid: an empty sessionId, vendor, or feature, or a seat count below 1, or a session ID reused with a different vendor, feature, or seat count.Fix the request. An empty shell variable is the usual cause.
Dispatch 404No such session: the ID is wrong, the session already ended, or the admit timeout ran out before the job started.Admit again with a new session ID. Do not retry the dispatch.
Dispatch 503, ThrottleDeferredA Throttle rule is pacing checkouts.Wait for Retry-After and dispatch again with the same session ID. The seat is still yours, so do not admit again.
Dispatch 503, NoCapacityAtDispatchThe server the seat was on failed, and no other server in the pool has a free seat.LicOps has released the session and sends no Retry-After. Admit again with a new session ID.

Checklist for your own client​

Confirm that your client does the following:

  • Generate the session ID yourself and keep it until the job ends.
  • Treat 200 from admit as "a decision arrived", then read decision.
  • Treat 503 from admit as a pause, not a refusal.
  • Log NoServerAvailable and NoCapacity separately: the first usually means a misspelled vendor or feature, the second a real shortage.
  • Dispatch after the job is placed, and use the server it returns.
  • On 503 ThrottleDeferred from dispatch, dispatch again with the same ID. On 503 NoCapacityAtDispatch, admit again with a new ID.
  • Release from code that runs on every ending and set it up before you admit.
  • Never fail a job because its release failed.
  • Use a short HTTP timeout, from 2 to 10 seconds, so a slow LicOps cannot stall your submissions.
  • Decide separately what admit and dispatch do when LicOps is unreachable, and write the decision down.

The documentation team might draft this page using a large language model. A technical writer reviews every page and verifies it against the product before publishing it. A human makes the final decision.