X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=installers%2Fcharm%2Fosm-nglcm%2Flib%2Fcharms%2Fosm_temporal%2Fv0%2Ftemporal.py;fp=installers%2Fcharm%2Fosm-nglcm%2Flib%2Fcharms%2Fosm_temporal%2Fv0%2Ftemporal.py;h=2fc2fe2257a9a7cc58dbbe0d53315847c0d47092;hb=7cb878a01863db170ee1d6c238d13708295b9748;hp=0000000000000000000000000000000000000000;hpb=e1005c45b33ba19c6a63a46b374813a34911b27a;p=osm%2Fdevops.git diff --git a/installers/charm/osm-nglcm/lib/charms/osm_temporal/v0/temporal.py b/installers/charm/osm-nglcm/lib/charms/osm_temporal/v0/temporal.py new file mode 100644 index 00000000..2fc2fe22 --- /dev/null +++ b/installers/charm/osm-nglcm/lib/charms/osm_temporal/v0/temporal.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +# Copyright 2022 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact: legal@canonical.com +# +# To get in touch with the maintainers, please contact: +# osm-charmers@lists.launchpad.net +# +# +# Learn more at: https://juju.is/docs/sdk + +"""Temporal Frontend library. + +This [library](https://juju.is/docs/sdk/libraries) implements both sides of the +`temporal` [interface](https://juju.is/docs/sdk/relations). + +The *provider* side of this interface is implemented by the +[osm-temporal Charmed Operator](https://charmhub.io/osm-temporal). + +Any Charmed Operator that *requires* Temporal for providing its +service should implement the *requirer* side of this interface. + +In a nutshell using this library to implement a Charmed Operator *requiring* +Temporal would look like + +``` +$ charmcraft fetch-lib charms.osm_temporal.v0.temporal +``` + +`metadata.yaml`: + +``` +requires: + temporal: + interface: frontend + limit: 1 +``` + +`src/charm.py`: + +``` +from charms.osm_temporal.v0.temporal import TemporalRequires +from ops.charm import CharmBase + + +class MyCharm(CharmBase): + + def __init__(self, *args): + super().__init__(*args) + self.temporal = TemporalRequires(self) + self.framework.observe( + self.on["temporal"].relation_changed, + self._on_temporal_relation_changed, + ) + self.framework.observe( + self.on["temporal"].relation_broken, + self._on_temporal_relation_broken, + ) + self.framework.observe( + self.on["temporal"].relation_broken, + self._on_temporal_broken, + ) + + def _on_temporal_relation_broken(self, event): + # Get TEMPORAL host and port + host: str = self.temporal.host + port: int = self.temporal.port + # host => "osm-temporal" + # port => 7233 + + def _on_temporal_broken(self, event): + # Stop service + # ... + self.unit.status = BlockedStatus("need temporal relation") +``` + +You can file bugs +[here](https://osm.etsi.org/bugzilla/enter_bug.cgi), selecting the `devops` module! +""" +from typing import Optional + +from ops.charm import CharmBase +from ops.framework import Object +from ops.model import Relation + + +# The unique Charmhub library identifier, never change it +LIBID = "5174d817d46c4e159c1a90ff8303d96a" + +# Increment this major API version when introducing breaking changes +LIBAPI = 0 + +# Increment this PATCH version before using `charmcraft publish-lib` or reset +# to 0 if you are raising the major API version +LIBPATCH = 1 + +TEMPORAL_HOST_APP_KEY = "host" +TEMPORAL_PORT_APP_KEY = "port" + + +class TemporalRequires(Object): # pragma: no cover + """Requires-side of the Temporal relation.""" + + def __init__(self, charm: CharmBase, endpoint_name: str = "temporal") -> None: + super().__init__(charm, endpoint_name) + self.charm = charm + self._endpoint_name = endpoint_name + + @property + def host(self) -> str: + """Get temporal hostname.""" + relation: Relation = self.model.get_relation(self._endpoint_name) + return ( + relation.data[relation.app].get(TEMPORAL_HOST_APP_KEY) + if relation and relation.app + else None + ) + + @property + def port(self) -> int: + """Get temporal port number.""" + relation: Relation = self.model.get_relation(self._endpoint_name) + return ( + int(relation.data[relation.app].get(TEMPORAL_PORT_APP_KEY)) + if relation and relation.app + else None + ) + + +class TemporalProvides(Object): + """Provides-side of the Temporal relation.""" + + def __init__(self, charm: CharmBase, endpoint_name: str = "temporal") -> None: + super().__init__(charm, endpoint_name) + self._endpoint_name = endpoint_name + + def set_host_info(self, host: str, port: int, relation: Optional[Relation] = None) -> None: + """Set Temporal host and port. + + This function writes in the application data of the relation, therefore, + only the unit leader can call it. + + Args: + host (str): Temporal hostname or IP address. + port (int): Temporal port. + relation (Optional[Relation]): Relation to update. + If not specified, all relations will be updated. + + Raises: + Exception: if a non-leader unit calls this function. + """ + if not self.model.unit.is_leader(): + raise Exception("only the leader set host information.") + + if relation: + self._update_relation_data(host, port, relation) + return + + for relation in self.model.relations[self._endpoint_name]: + self._update_relation_data(host, port, relation) + + def _update_relation_data(self, host: str, port: int, relation: Relation) -> None: + """Update data in relation if needed.""" + relation.data[self.model.app][TEMPORAL_HOST_APP_KEY] = host + relation.data[self.model.app][TEMPORAL_PORT_APP_KEY] = str(port)