Services

Here are some code examples for a Basic Service, Authorization, Metrics and Database Facilities and Transports

Go here for a more detailed look into Services: getting_started

Basic Service

hello_world.py

from omni.services.core import main

def hello_world():
    return "Hello World!"

main.register_endpoint("get", "/hello-world", hello_world)

That is all there is needed to write a service. To run this with Omniverse Kit and an HTTP transport:

./kit \
--exec hello_world.py \
--enable omni.services.core \
--enable omni.services.transport.server.http \
--/exts/omni.kit.registry.nucleus/registries/0/name=kit/services \
--/exts/omni.kit.registry.nucleus/registries/0/url=https://dw290v42wisod.cloudfront.net/exts/kit/services

Facilities

For information on Facilities: Introduction

Authorization Facility

extension.py

import omni.ext

from omni.services.core import main
from omni.services.security.auth import apikey

from .services.sample import router

class SampleAuthorizationFacilityExtension(omni.ext.IExt):
    """Sample Extension illustrating usage of the Authorization Facility."""

    def on_startup(self) -> None:
        main.register_router(router, prefix="/sample-auth", tags=["sample"], dependencies=[apikey.OmniApiKeyHeader()])

    def on_shutdown(self) -> None:
        main.deregister_router(router=router, prefix="/sample-auth")

By default this will just check for the presence of X-API-KEY in the headers, but OmniApiKeyHeader can be further customized with functions:

custom_auth.py

async def validate(api_key: str) -> None:
    if api_key != "foo":
        raise Exception("Invalid API key.")

main.register_endpoint(
    router,
    prefix="/sample-auth",
    tags=["sample"],
    dependencies=[apikey.OmniApiKeyHeader(check_functions=[validate])],
)

It can also be configured via settings. These can go into either in the extensions.toml, an application’s .kit file or via the command-line:

extension.toml

[settings]
exts."omni.services.security.auth.apikey".auth_function=["omni.services.security.auth.apikey.auth_functions.validate_from_settings_list"]

Database Facility

extension.toml

# [...]

[settings.exts."my.extension.name".dbs.database-handle]
connection_string = "mysql:///host.name"
min_size = 4
max_size = 10

Any additional arguments can also be supplied as querystring arguments to the connection string. For example, the following is functionally equivalent to the settings above:

extension.toml

# [...]

[settings.exts."my.extension.name".dbs.database-handle]
connection_string = "mysql:///host.name?min_size=4&max_size=10"

Once provided, a connection to a database can be obtained by referring to its handle, provided as key to the dbs property of the Extension settings. For example, using the configuration sample provided above:

sample.py

database_facility = DatabaseManagerFacility("my.extension.name")
async with database_facility.get("database-handle") as db:
    # Create a table:
    query = "CREATE TABLE IF NOT EXISTS HighScores (id INTEGER PRIMARY KEY, name VARCHAR(100), score INTEGER)"
    await db.execute(query=query)

    # Insert some data:
    query = "INSERT INTO HighScores(name, score) VALUES (:name, :score)"
    values = [
        {"name": "Daisy", "score": 92},
        {"name": "Neil", "score": 87},
        {"name": "Carol", "score": 43},
    ]
    await database.execute_many(query=query, values=values)

    # Execute a database query:
    query = "SELECT * FROM HighScores"
    rows = await database.fetch_all(query=query)
    print("High scores:", rows)

Metrics Facility

Configuration: The Metrics Facility can be accessed from a Kit Service by first registering the Facility’s instance on the Service’s router:

metrics/extension.py

import omni.ext

from omni.services.core import main
from omni.services.facilities.monitoring.metrics.facilities import MetricsFacility
from .services.sample import router


class SampleMetricsFacilityExtension(omni.ext.IExt):
    """Sample Extension illustrating usage of the Progress Facility."""

    def on_startup(self) -> None:
        router.register_facility(name="metrics", facility_inst=MetricsFacility("sample"))
        main.register_router(router=router, prefix="/sample-progress", tags=["example"])

    def on_shutdown(self) -> None:
        main.deregister_router(router=router, prefix="/sample-progress")

Once configured, the Metrics Facility then becomes available to the Service’s endpoints via dependency injection:

metrics/services/sample.py

from omni.services.core import routers
from omni.services.facilities.monitoring.progress.facilities import MetricsFacility

router = routers.ServiceAPIRouter()


@router.post("/sample-endpoint")
async def sample(
    metrics_facility: MetricsFacility = router.get_facility("metrics"),
) -> Dict:
    frame_count = metrics_facility.counter(
        name="processed_frames",
        documentation="Number of frames processed for a given USD stage",
        labelnames=("stage", "agent", "camera"),
        unit="frames",
    )
    process_time = metrics_facility.summaries(
        name="process_time",
        documentation="Time taken to process a frame",
        labelnames=("stage", "agent", "camera"),
    )

    # [...]

    for frame in frame_range:
        with process_time.labels(stage=stage_name, agent=agent, camera=camera).time():
            await _process_next_frame()
        frame_count.labels(stage=stage_name, agent=agent, camera=camera).inc()

    return {"success": True}

For a service that is “always on”, it is then possible to scrape the metrics on the /metrics endpoint by default. To set and use a specific job name, use the following setting:

metrics/config/extension.toml

[settings]
exts."services.monitoring.metrics".job_name = "sample-service"

For services that shutdown after each request, for example in a “Function as a service” type set up, it is also possible to push the metrics periodically to a pushgateway. This can be configured in the settings using the following settings:

Metrics Settings

Setting

Type

Description

push_metrics

Boolean

Indicating if metrics should be pushed (default: false)

push_gateway

String

Endpoint to push the metrics to (default: http://localhost:9091, see Note)

push_interval

Integer

Value indicating how many seconds should pass between pushing the metrics

Note

Default endpoint: this assumes a Prometheus Pushgateway to be installed and running on the local machine, remote hosts can be used as well.

metrics/app/example.kit

[settings]
exts."services.monitoring.metrics".push_metrics = true
exts."services.monitoring.metrics".push_gateway = "http://localhost:9091"
exts."services.monitoring.metrics".push_interval = 10

Transports

Transports allow different protocols and communication channels to be used with the Omniverse microservices stack.

Introduction to Transports