OpenTelemetry Python - OpAMP Client

OpenTelemetry Python - OpAMP client

This package provides a bunch of classes that can be used by OpenTelemetry distributions implementers to implement remote config support via the OpAMP protocol.

The client implements the following capabilities:

  • ReportsStatus

  • ReportsHeartbeat

  • AcceptsRemoteConfig

  • ReportsRemoteConfig

  • ReportsEffectiveConfig

These capabilities are enough to get a remote config from an OpAMP server, parse it, apply it and ack it.

While the client supports pluggable transports, only an HTTP backends using the requests library is implemented. Adding WebSocket support shouldn’t be hard but it will require some rework in the OpAMPAgent class.

Since OpAMP APIs, config options or environment variables are not standardizes the distros are required to provide code doing so. OTel Python distros would need to provide their own OpAMPCallbacks subclass that implements the actual change of whatever configuration their backends sends.

Please note that the API is not finalized yet and so the name is called _opamp with the underscore.

Usage

import os

from opentelemetry._opamp.agent import OpAMPAgent
from opentelemetry._opamp.callbacks import OpAMPCallbacks
from opentelemetry._opamp.client import OpAMPClient
from opentelemetry.sdk._configuration import _OTelSDKConfigurator
from opentelemetry.sdk.resources import OTELResourceDetector


class MyCallbacks(OpAMPCallbacks):
    def on_message(self, agent, client, message):
        if message.remote_config is None:
            return
        for config_filename, config in message.remote_config.config.config_map.items():
            print("do something")


class MyOpenTelemetryConfigurator(_OTelSDKConfigurator):
    def _configure(self, **kwargs):
        super()._configure(**kwargs)

        enable_opamp = False
        endpoint = os.environ.get("OTEL_PYTHON_OPAMP_ENDPOINT")
        if endpoint:
            # this is not great but we don't have the calculated resource attributes around
            # see https://github.com/open-telemetry/opentelemetry-python/pull/4646 for creating
            # an entry point distros can implement
            resource = OTELResourceDetector().detect()
            agent_identifying_attributes = {
                "service.name": resource.attributes.get("service.name"),
            }
            opamp_client = OpAMPClient(
                endpoint=endpoint,
                agent_identifying_attributes=agent_identifying_attributes,
            )
            opamp_agent = OpAMPAgent(
                interval=30,
                callbacks=MyCallbacks(),
                client=opamp_client,
            )
            opamp_agent.start()

API

class opentelemetry._opamp.MessageData(remote_config=None)[source]

Bases: object

Structured view of a ServerToAgent message for callback consumption.

Only fields the agent is expected to act on are exposed. Flags and error_response are handled internally by the client before this object reaches the callback.

remote_config: Optional[AgentRemoteConfig] = None
classmethod from_server_message(message)[source]
Return type:

MessageData

class opentelemetry._opamp.OpAMPAgent(*, interval=30, callbacks, max_retries=10, heartbeat_max_retries=1, initial_backoff=1.0, client)[source]

Bases: object

OpAMPAgent handles:
  • periodic “heartbeat” calls enqueued at a fixed interval

  • on-demand calls via send()

  • exponential backoff retry on failures

  • immediate cancellation of all jobs on shutdown

start()[source]

Starts the scheduler and worker threads.

Return type:

None

send(payload, max_retries=None, callback=None)[source]

Enqueue an on-demand request.

Return type:

None

stop(timeout=None)[source]

Signal server we are disconnecting and then threads to exit

Parameters:

timeout (Optional[float]) – seconds to wait for each thread to join

Return type:

None

class opentelemetry._opamp.OpAMPCallbacks[source]

Bases: ABC

OpAMP client callbacks with no-op defaults.

All methods have no-op defaults so that subclasses only need to override the callbacks they care about. New callbacks can be added in the future without breaking existing subclasses.

on_connect(agent, client)[source]

Called when the connection is successfully established to the Server. For HTTP clients this is called for any request if the response status is OK.

Return type:

None

on_connect_failed(agent, client, error)[source]

Called when the connection to the Server cannot be established. May also be called if the connection is lost and reconnection attempt fails.

Return type:

None

on_error(agent, client, error_response)[source]

Called when the Server reports an error in response to a previously sent request. Useful for logging purposes. The Agent should not attempt to process the error by reconnecting or retrying previous operations. The client handles the UNAVAILABLE case internally by performing retries as necessary.

Return type:

None

on_message(agent, client, message)[source]

Called when the Agent receives a message that needs processing.

Return type:

None

class opentelemetry._opamp.OpAMPClient(*, endpoint, headers=None, timeout_millis=1000, agent_identifying_attributes, agent_non_identifying_attributes=None, transport=None, tls_certificate=True, tls_client_certificate=None, tls_client_key=None)[source]

Bases: object

OpAMPClient implement the helpers for building and sending messages that the agent will use.

build_agent_disconnect_message()[source]
Return type:

bytes

build_heartbeat_message()[source]
Return type:

bytes

update_effective_config(effective_config, content_type)[source]
Return type:

EffectiveConfig

update_remote_config_status(remote_config_hash, status, error_message='')[source]
Return type:

Optional[RemoteConfigStatus]

build_remote_config_status_response_message(remote_config_status)[source]
Return type:

bytes

build_full_state_message()[source]
Return type:

bytes

send(data)[source]
static decode_remote_config(remote_config)[source]