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:
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.