API reference¶
The ops library is a Python framework for writing and testing Juju charms.
See more: Charm SDK documentation
The library (available on PyPI) provides:
ops, the API to respond to Juju events and manage the application;
ops.main entry point, used to initialise and run your charm;
ops.pebble, the Pebble client, a low-level API for Kubernetes containers;
the APIs for unit testing charms in a simulated environment:
State-transition testing. This is the recommended approach (it was previously known as ‘Scenario’).
Harness. This is a deprecated framework, and has issues, particularly with resetting the charm state between Juju events.
You can structure your charm however you like, but with the ops library, you get a framework that promotes consistency and readability by following best practices. It also helps you organise your code better by separating different aspects of the charm, such as managing the application’s state, handling integrations with other services, and making the charm easier to test.
ops¶
The API to respond to Juju events and manage the application.
This API provides core features to your charm, including:
CharmBase
, the base class for charms andObject
, the base class for charm libraries.EventBase
class and individual event types, like theActionEvent
class.Framework
class, accessible asself.framework
in a charm, the main interface for the charm to ops library infrastructure, including:Model
class that represents the Juju model, accessible asself.model
in a charm, including:Container
class to control Kubernetes workloads, including:add_layer()
andreplan()
methods to update Pebble configuration.pull()
andpush()
methods to copy data to and from a container, respectively.exec()
method to run arbitrary commands inside the container.
StatusBase
class and individual status types, like theActiveStatus
class.
- class ops.ActionEvent(handle: Handle, id: str | None = None)[source]¶
Bases:
EventBase
Events raised by Juju when an administrator invokes a Juju Action.
This class is the data type of events triggered when an administrator invokes a Juju Action. Callbacks bound to these events may be used for responding to the administrator’s Juju Action request.
To read the parameters for the action, see the instance variable
params
. To respond with the result of the action, callset_results()
. To add progress messages that are visible as the action is progressing uselog()
.- defer() NoReturn [source]¶
Action events are not deferrable like other events.
This is because an action runs synchronously and the administrator is waiting for the result.
- Raises:
RuntimeError – always.
- fail(message: str = '')[source]¶
Report that this action has failed.
- Parameters:
message – Optional message to record why it has failed.
- log(message: str)[source]¶
Send a message that a user will see while the action is running.
- Parameters:
message – The message for the user.
- restore(snapshot: Dict[str, Any])[source]¶
Used by the framework to record the action.
Not meant to be called directly by charm code.
- set_results(results: Dict[str, Any])[source]¶
Report the result of the action.
Juju eventually only accepts a str:str mapping, so we will attempt to flatten any more complex data structure like so:
>>> {'a': 'b'} # becomes: 'a'='b' >>> {'a': {'b': 'c'}} # becomes: 'a.b'='c' >>> {'a': {'b': 'c', 'd': 'e'}} # becomes: 'a.b'='c', 'a.d' = 'e' >>> {'a.b': 'c', 'a.d': 'e'} # equivalent to previous
Note that duplicate keys are not allowed, so this is invalid:
>>> {'a': {'b': 'c'}, 'a.b': 'c'}
Note that the resulting keys must start and end with lowercase alphanumeric, and can only contain lowercase alphanumeric, hyphens and periods.
Because results are passed to Juju using the command line, the maximum size is around 100KB. However, actions results are designed to be small: a few key-value pairs shown in the Juju CLI. If larger content is needed, store it in a file and use something like
juju scp
.If any exceptions occur whilst the action is being handled, juju will gather any stdout/stderr data (and the return code) and inject them into the results object. Thus, the results object might contain the following keys, additionally to those specified by the charm code:
Stdout
Stderr
Stdout-encoding
Stderr-encoding
ReturnCode
- Parameters:
results – The result of the action as a Dict
- Raises:
ModelError – if a reserved key is used.
ValueError – if
results
has a mix of dotted/non-dotted keys that expand out to result in duplicate keys, for example:{'a': {'b': 1}, 'a.b': 2}
. Also raised if a dict is passed with a key that fails to meet the format requirements.OSError – if extremely large (>100KB) results are provided.
- class ops.ActionMeta(name: str, raw: Dict[str, Any] | None = None)[source]¶
Bases:
object
Object containing metadata about an action’s definition.
- class ops.ActiveStatus(message: str = '')[source]¶
Bases:
StatusBase
The unit or application is ready and active.
Set this status when the charm is correctly offering all the services it has been asked to offer. If the unit or application is operational but some feature (like high availability) is in a degraded state, set “active” with an appropriate message.
- class ops.Application(name: str, meta: CharmMeta, backend: _ModelBackend, cache: _ModelCache)[source]¶
Bases:
object
Represents a named application in the model.
This might be this charm’s application, or might be an application this charm is integrated with. Charmers should not instantiate Application objects directly, but should use
Model.app
to get the application this unit is part of, orModel.get_app()
if they need a reference to a given application.- add_secret(content: Dict[str, str], *, label: str | None = None, description: str | None = None, expire: datetime | timedelta | None = None, rotate: SecretRotate | None = None) Secret [source]¶
Create a
Secret
owned by this application.Added in Juju version 3.0.
- Parameters:
content – A key-value mapping containing the payload of the secret, for example
{"password": "foo123"}
.label – Charm-local label (or “name”) to assign to this secret, which can later be used for lookup.
description – Description of the secret’s purpose.
expire – Time in the future (or timedelta from now) at which the secret is due to expire. When that time elapses, Juju will notify the charm by sending a SecretExpired event. None (the default) means the secret will never expire.
rotate – Rotation policy/time. Every time this elapses, Juju will notify the charm by sending a SecretRotate event. None (the default) means to use the Juju default, which is never rotate.
- Raises:
ValueError – if the secret is empty, or the secret key is invalid.
- name: str¶
The name of this application (eg, ‘mysql’). This name may differ from the name of the charm, if the user has deployed it to a different name.
- planned_units() int [source]¶
Get the number of units that Juju has “planned” for this application.
E.g., if an admin runs “juju deploy foo”, then “juju add-unit -n 2 foo”, the planned unit count for foo will be 3.
The data comes from the Juju agent, based on data it fetches from the controller. Pending units are included in the count, and scale down events may modify the count before some units have been fully torn down. The information in planned_units is up-to-date as of the start of the current hook invocation.
This method only returns data for this charm’s application – the Juju agent isn’t able to see planned unit counts for other applications in the model.
- Raises:
RuntimeError – on trying to get the planned units for a remote application.
- property status: StatusBase¶
Used to report or read the status of the overall application.
Changes to status take effect immediately, unlike other Juju operations such as modifying relation data or secrets, which only take effect after a successful event.
Can only be read and set by the lead unit of the application.
The status of remote units is always Unknown.
Alternatively, use the
collect_app_status
event to evaluate and set application status consistently at the end of every hook.- Raises:
RuntimeError – if setting the status of another application, or if setting the status of this application as a unit that is not the leader.
InvalidStatusError – if setting the status to something that is not a
StatusBase
Example:
self.model.app.status = ops.BlockedStatus('I need a human to come help me')
- class ops.Binding(name: str, relation_id: int | None, backend: _ModelBackend)[source]¶
Bases:
object
Binding to a network space.
- class ops.BindingMapping(backend: _ModelBackend)[source]¶
-
Mapping of endpoints to network bindings.
Charm authors should not instantiate this directly, but access it via
Model.get_binding()
- class ops.BlockedStatus(message: str = '')[source]¶
Bases:
StatusBase
The unit or application requires manual intervention.
Set this status when an administrator has to manually intervene to unblock the charm to let it proceed.
- class ops.BoundEvent(emitter: Object, event_type: Type[EventBase], event_kind: str)[source]¶
Bases:
object
Event bound to an Object.
- emit(*args: Any, **kwargs: Any)[source]¶
Emit event to all registered observers.
The current storage state is committed before and after each observer is notified.
Note that the emission of custom events is handled immediately. In other words, custom events are not queued, but rather nested. For example:
1. Main hook handler (emits custom_event_1) 2. Custom event 1 handler (emits custom_event_2) 3. Custom event 2 handler 4. Resume custom event 1 handler 5. Resume main hook handler
- class ops.BoundStoredState(parent: Object, attr_name: str)[source]¶
Bases:
object
Stored state data bound to a specific Object.
- class ops.CharmBase(framework: Framework)[source]¶
Bases:
Object
Base class that represents the charm overall.
CharmBase
is used to create a charm. This is done by inheriting fromCharmBase
and customising the subclass as required. So to create a charm calledMyCharm
, define a charm class and set up the required event handlers (“hooks”) in its constructor:import logging import ops class MyCharm(ops.CharmBase): def __init__(self, *args): super().__init__(*args) self.framework.observe(self.on.config_changed, self._on_config_changed) self.framework.observe(self.on.stop, self._on_stop) # ... if __name__ == "__main__": ops.main(MyCharm)
As shown in the example above, a charm class is instantiated by
ops.main
rather than charm authors directly instantiating a charm.- Parameters:
framework – The framework responsible for managing the Model and events for this charm.
- property app: Application¶
Application that this unit is part of.
- property config: ConfigData¶
A mapping containing the charm’s config and current values.
- on: CharmEvents[source]¶
This property is used to create an event handler using
Framework.observe()
, and can be one of the events listed atCharmEvents
.
- class ops.CharmEvents(parent: Object | None = None, key: str | None = None)[source]¶
Bases:
ObjectEvents
Events generated by Juju pertaining to application lifecycle.
By default, the events listed as attributes of this class will be provided via the
CharmBase.on
attribute. For example:self.framework.observe(self.on.config_changed, self._on_config_changed)
In addition to the events listed as attributes of this class, dynamically-named events will also be defined based on the charm’s metadata (
metadata.yaml
) for relations, storage, actions, and containers. These named events may be accessed asself.on[<name>].<event>
or using a prefix likeself.on.<name>_<event>
, for example:self.framework.observe(self.on["db"].relation_created, self._on_db_relation_created) self.framework.observe(self.on.workload_pebble_ready, self._on_workload_pebble_ready)
- collect_app_status¶
Triggered on the leader at the end of every hook to collect app statuses for evaluation (see
CollectStatusEvent
).
- collect_metrics¶
Triggered by Juju to collect metrics (see
CollectMetricsEvent
).Scheduled for removal in Juju version 4.0.
- collect_unit_status¶
Triggered at the end of every hook to collect unit statuses for evaluation (see
CollectStatusEvent
).
- config_changed¶
Triggered when a configuration change occurs (see
ConfigChangedEvent
).
- install¶
Triggered when a charm is installed (see
InstallEvent
).
- leader_elected¶
Triggered when a new leader has been elected (see
LeaderElectedEvent
).
- leader_settings_changed¶
Triggered when leader changes any settings (see
LeaderSettingsChangedEvent
).Deprecated since version 2.4.0.
- post_series_upgrade¶
Triggered after a series upgrade (see
PostSeriesUpgradeEvent
).Scheduled for removal in Juju version 4.0.
- pre_series_upgrade¶
Triggered to prepare a unit for series upgrade (see
PreSeriesUpgradeEvent
).Scheduled for removal in Juju version 4.0.
- remove¶
Triggered when a unit is about to be terminated (see
RemoveEvent
).
- secret_changed¶
Triggered by Juju on the observer when the secret owner changes its contents (see
SecretChangedEvent
).Added in Juju version 3.0: Charm secrets added in Juju 3.0, user secrets added in Juju 3.3
- secret_expired¶
Triggered by Juju on the owner when a secret’s expiration time elapses (see
SecretExpiredEvent
).Added in Juju version 3.0.
- secret_remove¶
Triggered by Juju on the owner when a secret revision can be removed (see
SecretRemoveEvent
).Added in Juju version 3.0.
- secret_rotate¶
Triggered by Juju on the owner when the secret’s rotation policy elapses (see
SecretRotateEvent
).Added in Juju version 3.0.
- start¶
Triggered immediately after first configuration change (see
StartEvent
).
- update_status¶
Triggered periodically by a status update request from Juju (see
UpdateStatusEvent
).
- upgrade_charm¶
Triggered by request to upgrade the charm (see
UpgradeCharmEvent
).
- class ops.CharmMeta(raw: Dict[str, Any] | None = None, actions_raw: Dict[str, Any] | None = None)[source]¶
Bases:
object
Object containing the metadata for the charm.
This is read from
metadata.yaml
andactions.yaml
. Generally charms will define this information, rather than reading it at runtime. This class is mostly for the framework to understand what the charm has defined.- Parameters:
raw – a mapping containing the contents of metadata.yaml
actions_raw – a mapping containing the contents of actions.yaml
- actions: Dict[str, ActionMeta]¶
Actions the charm has defined.
- assumes: JujuAssumes¶
Juju features this charm requires.
- containers: Dict[str, ContainerMeta]¶
Container metadata for each defined container.
- extra_bindings: Dict[str, None]¶
Additional named bindings that a charm can use for network configuration.
- static from_charm_root(charm_root: Path | str)[source]¶
Initialise CharmMeta from the path to a charm repository root folder.
- classmethod from_yaml(metadata: str | TextIO, actions: str | TextIO | None = None) CharmMeta [source]¶
Instantiate a
CharmMeta
from a YAML description ofmetadata.yaml
.- Parameters:
metadata – A YAML description of charm metadata (name, relations, etc.) This can be a simple string, or a file-like object (passed to
yaml.safe_load
).actions – YAML description of Actions for this charm (e.g., actions.yaml)
- links: MetadataLinks¶
Links to more details about the charm.
- payloads: Dict[str, PayloadMeta]¶
Payload metadata for each defined payload.
- peers: Dict[str, RelationMeta]¶
Peer relations.
- provides: Dict[str, RelationMeta]¶
Relations this charm provides.
- relations: Dict[str, RelationMeta]¶
All
RelationMeta
instances.This is merged from
requires
,provides
, andpeers
. If needed, the role of the relation definition can be obtained from itsrole
attribute.
- requires: Dict[str, RelationMeta]¶
Relations this charm requires.
- resources: Dict[str, ResourceMeta]¶
Resource metadata for each defined resource.
- series: List[str]¶
List of supported OS series that this charm can support.
The first entry in the list is the default series that will be used by deploy if no other series is requested by the user.
- storages: Dict[str, StorageMeta]¶
Storage metadata for each defined storage.
- class ops.CheckInfoMapping(checks: Iterable[CheckInfo])[source]¶
Bases:
Mapping
[str
,CheckInfo
]Map of check names to
ops.pebble.CheckInfo
objects.This is done as a mapping object rather than a plain dictionary so that we can extend it later, and so it’s not mutable.
- class ops.CloudCredential(auth_type: str, attributes: ~typing.Dict[str, str] = <factory>, redacted: ~typing.List[str] = <factory>)[source]¶
Bases:
object
Credentials for cloud.
Used as the type of attribute credential in
CloudSpec
.- attributes: Dict[str, str]¶
A dictionary containing cloud credentials.
For example, for AWS, it contains access-key and secret-key; for Azure, application-id, application-password and subscription-id can be found here.
- class ops.CloudSpec(type: str, name: str, region: str | None = None, endpoint: str | None = None, identity_endpoint: str | None = None, storage_endpoint: str | None = None, credential: ~ops.model.CloudCredential | None = None, ca_certificates: ~typing.List[str] = <factory>, skip_tls_verify: bool = False, is_controller_cloud: bool = False)[source]¶
Bases:
object
Cloud specification information (metadata) including credentials.
- credential: CloudCredential | None = None¶
Cloud credentials with key-value attributes.
- class ops.CollectMetricsEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered by Juju to collect metrics.
Juju fires this event every five minutes for the lifetime of the unit. Callback methods bound to this event may use the
add_metrics()
method of this class to send measurements to Juju.Note that associated callback methods are currently sandboxed in how they can interact with Juju.
Scheduled for removal in Juju version 4.0.
- add_metrics(metrics: Mapping[str, int | float], labels: Mapping[str, str] | None = None)[source]¶
Record metrics that have been gathered by the charm for this unit.
- Parameters:
metrics – Key-value mapping of metrics that have been gathered.
labels – Key-value labels applied to the metrics.
- Raises:
ModelError – if invalid keys or values are provided.
- class ops.CollectStatusEvent(handle: Handle)[source]¶
Bases:
LifecycleEvent
Event triggered at the end of every hook to collect statuses for evaluation.
If the charm wants to provide application or unit status in a consistent way after the end of every hook, it should observe the
collect_app_status
orcollect_unit_status
event, respectively.The framework will trigger these events after the hook code runs successfully (
collect_app_status
will only be triggered on the leader unit). This happens on every Juju event, whether it wasobserved
or not. If any statuses were added by the event handler usingadd_status()
, the framework will choose the highest-priority status and set that as the status (application status forcollect_app_status
, or unit status forcollect_unit_status
).The order of priorities is as follows, from highest to lowest:
blocked
maintenance
waiting
active
It is an error to call
add_status()
with an instance ofErrorStatus
orUnknownStatus
.If there are multiple statuses with the same priority, the first one added wins (and if an event is observed multiple times, the handlers are called in the order they were observed).
A collect-status event can be observed multiple times, and
add_status()
can be called multiple times to add multiple statuses for evaluation. This is useful when a charm has multiple components that each have a status. Each code path in a collect-status handler should calladd_status
at least once.Below is an example “web app” charm component that observes
collect_unit_status
to provide the status of the component, which requires a “port” config option set before it can proceed:class MyCharm(ops.CharmBase): def __init__(self, *args): super().__init__(*args) self.webapp = Webapp(self) # initialize other components class WebApp(ops.Object): def __init__(self, charm: ops.CharmBase): super().__init__(charm, 'webapp') self.framework.observe(charm.on.collect_unit_status, self._on_collect_status) def _on_collect_status(self, event: ops.CollectStatusEvent): if 'port' not in self.model.config: event.add_status(ops.BlockedStatus('please set "port" config')) return event.add_status(ops.ActiveStatus())
- add_status(status: StatusBase)[source]¶
Add a status for evaluation.
See
CollectStatusEvent
for a description of how to use this.
- class ops.CommitEvent(handle: Handle)[source]¶
Bases:
LifecycleEvent
Event that will be emitted second on commit.
- class ops.ConfigChangedEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered when a configuration change occurs.
This event will fire in several situations:
When the admin reconfigures the charm using the Juju CLI, for example
juju config mycharm foo=bar
. This event notifies the charm of its new configuration. (The event itself, however, is not aware of what specifically has changed in the config).Right after the unit starts up for the first time. This event notifies the charm of its initial configuration. Typically, this event will fire between an
InstallEvent
and a :class:~`ops.StartEvent` during the startup sequence (when a unit is first deployed), but in general it will fire whenever the unit is (re)started, for example after pod churn on Kubernetes, on unit rescheduling, on unit upgrade or refresh, and so on.As a specific instance of the above point: when networking changes (if the machine reboots and comes up with a different IP).
When the app config changes, for example when juju trust is run.
Any callback method bound to this event cannot assume that the software has already been started; it should not start stopped software, but should (if appropriate) restart running software to take configuration changes into account.
- class ops.ConfigData(backend: _ModelBackend)[source]¶
Bases:
_GenericLazyMapping
[bool
|int
|float
|str
]Configuration data.
This class should not be instantiated directly. It should be accessed via
Model.config
.
- class ops.Container(name: str, backend: _ModelBackend, pebble_client: Client | None = None)[source]¶
Bases:
object
Represents a named container in a unit.
This class should not be instantiated directly, instead use
Unit.get_container()
orUnit.containers
.For methods that make changes to the container, if the change fails or times out, then a
ops.pebble.ChangeError
orops.pebble.TimeoutError
will be raised.Interactions with the container use Pebble, so all methods may raise exceptions when there are problems communicating with Pebble. Problems connecting to or transferring data with Pebble will raise a
ops.pebble.ConnectionError
- you can guard against these by first checkingcan_connect()
, but that generally introduces a race condition where problems occur aftercan_connect()
has succeeded. When an error occurs executing the request, such as trying to add an invalid layer or execute a command that does not exist, anops.pebble.APIError
is raised.- add_layer(label: str, layer: str | LayerDict | Layer, *, combine: bool = False)[source]¶
Dynamically add a new layer onto the Pebble configuration layers.
- Parameters:
label – Label for new layer (and label of layer to merge with if combining).
layer – A YAML string, configuration layer dict, or pebble.Layer object containing the Pebble layer to add.
combine – If combine is False (the default), append the new layer as the top layer with the given label (must be unique). If combine is True and the label already exists, the two layers are combined into a single one considering the layer override rules; if the layer doesn’t exist, it is added as usual.
- can_connect() bool [source]¶
Report whether the Pebble API is reachable in the container.
This method returns a bool that indicates whether the Pebble API is available at the time the method is called. It does not guard against the Pebble API becoming unavailable, and should be treated as a “point in time” status only.
For example:
# Add status based on any earlier errors communicating with Pebble. ... # Check that Pebble is still reachable now. container = self.unit.get_container("example") if not container.can_connect(): event.add_status(ops.MaintenanceStatus("Waiting for Pebble..."))
- exec(command: List[str], *, service_context: str | None = None, environment: Dict[str, str] | None = None, working_dir: str | None = None, timeout: float | None = None, user_id: int | None = None, user: str | None = None, group_id: int | None = None, group: str | None = None, stdin: str | TextIO | None = None, stdout: TextIO | None = None, stderr: TextIO | None = None, encoding: str = 'utf-8', combine_stderr: bool = False) ExecProcess[str] [source]¶
- exec(command: List[str], *, service_context: str | None = None, environment: Dict[str, str] | None = None, working_dir: str | None = None, timeout: float | None = None, user_id: int | None = None, user: str | None = None, group_id: int | None = None, group: str | None = None, stdin: bytes | BinaryIO | None = None, stdout: BinaryIO | None = None, stderr: BinaryIO | None = None, encoding: None = None, combine_stderr: bool = False) ExecProcess[bytes]
Execute the given command on the remote system.
See
ops.pebble.Client.exec()
for documentation of the parameters and return value, as well as examples.Note that older versions of Juju do not support the
service_context
parameter, so if the Charm is to be used on those versions, thenJujuVersion.supports_exec_service_context()
should be used as a guard.- Raises:
ExecError – if the command exits with a non-zero exit code.
- exists(path: str | PurePath) bool [source]¶
Report whether a path exists on the container filesystem.
- get_check(check_name: str) CheckInfo [source]¶
Get check information for a single named check.
- Raises:
ModelError – if a check with the given name is not found
- get_checks(*check_names: str, level: CheckLevel | None = None) CheckInfoMapping [source]¶
Fetch and return a mapping of check information indexed by check name.
- Parameters:
check_names – Optional check names to query for. If no check names are specified, return checks with any name.
level – Optional check level to query for. If not specified, fetch all checks.
- get_notice(id: str) Notice [source]¶
Get details about a single notice by ID.
Added in Juju version 3.4.
- Raises:
ModelError – if a notice with the given ID is not found
- get_notices(*, users: NoticesUsers | None = None, user_id: int | None = None, types: Iterable[NoticeType | str] | None = None, keys: Iterable[str] | None = None) List[Notice] [source]¶
Query for notices that match all of the provided filters.
See
ops.pebble.Client.get_notices()
for documentation of the parameters.Added in Juju version 3.4.
- get_plan() Plan [source]¶
Get the combined Pebble configuration.
This will immediately reflect changes from any previous
add_layer()
calls, regardless of whetherreplan()
orrestart()
have been called.
- get_service(service_name: str) ServiceInfo [source]¶
Get status information for a single named service.
- Raises:
ModelError – if a service with the given name is not found
- get_services(*service_names: str) Mapping[str, ServiceInfo] [source]¶
Fetch and return a mapping of status information indexed by service name.
If no service names are specified, return status information for all services, otherwise return information for only the given services.
- isdir(path: str | PurePath) bool [source]¶
Report whether a directory exists at the given path on the container filesystem.
- list_files(path: str | PurePath, *, pattern: str | None = None, itself: bool = False) List[FileInfo] [source]¶
Return list of directory entries from given path on remote system.
Despite the name, this method returns a list of files and directories, similar to
os.listdir()
oros.scandir()
.- Parameters:
path – Path of the directory to list, or path of the file to return information about.
pattern – If specified, filter the list to just the files that match, for example
*.txt
.itself – If path refers to a directory, return information about the directory itself, rather than its contents.
- make_dir(path: str | PurePath, *, make_parents: bool = False, permissions: int | None = None, user_id: int | None = None, user: str | None = None, group_id: int | None = None, group: str | None = None)[source]¶
Create a directory on the remote system with the given attributes.
- Parameters:
path – Path of the directory to create on the remote system.
make_parents – If True, create parent directories if they don’t exist.
permissions – Permissions (mode) to create directory with (Pebble default is 0o755).
user_id – User ID (UID) for directory.
user – Username for directory. User’s UID must match user_id if both are specified.
group_id – Group ID (GID) for directory.
group – Group name for directory. Group’s GID must match group_id if both are specified.
- property pebble: Client¶
The low-level
ops.pebble.Client
instance for this container.
- pull(path: str | PurePath, *, encoding: None) BinaryIO [source]¶
- pull(path: str | PurePath, *, encoding: str = 'utf-8') TextIO
Read a file’s content from the remote system.
- Parameters:
path – Path of the file to read from the remote system.
encoding – Encoding to use for decoding the file’s bytes to string, or
None
to specify no decoding.
- Returns:
A readable file-like object, whose
read()
method will return strings decoded according to the specified encoding, or bytes if encoding isNone
.- Raises:
pebble.PathError – If there was an error reading the file at path, for example, if the file doesn’t exist or is a directory.
- pull_path(source_path: str | PurePath | Iterable[str | PurePath], dest_dir: str | Path)[source]¶
Recursively pull a remote path or files to the local system.
Only regular files and directories are copied; symbolic links, device files, etc. are skipped. Pulling is attempted to completion even if errors occur during the process. All errors are collected incrementally. After copying has completed, if any errors occurred, a single
MultiPushPullError
is raised containing details for each error.Assuming the following files exist remotely:
/foo/bar/baz.txt
/foo/foobar.txt
/quux.txt
These are various pull examples:
# copy one file container.pull_path('/foo/foobar.txt', '/dst') # Destination results: /dst/foobar.txt # copy a directory container.pull_path('/foo', '/dst') # Destination results: /dst/foo/bar/baz.txt, /dst/foo/foobar.txt # copy a directory's contents container.pull_path('/foo/*', '/dst') # Destination results: /dst/bar/baz.txt, /dst/foobar.txt # copy multiple files container.pull_path(['/foo/bar/baz.txt', 'quux.txt'], '/dst') # Destination results: /dst/baz.txt, /dst/quux.txt # copy a file and a directory container.pull_path(['/foo/bar', '/quux.txt'], '/dst') # Destination results: /dst/bar/baz.txt, /dst/quux.txt
- Parameters:
source_path – A single path or list of paths to pull from the remote system. The paths can be either a file or a directory but must be absolute paths. If
source_path
is a directory, the directory base name is attached to the destination directory – that is, the source path directory is placed inside the destination directory. If a source path ends with a trailing/*
it will have its contents placed inside the destination directory.dest_dir – Local destination directory inside which the source dir/files will be placed.
- push(path: str | PurePath, source: bytes | str | BinaryIO | TextIO, *, encoding: str = 'utf-8', make_dirs: bool = False, permissions: int | None = None, user_id: int | None = None, user: str | None = None, group_id: int | None = None, group: str | None = None)[source]¶
Write content to a given file path on the remote system.
Note that if another process has the file open on the remote system, or if the remote file is a bind mount, pushing will fail with a
pebble.PathError
. UseContainer.exec()
for full control.- Parameters:
path – Path of the file to write to on the remote system.
source – Source of data to write. This is either a concrete str or bytes instance, or a readable file-like object.
encoding – Encoding to use for encoding source str to bytes, or strings read from source if it is a TextIO type. Ignored if source is bytes or BinaryIO.
make_dirs – If True, create parent directories if they don’t exist.
permissions – Permissions (mode) to create file with (Pebble default is 0o644).
user_id – User ID (UID) for file.
user – Username for file. User’s UID must match user_id if both are specified.
group_id – Group ID (GID) for file.
group – Group name for file. Group’s GID must match group_id if both are specified.
- push_path(source_path: str | Path | Iterable[str | Path], dest_dir: str | PurePath)[source]¶
Recursively push a local path or files to the remote system.
Only regular files and directories are copied; symbolic links, device files, etc. are skipped. Pushing is attempted to completion even if errors occur during the process. All errors are collected incrementally. After copying has completed, if any errors occurred, a single
MultiPushPullError
is raised containing details for each error.Assuming the following files exist locally:
/foo/bar/baz.txt
/foo/foobar.txt
/quux.txt
These are various push examples:
# copy one file container.push_path('/foo/foobar.txt', '/dst') # Destination results: /dst/foobar.txt # copy a directory container.push_path('/foo', '/dst') # Destination results: /dst/foo/bar/baz.txt, /dst/foo/foobar.txt # copy a directory's contents container.push_path('/foo/*', '/dst') # Destination results: /dst/bar/baz.txt, /dst/foobar.txt # copy multiple files container.push_path(['/foo/bar/baz.txt', 'quux.txt'], '/dst') # Destination results: /dst/baz.txt, /dst/quux.txt # copy a file and a directory container.push_path(['/foo/bar', '/quux.txt'], '/dst') # Destination results: /dst/bar/baz.txt, /dst/quux.txt
- Parameters:
source_path – A single path or list of paths to push to the remote system. The paths can be either a file or a directory. If
source_path
is a directory, the directory base name is attached to the destination directory – that is, the source path directory is placed inside the destination directory. If a source path ends with a trailing/*
it will have its contents placed inside the destination directory.dest_dir – Remote destination directory inside which the source dir/files will be placed. This must be an absolute path.
- remove_path(path: str | PurePath, *, recursive: bool = False)[source]¶
Remove a file or directory on the remote system.
- Parameters:
path – Path of the file or directory to delete from the remote system.
recursive – If True, and path is a directory, recursively delete it and everything under it. If path is a file, delete the file. In either case, do nothing if the file or directory does not exist. Behaviourally similar to
rm -rf <file|dir>
.
- Raises:
pebble.PathError – If a relative path is provided, or if recursive is False and the file or directory cannot be removed (it does not exist or is not empty).
- replan() None [source]¶
Replan all services: restart changed services and start startup-enabled services.
- restart(*service_names: str)[source]¶
Restart the given service(s) by name.
Listed running services will be stopped and restarted, and listed stopped services will be started.
- send_signal(sig: int | str, *service_names: str)[source]¶
Send the given signal to one or more services.
- Parameters:
sig – Name or number of signal to send, for example
"SIGHUP"
,1
, orsignal.SIGHUP
.service_names – Name(s) of the service(s) to send the signal to.
- Raises:
pebble.APIError – If any of the services are not in the plan or are not currently running.
- class ops.ContainerBase(os_name: str, channel: str, architectures: List[str])[source]¶
Bases:
object
Metadata to resolve a container image.
- channel: str¶
Channel of the OS in format
track[/risk][/branch]
as used by Snaps.For example:
20.04/stable
or18.04/stable/fips
- classmethod from_dict(d: _ContainerBaseDict) ContainerBase [source]¶
Create new ContainerBase object from dict parsed from YAML.
- class ops.ContainerMapping(names: Iterable[str], backend: _ModelBackend)[source]¶
Bases:
Mapping
[str
,Container
]Map of container names to Container objects.
This is done as a mapping object rather than a plain dictionary so that we can extend it later, and so it’s not mutable.
- class ops.ContainerMeta(name: str, raw: Dict[str, Any])[source]¶
Bases:
object
Metadata about an individual container.
- bases: List[ContainerBase] | None¶
List of bases for use in resolving a container image.
Sorted by descending order of preference, and must not be present if resource is specified.
- property mounts: Dict[str, ContainerStorageMeta]¶
An accessor for the mounts in a container.
Dict keys match key name in
StorageMeta
Example:
storage: foo: type: filesystem location: /test containers: bar: mounts: - storage: foo - location: /test/mount
- class ops.ContainerStorageMeta(storage: str, location: str)[source]¶
Bases:
object
Metadata about storage for an individual container.
If multiple locations are specified for the same storage, such as Kubernetes subPath mounts,
location
will not be an accessible attribute, as it would not be possible to determine which mount point was desired, andlocations
should be iterated over.- property location: str¶
The location the storage is mounted at.
- Raises:
RuntimeError – if there is more than one mount point with the same backing storage - use
locations
instead.
- storage: str¶
Name for the mount point, which should exist in the keys of the charm’s
StorageMeta
.
- class ops.ErrorStatus(message: str = '')[source]¶
Bases:
StatusBase
The unit status is error.
The unit-agent has encountered an error (the application or unit requires human intervention in order to operate correctly).
This status is read-only; trying to set unit or application status to
ErrorStatus
will raiseInvalidStatusError
.
- class ops.EventBase(handle: Handle)[source]¶
Bases:
object
The base class for all events.
Inherit this and override the
snapshot
andrestore
methods to create a custom event.- defer() None [source]¶
Defer the event to the future.
Deferring an event from a handler puts that handler into a queue, to be called again the next time the charm is invoked. This invocation may be the result of an action, or any event other than metric events. The queue of events will be dispatched before the new event is processed.
Important points that follow from the above:
defer()
does not interrupt the execution of the current event handler. In almost all cases, a call todefer()
should be followed by an explicitreturn
from the handler;the re-execution of the deferred event handler starts from the top of the handler method (not where defer was called);
only the handlers that actually called
defer()
are called again (that is: despite talking about “deferring an event” it is actually the handler/event combination that is deferred); andany deferred events get processed before the event (or action) that caused the current invocation of the charm.
The general desire to call
defer()
happens when some precondition isn’t yet met. However, care should be exercised as to whether it is better to defer this event so that it is seen again, or whether it is better to just wait for the event that indicates the precondition has been met.For example, if handling a config change requires that two config values are changed, there’s no reason to defer the first
config-changed
because there will be a secondconfig-changed
event fired when the other config value changes.Similarly, if two events need to occur before execution can proceed (say event A and B), the event A handler could
defer()
because B has not been seen yet. However, that leads to:event A fires, calls defer()
event B fires, event A handler is called first, still hasn’t seen B happen, so is deferred again. Then B happens, which progresses since it has seen A.
At some future time, event C happens, which also checks if A can proceed.
- class ops.EventSource(event_type: Type[EventBase])[source]¶
Bases:
object
EventSource wraps an event type with a descriptor to facilitate observing and emitting.
It is generally used as:
class SomethingHappened(ops.EventBase): pass class SomeObject(Object): something_happened = ops.EventSource(SomethingHappened)
With that, instances of that type will offer the
someobj.something_happened
attribute which is aBoundEvent
, and may be used to emit and observe the event.
- class ops.Framework(storage: SQLiteStorage | JujuStorage, charm_dir: str | Path, meta: CharmMeta, model: Model, event_name: str | None = None, juju_debug_at: Set[str] | None = None)[source]¶
Bases:
Object
Main interface from the Charm to the ops library’s infrastructure.
- breakpoint(name: str | None = None)[source]¶
Add breakpoint, optionally named, at the place where this method is called.
For the breakpoint to be activated the JUJU_DEBUG_AT environment variable must be set to “all” or to the specific name parameter provided, if any. In every other situation calling this method does nothing.
The framework also provides a standard breakpoint named “hook”, that will stop execution when a hook event is about to be handled.
For those reasons, the “all” and “hook” breakpoint names are reserved.
- Raises:
ValueError – if the breakpoint name is invalid.
- load_snapshot(handle: Handle) Serializable [source]¶
Load a persistent snapshot.
- observe(bound_event: BoundEvent, observer: Callable[[Any], None])[source]¶
Register observer to be called when bound_event is emitted.
If this is called multiple times for the same event type, the framework calls the observers in the order they were observed.
The bound_event is generally provided as an attribute of the object that emits the event, and is created in this style:
class SomeObject: something_happened = Event(SomethingHappened)
That event may be observed as:
framework.observe(someobj.something_happened, self._on_something_happened)
- Raises:
RuntimeError – if bound_event or observer are the wrong type.
- reemit() None [source]¶
Reemit previously deferred events to the observers that deferred them.
Only the specific observers that have previously deferred the event will be notified again. Observers that asked to be notified about events after it’s been first emitted won’t be notified, as that would mean potentially observing events out of order.
- register_type(cls: Type[Serializable], parent: Handle | Object | None, kind: str | None = None)[source]¶
Register a type to a handle.
- remove_unreferenced_events() None [source]¶
Remove events from storage that are not referenced.
In older versions of the framework, events that had no observers would get recorded but never deleted. This makes a best effort to find these events and remove them from the database.
- save_snapshot(value: StoredStateData | EventBase)[source]¶
Save a persistent snapshot of the provided value.
- set_breakpointhook() Any | None [source]¶
Hook into
sys.breakpointhook
so the builtinbreakpoint()
works as expected.This method is called by
main
, and is not intended to be called by users of the framework itself outside of perhaps some testing scenarios.The
breakpoint()
function is a Python >= 3.7 feature.- Returns:
The old value of
sys.breakpointhook
.
- class ops.FrameworkEvents(parent: Object | None = None, key: str | None = None)[source]¶
Bases:
ObjectEvents
Manager of all framework events.
- commit¶
Triggered before event data is committed to storage.
- class ops.Handle(parent: Handle | Object | None, kind: str, key: str | None)[source]¶
Bases:
object
Handle defines a name for an object in the form of a hierarchical path.
The provided parent is the object (or that object’s handle) that this handle sits under, or None if the object identified by this handle stands by itself as the root of its own hierarchy.
The handle kind is a string that defines a namespace so objects with the same parent and kind will have unique keys.
The handle key is a string uniquely identifying the object. No other objects under the same parent and kind may have the same key.
- class ops.HandleKind[source]¶
Bases:
object
Helper descriptor to define the Object.handle_kind field.
The handle_kind for an object defaults to its type name, but it may be explicitly overridden if desired.
- class ops.HookEvent(handle: Handle)[source]¶
Bases:
EventBase
Events raised by Juju to progress a charm’s lifecycle.
Hooks are callback methods of a charm class (a subclass of
CharmBase
) that are invoked in response to events raised by Juju. These callback methods are the means by which a charm governs the lifecycle of its application.The
HookEvent
class is the base of a type hierarchy of events related to the charm’s lifecycle.HookEvent
subtypes are grouped into the following categoriesCore lifecycle events
Relation events
Storage events
Metric events
- class ops.InstallEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered when a charm is installed.
This event is triggered at the beginning of a charm’s lifecycle. Any associated callback method should be used to perform one-time setup operations, such as installing prerequisite software.
- exception ops.InvalidStatusError[source]¶
Bases:
ModelError
Raised if trying to set an Application or Unit status to something invalid.
- class ops.JujuAssumes(features: List[str | JujuAssumes], condition: JujuAssumesCondition = JujuAssumesCondition.ALL)[source]¶
Bases:
object
Juju model features that are required by the charm.
See the Juju docs for a list of available features.
- condition: JujuAssumesCondition = 'all-of'¶
- features: List[str | JujuAssumes]¶
- classmethod from_list(raw: List[Any], condition: JujuAssumesCondition = JujuAssumesCondition.ALL) JujuAssumes [source]¶
Create new JujuAssumes object from list parsed from YAML.
- class ops.JujuAssumesCondition(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
Enum
Distinguishes between
JujuAssumes
that must match all or any features.- ALL = 'all-of'¶
All features are required to satisfy the requirement.
- ANY = 'any-of'¶
Any of the features satisfies the requirement.
- class ops.JujuVersion(version: str)[source]¶
Bases:
object
Helper to work with the Juju version.
It knows how to parse the
JUJU_VERSION
environment variable, and exposes different capabilities according to the specific version. It also allows users to compareJujuVersion
instances with<
and>
operators.- classmethod from_environ() JujuVersion [source]¶
Build a version from the
JUJU_VERSION
environment variable.
- has_controller_storage() bool [source]¶
Report whether this Juju version supports controller-side storage.
- property supports_exec_service_context: bool¶
Report whether this Juju version supports exec’s service_context option.
- class ops.LazyCheckInfo(container: Container, name: str)[source]¶
Bases:
object
Provide lazily-loaded access to a Pebble check’s info.
The attributes provided by this class are the same as those of
ops.pebble.CheckInfo
, however, the notice details are only fetched from Pebble if necessary (and cached on the instance).- level: CheckLevel | str | None¶
- status: CheckStatus | str¶
- class ops.LazyMapping[source]¶
Bases:
_GenericLazyMapping
[str
]Represents a dict[str, str] that isn’t populated until it is accessed.
Charm authors should generally never need to use this directly, but it forms the basis for many of the dicts that the framework tracks.
- class ops.LazyNotice(container: Container, id: str, type: str, key: str)[source]¶
Bases:
object
Provide lazily-loaded access to a Pebble notice’s details.
The attributes provided by this class are the same as those of
ops.pebble.Notice
, however, the notice details are only fetched from Pebble if necessary (and cached on the instance).- type: NoticeType | str¶
- class ops.LeaderElectedEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered when a new leader has been elected.
Juju will trigger this event when a new leader unit is chosen for a given application.
This event fires at least once after Juju selects a leader unit. Callback methods bound to this event may take any action required for the elected unit to assert leadership. Note that only the elected leader unit will receive this event.
- class ops.LeaderSettingsChangedEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered when leader changes any settings.
Deprecated since version 2.4.0: This event has been deprecated in favor of using a Peer relation, and having the leader set a value in the Application data bag for that peer relation. (See
RelationChangedEvent
.)
- class ops.LifecycleEvent(handle: Handle)[source]¶
Bases:
EventBase
Events tied to the lifecycle of the Framework object.
Note: Here, “lifecycle” was poorly named and has nothing to do with the Juju [application] lifecycle.
- defer() NoReturn [source]¶
Lifecycle events are not deferrable like other events.
This is because these events are run alongside each event invocation, so deferring would always end up simply doubling the work.
- Raises:
RuntimeError – always.
- class ops.MaintenanceStatus(message: str = '')[source]¶
Bases:
StatusBase
The unit or application is performing maintenance tasks.
Set this status when the charm is performing an operation such as
apt install
, or is waiting for something under its control, such aspebble-ready
or an exec operation in the workload container. In contrast toWaitingStatus
, “maintenance” reflects activity on this unit or charm, not on peers or related units.
- class ops.MetadataLinks(websites: List[str], sources: List[str], issues: List[str], documentation: str | None)[source]¶
Bases:
object
Links to additional information about a charm.
- class ops.Model(meta: CharmMeta, backend: _ModelBackend, broken_relation_id: int | None = None)[source]¶
Bases:
object
Represents the Juju Model as seen from this unit.
This should not be instantiated directly by Charmers, but can be accessed as
self.model
from any class that derives fromObject
.- property app: Application¶
The application this unit is a part of.
Use
get_app()
to get an arbitrary application by name.
- property config: ConfigData¶
Return a mapping of config for the current application.
- get_app(app_name: str) Application [source]¶
Get an application by name.
Use
app
to get this charm’s application.Internally this uses a cache, so asking for the same application two times will return the same object.
- get_binding(binding_key: str | Relation) Binding | None [source]¶
Get a network space binding.
- Parameters:
binding_key – The relation name or instance to obtain bindings for.
- Returns:
If
binding_key
is a relation name, the method returns the default binding for that relation. If a relation instance is provided, the method first looks up a more specific binding for that specific relation ID, and if none is found falls back to the default binding for the relation name.
- get_cloud_spec() CloudSpec [source]¶
Get details of the cloud in which the model is deployed.
Note: This information is only available for machine charms, not Kubernetes sidecar charms.
- Returns:
a specification for the cloud in which the model is deployed, including credential information.
- Raises:
ModelError – if called in a Kubernetes model.
- get_relation(relation_name: str, relation_id: int | None = None) Relation | None [source]¶
Get a specific Relation instance.
If relation_id is not given, this will return the Relation instance if the relation is established only once or None if it is not established. If this same relation is established multiple times the error TooManyRelatedAppsError is raised.
- Parameters:
relation_name – The name of the endpoint for this charm
relation_id – An identifier for a specific relation. Used to disambiguate when a given application has more than one relation on a given endpoint.
- Raises:
TooManyRelatedAppsError – is raised if there is more than one integration with the supplied relation_name and no relation_id was supplied
- get_secret(*, id: str | None = None, label: str | None = None) Secret [source]¶
Get the
Secret
with the given ID or label.The caller must provide at least one of id (the secret’s locator ID) or label (the charm-local “name”).
If both are provided, the secret will be fetched by ID, and the secret’s label will be updated to the label provided. Normally secret owners set a label using
add_secret
, whereas secret observers set a label usingget_secret
(see an example atSecret.label
).The content of the secret is retrieved, so calls to
Secret.get_content()
do not require querying the secret storage again, unlessrefresh=True
is used, orSecret.set_content()
has been called.Added in Juju version 3.0: Charm secrets added in Juju 3.0, user secrets added in Juju 3.3
- Parameters:
id – Secret ID if fetching by ID.
label – Secret label if fetching by label (or updating it).
- Raises:
SecretNotFoundError – If a secret with this ID or label doesn’t exist.
ModelError – if the charm does not have permission to access the secret.
- get_unit(unit_name: str) Unit [source]¶
Get an arbitrary unit by name.
Use
unit
to get the current unit.Internally this uses a cache, so asking for the same unit two times will return the same object.
- property name: str¶
Return the name of the Model that this unit is running in.
This is read from the environment variable
JUJU_MODEL_NAME
.
- property pod: Pod¶
Represents the definition of a pod spec in legacy Kubernetes models.
Use
Pod.set_spec()
to set the container specification for legacy Kubernetes charms.Deprecated since version 2.4.0: New charms should use the sidecar pattern with Pebble.
- property relations: RelationMapping¶
Mapping of endpoint to list of
Relation
.Answers the question “what am I currently integrated with”. See also
get_relation()
.In a
relation-broken
event, the broken relation is excluded from this list.
- property resources: Resources¶
Access to resources for this charm.
Use
model.resources.fetch(resource_name)
to get the path on disk where the resource can be found.
- property storages: StorageMapping¶
Mapping of storage_name to
Storage
as defined in metadata.yaml.
- property unit: Unit¶
The unit that is running this code.
Use
get_unit()
to get an arbitrary unit by name.
- exception ops.ModelError[source]¶
Bases:
Exception
Base class for exceptions raised when interacting with the Model.
- exception ops.MultiPushPullError(message: str, errors: List[Tuple[str, Exception]])[source]¶
Bases:
Exception
Aggregates multiple push and pull exceptions into one.
This class should not be instantiated directly. It is raised by
Container.push_path()
andContainer.pull_path()
.
- class ops.Network(network_info: _NetworkDict)[source]¶
Bases:
object
Network space details.
Charm authors should not instantiate this directly, but should get access to the Network definition from
Model.get_binding()
and itsnetwork
attribute.- property bind_address: IPv4Address | IPv6Address | str | None¶
A single address that the charm’s application should bind() to.
For the common case where there is a single answer. This represents a single address from
interfaces
that can be used to configure where the charm’s application should bind() and listen().
- egress_subnets: List[IPv4Network | IPv6Network]¶
A list of networks representing the subnets that other units will see the charm connecting from. Due to things like NAT it isn’t always possible to narrow it down to a single address, but when it is clear, the CIDRs will be constrained to a single address (for example, 10.0.0.1/32).
- property ingress_address: IPv4Address | IPv6Address | str | None¶
The address other applications should use to connect to the current unit.
Due to things like public/private addresses, NAT and tunneling, the address the charm will bind() to is not always the address other people can use to connect() to the charm. This is just the first address from
ingress_addresses
.
- ingress_addresses: List[IPv4Address | IPv6Address | str]¶
A list of IP addresses that other units should use to get in touch with the charm.
- interfaces: List[NetworkInterface]¶
A list of network interface details. This includes the information about how the application should be configured (for example, what IP addresses should be bound to).
Multiple addresses for a single interface are represented as multiple interfaces, for example:
[NetworkInfo('ens1', '10.1.1.1/32'), NetworkInfo('ens1', '10.1.2.1/32'])
- class ops.NetworkInterface(name: str, address_info: _AddressDict)[source]¶
Bases:
object
Represents a single network interface that the charm needs to know about.
Charmers should not instantiate this type directly. Instead use
Model.get_binding()
to get the network information for a given endpoint.- address: IPv4Address | IPv6Address | str | None¶
The address of the network interface.
- subnet: IPv4Network | IPv6Network | None¶
The subnet of the network interface. This may be a single address (for example, ‘10.0.1.2/32’).
- exception ops.NoTypeError(handle_path: str)[source]¶
Bases:
Exception
No class to hold it was found when restoring an event.
- class ops.Object(parent: Framework | Object, key: str | None)[source]¶
Bases:
object
Initialize an Object as a new leaf in
Framework
, identified by key.- Parameters:
parent – parent node in the tree.
key – unique identifier for this object.
Every object belongs to exactly one framework.
Every object has a parent, which might be a framework.
We track a “path to object,” which is the path to the parent, plus the object’s unique identifier. Event handlers use this identity to track the destination of their events, and the Framework uses this id to track persisted state between event executions.
The Framework should raise an error if it ever detects that two objects with the same id have been created.
- class ops.ObjectEvents(parent: Object | None = None, key: str | None = None)[source]¶
Bases:
Object
Convenience type to allow defining
.on
attributes at class level.- classmethod define_event(event_kind: str, event_type: Type[EventBase])[source]¶
Define an event on this type at runtime.
Note that attempting to define the same event kind more than once will raise an “overlaps with existing type” runtime error. Ops uses a labeling system to track and reconstruct events between hook executions (each time a hook runs, the Juju Agent invokes a fresh instance of ops; there is no ops process that persists on the host between hooks). Having duplicate Python objects creates duplicate labels. Overwriting a previously created label means that only the latter code path will be run when the current event, if it does get deferred, is re-emitted. This is usually not what is desired, and is error-prone and ambiguous.
- Parameters:
event_kind – An attribute name that will be used to access the event. Must be a valid Python identifier, not be a keyword or an existing attribute.
event_type – A type of the event to define.
- Raises:
RuntimeError – if the same event is defined twice, or if
event_kind
is an invalid name.
- events() Dict[str, BoundEvent] [source]¶
Return a mapping of event_kinds to bound_events for all available events.
- class ops.PayloadMeta(name: str, raw: Dict[str, Any])[source]¶
Bases:
object
Object containing metadata about a payload definition.
- class ops.PebbleCheckEvent(handle: Handle, workload: Container, check_name: str)[source]¶
Bases:
WorkloadEvent
Base class for Pebble check events.
- info: LazyCheckInfo¶
Provide access to the check’s current state.
- class ops.PebbleCheckFailedEvent(handle: Handle, workload: Container, check_name: str)[source]¶
Bases:
PebbleCheckEvent
Event triggered when a Pebble check exceeds the configured failure threshold.
Note that the check may have started passing by the time this event is emitted (which will mean that a
PebbleCheckRecoveredEvent
will be emitted next). If the handler is executing code that should only be done if the check is currently failing, check the current status withevent.info.status == ops.pebble.CheckStatus.DOWN
.Added in Juju version 3.6.
- class ops.PebbleCheckRecoveredEvent(handle: Handle, workload: Container, check_name: str)[source]¶
Bases:
PebbleCheckEvent
Event triggered when a Pebble check recovers.
This event is only triggered when the check has previously reached a failure state (not simply failed, but failed at least as many times as the configured threshold).
Added in Juju version 3.6.
- class ops.PebbleCustomNoticeEvent(handle: Handle, workload: Container, notice_id: str, notice_type: str, notice_key: str)[source]¶
Bases:
PebbleNoticeEvent
Event triggered when a Pebble notice of type “custom” is created or repeats.
Added in Juju version 3.4.
- class ops.PebbleNoticeEvent(handle: Handle, workload: Container, notice_id: str, notice_type: str, notice_key: str)[source]¶
Bases:
WorkloadEvent
Base class for Pebble notice events (each notice type is a subclass).
- notice: LazyNotice¶
Provide access to the event notice’s details.
- class ops.PebbleReadyEvent(handle: Handle, workload: Container)[source]¶
Bases:
WorkloadEvent
Event triggered when Pebble is ready for a workload.
This event is triggered when the Pebble process for a workload/container starts up, allowing the charm to configure how services should be launched.
Callback methods bound to this event allow the charm to run code after a workload has started its Pebble instance and is ready to receive instructions regarding what services should be started. The name prefix of the hook will depend on the container key defined in the
metadata.yaml
file.
- class ops.Pod(backend: _ModelBackend)[source]¶
Bases:
object
Represents the definition of a pod spec in legacy Kubernetes models.
Currently only supports simple access to setting the Juju pod spec via
set_spec
.Deprecated since version 2.4.0: New charms should use the sidecar pattern with Pebble.
- set_spec(spec: Mapping[str, Any], k8s_resources: Mapping[str, Any] | None = None)[source]¶
Set the specification for pods that Juju should start in kubernetes.
See
juju help-tool pod-spec-set
for details of what should be passed.- Parameters:
spec – The mapping defining the pod specification
k8s_resources – Additional kubernetes specific specification.
- class ops.Port(protocol: Literal['tcp', 'udp', 'icmp'], port: int | None)[source]¶
Bases:
object
Represents a port opened by
Unit.open_port()
orUnit.set_ports()
.
- class ops.PostSeriesUpgradeEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered after a series upgrade.
This event is triggered after the administrator has done a distribution upgrade (or rolled back and kept the same series). It is called in response to
juju upgrade-machine <machine> complete
. Associated charm callback methods are expected to do whatever steps are necessary to reconfigure their applications for the new series. This may include things like populating the upgraded version of a database. Note however charms are expected to check if the series has actually changed or whether it was rolled back to the original series.Scheduled for removal in Juju version 4.0.
- class ops.PreCommitEvent(handle: Handle)[source]¶
Bases:
LifecycleEvent
Event that will be emitted first on commit.
- class ops.PreSeriesUpgradeEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered to prepare a unit for series upgrade.
This event triggers when an administrator executes
juju upgrade-machine <machine> prepare
. The event will fire for each unit that is running on the specified machine. Any callback method bound to this event must prepare the charm for an upgrade to the series. This may include things like exporting database content to a version neutral format, or evacuating running instances to other machines.It can be assumed that only after all units on a machine have executed the callback method associated with this event, the administrator will initiate steps to actually upgrade the series. After the upgrade has been completed, the
PostSeriesUpgradeEvent
will fire.Scheduled for removal in Juju version 4.0.
- class ops.PrefixedEvents(emitter: Object, key: str)[source]¶
Bases:
object
Events to be found in all events using a specific prefix.
- class ops.Relation(relation_name: str, relation_id: int, is_peer: bool, our_unit: Unit, backend: _ModelBackend, cache: _ModelCache, active: bool = True)[source]¶
Bases:
object
Represents an established relation between this application and another application.
This class should not be instantiated directly, instead use
Model.get_relation()
,Model.relations
, orops.RelationEvent.relation
. This is principally used byops.RelationMeta
to represent the relationships between charms.- active: bool¶
Indicates whether this relation is active.
This is normally
True
; it will beFalse
if the current event is arelation-broken
event associated with this relation.
- app: Application¶
Represents the remote application of this relation.
For peer relations, this will be the local application.
- data: RelationData¶
Holds the data buckets for each entity of a relation.
This is accessed using, for example,
Relation.data[unit]['foo']
.
- class ops.RelationBrokenEvent(handle: Handle, relation: Relation, app: Application | None = None, unit: Unit | None = None)[source]¶
Bases:
RelationEvent
Event triggered when a relation is removed.
If a relation is being removed (
juju remove-relation
orjuju remove-application
), once all the units have been removed, this event will fire to signal that the relationship has been fully terminated.The event indicates that the current relation is no longer valid, and that the charm’s software must be configured as though the relation had never existed. It will only be called after every callback method bound to
RelationDepartedEvent
has been run. If a callback method bound to this event is being executed, it is guaranteed that no remote units are currently known locally.
- class ops.RelationChangedEvent(handle: Handle, relation: Relation, app: Application | None = None, unit: Unit | None = None)[source]¶
Bases:
RelationEvent
Event triggered when relation data changes.
This event is triggered whenever there is a change to the data bucket for a related application or unit. Look at
event.relation.data[event.unit/app]
to see the new information, whereevent
is the event object passed to the callback method bound to this event.This event always fires once, after
RelationJoinedEvent
, and will subsequently fire whenever that remote unit changes its data for the relation. Callback methods bound to this event should be the only ones that rely on remote relation data. They should not error if the data is incomplete, since it can be guaranteed that when the remote unit or application changes its data, the event will fire again.The data that may be queried, or set, are determined by the relation’s interface.
- class ops.RelationCreatedEvent(handle: Handle, relation: Relation, app: Application | None = None, unit: Unit | None = None)[source]¶
Bases:
RelationEvent
Event triggered when a new relation is created.
This is triggered when a new integration with another app is added in Juju. This can occur before units for those applications have started. All existing relations will trigger RelationCreatedEvent before
StartEvent
is emitted.
- class ops.RelationData(relation: Relation, our_unit: Unit, backend: _ModelBackend)[source]¶
Bases:
Mapping
[Unit
|Application
,RelationDataContent
]Represents the various data buckets of a given relation.
Each unit and application involved in a relation has their own data bucket. For example,
{entity: RelationDataContent}
, where entity can be either aUnit
or anApplication
.Units can read and write their own data, and if they are the leader, they can read and write their application data. They are allowed to read remote unit and application data.
This class should not be instantiated directly, instead use
Relation.data
- exception ops.RelationDataAccessError[source]¶
Bases:
RelationDataError
Raised by
Relation.data[entity][key] = value
if unable to access.This typically means that permission to write read/write the databag is missing, but in some cases it is raised when attempting to read/write from a deceased remote entity.
- class ops.RelationDataContent(relation: Relation, entity: Unit | Application, backend: _ModelBackend)[source]¶
Bases:
LazyMapping
,MutableMapping
[str
,str
]Data content of a unit or application in a relation.
- exception ops.RelationDataError[source]¶
Bases:
ModelError
Raised when a relation data read/write is invalid.
This is raised either when trying to set a value to something that isn’t a string, or when trying to set a value in a bucket without the required access. (For example, another application/unit, or setting application data without being the leader.)
- exception ops.RelationDataTypeError[source]¶
Bases:
RelationDataError
Raised by
Relation.data[entity][key] = value
if key or value are not strings.
- class ops.RelationDepartedEvent(handle: Handle, relation: Relation, app: Application | None = None, unit: Unit | None = None, departing_unit_name: str | None = None)[source]¶
Bases:
RelationEvent
Event triggered when a unit leaves a relation.
This is the inverse of the
RelationJoinedEvent
, representing when a unit is leaving the relation (the unit is being removed, the app is being removed, the relation is being removed). For remaining units, this event is emitted once for each departing unit. For departing units, this event is emitted once for each remaining unit.Callback methods bound to this event may be used to remove all references to the departing remote unit, because there’s no guarantee that it’s still part of the system; it’s perfectly probable (although not guaranteed) that the system running that unit has already shut down.
Once all callback methods bound to this event have been run for such a relation, the unit agent will fire the
RelationBrokenEvent
.- property departing_unit: Unit | None¶
The
ops.Unit
that is departing, if any.Use this method to determine (for example) whether this unit is the departing one.
- restore(snapshot: Dict[str, Any])[source]¶
Used by the framework to deserialize the event from disk.
Not meant to be called by charm code.
- class ops.RelationEvent(handle: Handle, relation: Relation, app: Application | None = None, unit: Unit | None = None)[source]¶
Bases:
HookEvent
A base class representing the various relation lifecycle events.
Relation lifecycle events are generated when application units participate in relations. Units can only participate in relations after they have been “started”, and before they have been “stopped”. Within that time window, the unit may participate in several different relations at a time, including multiple relations with the same name.
- app: Application¶
The remote application that has triggered this event.
- restore(snapshot: Dict[str, Any])[source]¶
Used by the framework to deserialize the event from disk.
Not meant to be called by charm code.
- snapshot() Dict[str, Any] [source]¶
Used by the framework to serialize the event to disk.
Not meant to be called by charm code.
- unit: Unit | None¶
The remote unit that has triggered this event.
This will be
None
if the relation event was triggered as anApplication
-level event.
- class ops.RelationJoinedEvent(handle: Handle, relation: Relation, app: Application | None = None, unit: Unit | None = None)[source]¶
Bases:
RelationEvent
Event triggered when a new unit joins a relation.
This event is triggered whenever a new unit of a related application joins the relation. The event fires only when that remote unit is first observed by the unit. Callback methods bound to this event may set any local unit data that can be determined using no more than the name of the joining unit and the remote
private-address
setting, which is always available when the relation is created and is by convention not deleted.
- class ops.RelationMapping(relations_meta: Dict[str, RelationMeta], our_unit: Unit, backend: _ModelBackend, cache: _ModelCache, broken_relation_id: int | None)[source]¶
Bases:
Mapping
[str
,List
[Relation
]]Map of relation names to lists of
Relation
instances.
- class ops.RelationMeta(role: RelationRole, relation_name: str, raw: _RelationMetaDict)[source]¶
Bases:
object
Object containing metadata about a relation definition.
Should not be constructed directly by charm code, but gotten from one of
CharmMeta.peers
,CharmMeta.requires
,CharmMeta.provides
, orCharmMeta.relations
.- VALID_SCOPES = ['global', 'container']¶
- optional: bool¶
If True, the relation is considered optional.
This value is informational only and is not used by Juju itself (all relations are optional from Juju’s perspective), but it may be set in
metadata.yaml
and used by the charm code if appropriate.
- role: RelationRole¶
Role this relation takes, one of ‘peer’, ‘requires’, or ‘provides’.
- exception ops.RelationNotFoundError[source]¶
Bases:
ModelError
Raised when querying Juju for a given relation and that relation doesn’t exist.
- class ops.RelationRole(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
Enum
An annotation for a charm’s role in a relation.
For each relation a charm’s role may be
A Peer
A service consumer in the relation (‘requires’)
A service provider in the relation (‘provides’)
- is_peer() bool [source]¶
Report whether this role is ‘peer’.
role.is_peer()
is a shortcut forrole == ops.RelationRole.peer
.
- peer = 'peer'¶
- provides = 'provides'¶
- requires = 'requires'¶
- class ops.RemoveEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered when a unit is about to be terminated.
This event fires prior to Juju removing the charm and terminating its unit.
- defer() NoReturn [source]¶
Remove events are not deferrable like other events.
This is because the unit is about to be torn down, and there will not be an opportunity for the deferred event to run.
- Raises:
RuntimeError – always.
- class ops.ResourceMeta(name: str, raw: _ResourceMetaDict)[source]¶
Bases:
object
Object containing metadata about a resource definition.
- class ops.Resources(names: Iterable[str], backend: _ModelBackend)[source]¶
Bases:
object
Object representing resources for the charm.
- class ops.Secret(backend: _ModelBackend, id: str | None = None, label: str | None = None, content: Dict[str, str] | None = None, _secret_set_cache: defaultdict[str, Dict[str, Any]] | None = None)[source]¶
Bases:
object
Represents a single secret in the model.
This class should not be instantiated directly, instead use
Model.get_secret()
(for observers and owners), orApplication.add_secret()
orUnit.add_secret()
(for owners).All secret events have a
.secret
attribute which provides theSecret
associated with that event.Added in Juju version 3.0: Charm secrets added in Juju 3.0, user secrets added in Juju 3.3
- get_content(*, refresh: bool = False) Dict[str, str] [source]¶
Get the secret’s content.
The content of the secret is cached on the
Secret
object, so subsequent calls do not require querying the secret storage again, unlessrefresh=True
is used, orset_content()
is called.- Returns:
A copy of the secret’s content dictionary.
- Parameters:
refresh – If true, fetch the latest revision’s content and tell Juju to update to tracking that revision. The default is to get the content of the currently-tracked revision.
- Raises:
SecretNotFoundError – if the secret no longer exists.
ModelError – if the charm does not have permission to access the secret.
- get_info() SecretInfo [source]¶
Get this secret’s information (metadata).
Only secret owners can fetch this information.
- Raises:
SecretNotFoundError – if the secret no longer exists, or if the charm does not have permission to access the secret.
- grant(relation: Relation, *, unit: Unit | None = None)[source]¶
Grant read access to this secret.
If the application or unit has already been granted access to this secret, do nothing.
- Parameters:
relation – The relation used to scope the life of this secret.
unit – If specified, grant access to only this unit, rather than all units in the application.
- property id: str | None¶
Locator ID (URI) for this secret.
This has an unfortunate name for historical reasons, as it’s not really a unique identifier, but the secret’s locator URI, which will include the model UUID (for cross-model secrets).
Charms should treat this as an opaque string for looking up secrets and sharing them via relation data. If a charm-local “name” is needed for a secret, use a
label
. (If a charm needs a truly unique identifier for identifying one secret in a set of secrets of arbitrary size, useunique_identifier
– this should be rare.)This will be None if the secret was obtained using
Model.get_secret()
with a label but no ID.
- property label: str | None¶
Label used to reference this secret locally.
This label is effectively a name for the secret that’s local to the charm, for example “db-password” or “tls-cert”. The secret owner sets a label with
Application.add_secret()
orUnit.add_secret()
, and the secret observer sets a label with a call toModel.get_secret()
.The label property can be used distinguish between multiple secrets in event handlers like
ops.SecretChangedEvent
. For example, if a charm is observing two secrets, it might callmodel.get_secret(id=secret_id, label='db-password')
andmodel.get_secret(id=secret_id, label='tls-cert')
in the relevant relation-changed event handlers, and then switch onevent.secret.label
in secret-changed:def _on_secret_changed(self, event): if event.secret.label == 'db-password': content = event.secret.get_content(refresh=True) self._configure_db_credentials(content['username'], content['password']) elif event.secret.label == 'tls-cert': content = event.secret.get_content(refresh=True) self._update_tls_cert(content['cert']) else: pass # ignore other labels (or log a warning)
Juju will ensure that the entity (the owner or observer) only has one secret with this label at once.
This will be None if the secret was obtained using
Model.get_secret()
with an ID but no label.
- peek_content() Dict[str, str] [source]¶
Get the content of the latest revision of this secret.
This returns the content of the latest revision without updating the tracking. The content is not cached locally, so multiple calls will result in multiple queries to the secret storage.
- Raises:
SecretNotFoundError – if the secret no longer exists.
ModelError – if the charm does not have permission to access the secret.
- remove_all_revisions() None [source]¶
Remove all revisions of this secret.
This is called when the secret is no longer needed, for example when handling
ops.RelationBrokenEvent
.If the charm does not have permission to remove the secret, or it no longer exists, this method will succeed, but the unit will go into error state on completion of the current Juju hook.
- remove_revision(revision: int)[source]¶
Remove the given secret revision.
This is normally only called when handling
ops.SecretRemoveEvent
orops.SecretExpiredEvent
.If the charm does not have permission to remove the secret, or it no longer exists, this method will succeed, but the unit will go into error state on completion of the current Juju hook.
- Parameters:
revision – The secret revision to remove. This should usually be set to
SecretRemoveEvent.revision
orSecretExpiredEvent.revision
.
- revoke(relation: Relation, *, unit: Unit | None = None)[source]¶
Revoke read access to this secret.
If the application or unit does not have access to this secret, do nothing.
- Parameters:
relation – The relation used to scope the life of this secret.
unit – If specified, revoke access to only this unit, rather than all units in the application.
- set_content(content: Dict[str, str])[source]¶
Update the content of this secret.
This will create a new secret revision, and notify all units tracking the secret (the “observers”) that a new revision is available with a
ops.SecretChangedEvent
.If the charm does not have permission to update the secret, or the secret no longer exists, this method will succeed, but the unit will go into error state on completion of the current Juju hook.
Changed in Juju version 3.6: A new secret revision will not be created if the content being set is identical to the latest revision.
- Parameters:
content – A key-value mapping containing the payload of the secret, for example
{"password": "foo123"}
.
- set_info(*, label: str | None = None, description: str | None = None, expire: datetime | timedelta | None = None, rotate: SecretRotate | None = None)[source]¶
Update this secret’s information (metadata).
This will not create a new secret revision (that applies only to
set_content()
). Once attributes are set, they cannot be unset.If the charm does not have permission to update the secret, or the secret no longer exists, this method will succeed, but the unit will go into error state on completion of the current Juju hook.
- Parameters:
label – New label to apply.
description – New description to apply.
expire – New expiration time (or timedelta from now) to apply.
rotate – New rotation policy to apply. The new policy will take effect only after the currently-scheduled rotation.
- property unique_identifier: str | None¶
Unique identifier of this secret.
This is the secret’s globally-unique identifier (currently a 20-character Xid, for example “9m4e2mr0ui3e8a215n4g”).
Charms should use
id
(the secret’s locator ID) to send the secret’s ID across relation data, and labels (label
) to assign a charm-local “name” to the secret for lookup in this charm. However,unique_identifier
can be useful to distinguish secrets in cases where the charm has a set of secrets of arbitrary size, for example, a group of 10 or 20 TLS certificates.This will be None if the secret was obtained using
Model.get_secret()
with a label but no ID.
- class ops.SecretChangedEvent(handle: Handle, id: str, label: str | None)[source]¶
Bases:
SecretEvent
Event triggered on the secret observer charm when the secret owner changes its contents.
When the owner of a secret changes the secret’s contents, Juju will create a new secret revision, and all applications or units that are tracking this secret will be notified via this event that a new revision is available.
Typically, the charm will fetch the new content by calling
event.secret.get_content()
withrefresh=True
to tell Juju to start tracking the new revision.Added in Juju version 3.0: Charm secrets added in Juju 3.0, user secrets added in Juju 3.3
- class ops.SecretEvent(handle: Handle, id: str, label: str | None)[source]¶
Bases:
HookEvent
Base class for all secret events.
- restore(snapshot: Dict[str, Any])[source]¶
Used by the framework to deserialize the event from disk.
Not meant to be called by charm code.
- property secret: Secret¶
The secret instance this event refers to.
Note that the secret content is not retrieved from the secret storage until
Secret.get_content()
is called.
- class ops.SecretExpiredEvent(handle: Handle, id: str, label: str | None, revision: int)[source]¶
Bases:
SecretEvent
Event triggered on the secret owner charm when a secret’s expiration time elapses.
This event is fired on the secret owner to inform it that the secret revision must be removed. The event will keep firing until the owner removes the revision by calling
event.secret.remove_revision()
.Added in Juju version 3.0.
- defer() NoReturn [source]¶
Secret expiration events are not deferrable (Juju handles re-invocation).
- Raises:
RuntimeError – always.
- class ops.SecretInfo(id: str, label: str | None, revision: int, expires: datetime | None, rotation: SecretRotate | None, rotates: datetime | None, description: str | None = None, *, model_uuid: str | None = None)[source]¶
Bases:
object
Secret information (metadata).
- exception ops.SecretNotFoundError[source]¶
Bases:
ModelError
Raised when the specified secret does not exist.
- class ops.SecretRemoveEvent(handle: Handle, id: str, label: str | None, revision: int)[source]¶
Bases:
SecretEvent
Event triggered on the secret owner charm when a secret revision can be removed.
When the owner of a secret creates a new revision, and after all observers have updated to that new revision, this event will be fired to inform the secret owner that the old revision can be removed.
After any required cleanup, the charm should call
event.secret.remove_revision()
to remove the now-unused revision. If the charm does not, then the event will be emitted again, when further revisions are ready for removal.Added in Juju version 3.0.
- class ops.SecretRotate(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
Enum
Secret rotation policies.
- DAILY = 'daily'¶
- HOURLY = 'hourly'¶
- MONTHLY = 'monthly'¶
- NEVER = 'never'¶
- QUARTERLY = 'quarterly'¶
- WEEKLY = 'weekly'¶
- YEARLY = 'yearly'¶
- class ops.SecretRotateEvent(handle: Handle, id: str, label: str | None)[source]¶
Bases:
SecretEvent
Event triggered on the secret owner charm when the secret’s rotation policy elapses.
This event is fired on the secret owner to inform it that the secret must be rotated. The event will keep firing until the owner creates a new revision by calling
event.secret.set_content()
.Added in Juju version 3.0.
- defer() NoReturn [source]¶
Secret rotation events are not deferrable (Juju handles re-invocation).
- Raises:
RuntimeError – always.
- class ops.Serializable(*args, **kwargs)[source]¶
Bases:
Protocol
The type returned by
Framework.load_snapshot()
.- handle_kind = ''¶
- class ops.ServiceInfoMapping(services: Iterable[ServiceInfo])[source]¶
Bases:
Mapping
[str
,ServiceInfo
]Map of service names to
pebble.ServiceInfo
objects.This is done as a mapping object rather than a plain dictionary so that we can extend it later, and so it’s not mutable.
- class ops.StartEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered immediately after first configuration change.
This event is triggered immediately after the first
ConfigChangedEvent
. Callback methods bound to the event should be used to ensure that the charm’s software is in a running state. Note that the charm’s software should be configured so as to persist in this state through reboots without further intervention on Juju’s part.
- class ops.StatusBase(message: str = '')[source]¶
Bases:
object
Status values specific to applications and units.
To access a status by name, use
StatusBase.from_name()
. However, most use cases will directly use the child class such asActiveStatus
to indicate their status.- classmethod from_name(name: str, message: str)[source]¶
Create a status instance from a name and message.
If
name
is “unknown”,message
is ignored, because unknown status does not have an associated message.- Parameters:
name – Name of the status, one of: “active”, “blocked”, “maintenance”, “waiting”, “error”, or “unknown”.
message – Message to include with the status.
- Raises:
KeyError – If
name
is not a registered status.
- classmethod register(child: Type[StatusBase])[source]¶
Deprecated since version 2.17.0: Deprecated - this was for internal use only.
- class ops.StopEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered when a charm is shut down.
This event is triggered when an application’s removal is requested by the client. The event fires immediately before the end of the unit’s destruction sequence. Callback methods bound to this event should be used to ensure that the charm’s software is not running, and that it will not start again on reboot.
- defer() NoReturn [source]¶
Stop events are not deferrable like other events.
This is because the unit is in the process of tearing down, and there will not be an opportunity for the deferred event to run.
- Raises:
RuntimeError – always.
- class ops.Storage(storage_name: str, storage_index: int, backend: _ModelBackend)[source]¶
Bases:
object
Represents a storage as defined in
metadata.yaml
.- property id: int¶
Deprecated since version 2.4.0: Use
Storage.index
instead.
- class ops.StorageAttachedEvent(handle: Handle, storage: Storage)[source]¶
Bases:
StorageEvent
Event triggered when new storage becomes available.
This event is triggered when new storage is available for the charm to use.
Callback methods bound to this event allow the charm to run code when storage has been added. Such methods will be run before the
InstallEvent
fires, so that the installation routine may use the storage. The name prefix of this hook will depend on the storage key defined in themetadata.yaml
file.
- class ops.StorageDetachingEvent(handle: Handle, storage: Storage)[source]¶
Bases:
StorageEvent
Event triggered prior to removal of storage.
This event is triggered when storage a charm has been using is going away.
Callback methods bound to this event allow the charm to run code before storage is removed. Such methods will be run before storage is detached, and always before the
StopEvent
fires, thereby allowing the charm to gracefully release resources before they are removed and before the unit terminates. The name prefix of the hook will depend on the storage key defined in themetadata.yaml
file.
- class ops.StorageEvent(handle: Handle, storage: Storage)[source]¶
Bases:
HookEvent
Base class representing storage-related events.
Juju can provide a variety of storage types to a charms. The charms can define several different types of storage that are allocated from Juju. Changes in state of storage trigger sub-types of
StorageEvent
.- restore(snapshot: Dict[str, Any])[source]¶
Used by the framework to deserialize the event from disk.
Not meant to be called by charm code.
- class ops.StorageMapping(storage_names: Iterable[str], backend: _ModelBackend)[source]¶
Bases:
Mapping
[str
,List
[Storage
]]Map of storage names to lists of Storage instances.
- request(storage_name: str, count: int = 1)[source]¶
Requests new storage instances of a given name.
Uses storage-add tool to request additional storage. Juju will notify the unit via
<storage-name>-storage-attached
events when it becomes available.- Raises:
ModelError – if the storage is not in the charm’s metadata.
- class ops.StorageMeta(name: str, raw: _StorageMetaDict)[source]¶
Bases:
object
Object containing metadata about a storage definition.
- multiple_range: Tuple[int, int | None] | None¶
Range of numeric qualifiers when multiple storage units are used.
True if all units of the application share the storage.
- class ops.StoredDict(stored_data: StoredStateData, under: Dict[Hashable, Any])[source]¶
Bases:
MutableMapping
[Hashable
,Any
]A dict-like object that uses the StoredState as backend.
Charms are not expected to use this class directly. Adding a
StoredState
attribute to a charm class will automatically use this class to store dictionaries.
- class ops.StoredList(stored_data: StoredStateData, under: List[Any])[source]¶
Bases:
MutableSequence
[Any
]A list-like object that uses the StoredState as backend.
Charms are not expected to use this class directly. Adding a
StoredState
attribute to a charm class will automatically use this class to store lists.
- class ops.StoredSet(stored_data: StoredStateData, under: Set[Any])[source]¶
Bases:
MutableSet
[Any
]A set-like object that uses the StoredState as backend.
Charms are not expected to use this class directly. Adding a
StoredState
attribute to a charm class will automatically use this class to store sets.
- class ops.StoredState[source]¶
Bases:
object
A class used to store data the charm needs, persisted across invocations.
Instances of
MyClass
can transparently save state between invocations by setting attributes on_stored
. Initial state should be set withset_default
on the bound object; for example:class MyClass(ops.Object): _stored = ops.StoredState() def __init__(self, parent, key): super().__init__(parent, key) self._stored.set_default(seen=set()) self.framework.observe(self.on.seen, self._on_seen) def _on_seen(self, event): self._stored.seen.add(event.uuid)
Data is stored alongside the charm (in the charm container for Kubernetes sidecar charms, and on the machine for machine charms). The exceptions are two deprecated cases: Kubernetes podspec charms, and charms explicitly passing True for use_juju_for_storage when running
ops.main()
.For machine charms, charms are upgraded in-place on the machine, so the data is preserved. For Kubernetes sidecar charms, when the charm is upgraded, the pod is replaced, so any data is lost. When data should be preserved across upgrades, Kubernetes sidecar charms should use a peer-relation for the data instead of StoredState.
- class ops.StoredStateData(parent: Object, attr_name: str)[source]¶
Bases:
Object
Manager of the stored data.
- exception ops.TooManyRelatedAppsError(relation_name: str, num_related: int, max_supported: int)[source]¶
Bases:
ModelError
Raised by
Model.get_relation()
if there is more than one integrated application.
- class ops.Unit(name: str, meta: CharmMeta, backend: _ModelBackend, cache: _ModelCache)[source]¶
Bases:
object
Represents a named unit in the model.
This might be the current unit, another unit of the charm’s application, or a unit of another application that the charm is integrated with.
- add_secret(content: Dict[str, str], *, label: str | None = None, description: str | None = None, expire: datetime | timedelta | None = None, rotate: SecretRotate | None = None) Secret [source]¶
Create a
Secret
owned by this unit.See
Application.add_secret()
for parameter details.- Raises:
ValueError – if the secret is empty, or the secret key is invalid.
- app: Application¶
Application the unit is part of.
- close_port(protocol: Literal['tcp', 'udp', 'icmp'], port: int | None = None) None [source]¶
Close a port with the given protocol for this unit.
Some behaviour, such as whether the port is closed externally without using “juju unexpose”, differs between Kubernetes and machine charms. See the Juju documentation for more detail.
Use
set_ports()
for a more declarative approach where all of the ports that should be open are provided in a single call. For example,set_ports()
will close all open ports.- Parameters:
protocol – String representing the protocol; must be one of ‘tcp’, ‘udp’, or ‘icmp’ (lowercase is recommended, but uppercase is also supported).
port – The port to open. Required for TCP and UDP; not allowed for ICMP.
- Raises:
ModelError – If
port
is provided whenprotocol
is ‘icmp’ orport
is not provided whenprotocol
is ‘tcp’ or ‘udp’.
- property containers: Mapping[str, Container]¶
Return a mapping of containers indexed by name.
- Raises:
RuntimeError – if called for another unit
- get_container(container_name: str) Container [source]¶
Get a single container by name.
- Raises:
ModelError – if the named container doesn’t exist
- is_leader() bool [source]¶
Return whether this unit is the leader of its application.
This can only be called for the current unit.
- Raises:
RuntimeError – if called for another unit
- open_port(protocol: Literal['tcp', 'udp', 'icmp'], port: int | None = None) None [source]¶
Open a port with the given protocol for this unit.
Some behaviour, such as whether the port is opened externally without using “juju expose” and whether the opened ports are per-unit, differs between Kubernetes and machine charms. See the Juju documentation for more detail.
Use
set_ports()
for a more declarative approach where all of the ports that should be open are provided in a single call.- Parameters:
protocol – String representing the protocol; must be one of ‘tcp’, ‘udp’, or ‘icmp’ (lowercase is recommended, but uppercase is also supported).
port – The port to open. Required for TCP and UDP; not allowed for ICMP.
- Raises:
ModelError – If
port
is provided whenprotocol
is ‘icmp’ orport
is not provided whenprotocol
is ‘tcp’ or ‘udp’.
- reboot(now: bool = False) None [source]¶
Reboot the host machine.
Normally, the reboot will only take place after the current hook successfully completes. Use
now=True
to reboot immediately without waiting for the hook to complete; this is useful when multiple restarts are required (Juju will re-run the hook after rebooting).This is not supported on Kubernetes charms, can only be called for the current unit, and cannot be used in an action hook.
- Parameters:
now – terminate immediately without waiting for the current hook to complete, restarting the hook after reboot.
- Raises:
RuntimeError – if called on a remote unit.
ModelError – if used in an action hook.
- set_ports(*ports: int | Port) None [source]¶
Set the open ports for this unit, closing any others that are open.
Some behaviour, such as whether the port is opened or closed externally without using Juju’s
expose
andunexpose
commands, differs between Kubernetes and machine charms. See the Juju documentation for more detail.Use
open_port()
andclose_port()
to manage ports individually.- Parameters:
ports – The ports to open. Provide an int to open a TCP port, or a
Port
to open a port for another protocol.- Raises:
ModelError – if a
Port
is provided whereprotocol
is ‘icmp’ butport
is notNone
, or whereprotocol
is ‘tcp’ or ‘udp’ andport
isNone
.
- set_workload_version(version: str) None [source]¶
Record the version of the software running as the workload.
This shouldn’t be confused with the revision of the charm. This is informative only; shown in the output of ‘juju status’.
- property status: StatusBase¶
Used to report or read the status of a specific unit.
Changes to status take effect immediately, unlike other Juju operations such as modifying relation data or secrets, which only take effect after a successful event.
The status of any unit other than the current unit is always Unknown.
Alternatively, use the
collect_unit_status
event to evaluate and set unit status consistently at the end of every hook.- Raises:
RuntimeError – if setting the status of a unit other than the current unit
InvalidStatusError – if setting the status to something other than a
StatusBase
Example:
self.model.unit.status = ops.MaintenanceStatus('reconfiguring the frobnicators')
- class ops.UnknownStatus[source]¶
Bases:
StatusBase
The unit status is unknown.
A unit-agent has finished calling install, config-changed and start, but the charm has not called status-set yet.
This status is read-only; trying to set unit or application status to
UnknownStatus
will raiseInvalidStatusError
.
- class ops.UpdateStatusEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered by a status update request from Juju.
This event is periodically triggered by Juju so that it can provide constant feedback to the administrator about the status of the application the charm is modeling. Any callback method bound to this event should determine the “health” of the application and set the status appropriately.
The interval between
UpdateStatusEvent
events can be configured model-wide, e.g.juju model-config update-status-hook-interval=1m
.
- class ops.UpgradeCharmEvent(handle: Handle)[source]¶
Bases:
HookEvent
Event triggered by request to upgrade the charm.
This event will be triggered when an administrator executes
juju upgrade-charm
. The event fires after Juju has unpacked the upgraded charm code, and so this event will be handled by the callback method bound to the event in the new codebase. The associated callback method is invoked provided there is no existing error state. The callback method should be used to reconcile current state written by an older version of the charm into whatever form that is needed by the current charm version.
- class ops.WaitingStatus(message: str = '')[source]¶
Bases:
StatusBase
The unit or application is waiting on a charm it’s integrated with.
Set this status when waiting on a charm this is integrated with. For example, a web app charm would set “waiting” status when it is integrated with a database charm that is not ready yet (it might be creating a database). In contrast to
MaintenanceStatus
, “waiting” reflects activity on related units, not on this unit or charm.
- class ops.WorkloadEvent(handle: Handle, workload: Container)[source]¶
Bases:
HookEvent
Base class representing workload-related events.
Workload events are generated for all containers that the charm expects in metadata.
- restore(snapshot: Dict[str, Any])[source]¶
Used by the framework to deserialize the event from disk.
Not meant to be called by charm code.
ops.main entry point¶
The main entry point to initialise and run your charm.
- ops.main(charm_class: Type[CharmBase], use_juju_for_storage: bool | None = None)¶
Set up the charm and dispatch the observed event.
Recommended usage:
import ops class SomeCharm(ops.CharmBase): ... if __name__ == "__main__": ops.main(SomeCharm)
- Parameters:
charm_class – the charm class to instantiate and receive the event.
use_juju_for_storage – whether to use controller-side storage. The default is
False
for most charms. Podspec charms that haven’t previously used local storage and that are running on a new enough Juju default to controller-side storage, and local storage otherwise.
legacy main module¶
Support legacy ops.main.main() import.