diff --git a/charms/openstack-hypervisor/charmcraft.yaml b/charms/openstack-hypervisor/charmcraft.yaml index 9a08b7eb..fb425369 100644 --- a/charms/openstack-hypervisor/charmcraft.yaml +++ b/charms/openstack-hypervisor/charmcraft.yaml @@ -62,6 +62,25 @@ actions: description: | List host NICS, and which one are candidates for use as external NIC. additionalProperties: false + disable: + description: | + Prevent new instances from being created. + params: + reason: + type: string + description: Reason for disabling the hypervisor + default: Stopped via operator action + additionalProperties: false + enable: + description: | + Allow new instances to be created. + additionalProperties: false + running-guests: + description: | + List the running guests on the hypervisor. + + Only lists guests created by the OpenStack cloud. + additionalProperties: false requires: amqp: diff --git a/charms/openstack-hypervisor/src/charm.py b/charms/openstack-hypervisor/src/charm.py index 64ff4cb4..b2505f58 100755 --- a/charms/openstack-hypervisor/src/charm.py +++ b/charms/openstack-hypervisor/src/charm.py @@ -71,6 +71,10 @@ DATA_BINDING = "data" MTLS_USAGES = {x509.OID_SERVER_AUTH, x509.OID_CLIENT_AUTH} +class HypervisorError(Exception): + """Custom exception for Hypervisor errors.""" + + @sunbeam_tracing.trace_type class MTlsCertificatesHandler(sunbeam_rhandlers.TlsCertificatesHandler): """Handler for certificates interface.""" @@ -178,6 +182,18 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm): self.on.list_nics_action, self._list_nics_action, ) + self.framework.observe( + self.on.enable_action, + self._enable_action, + ) + self.framework.observe( + self.on.disable_action, + self._disable_action, + ) + self.framework.observe( + self.on.running_guests_action, + self._running_guests_action, + ) self.framework.observe( self.on.install, self._on_install, @@ -231,6 +247,14 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm): return None return str(address) + def _proxy_configs(self) -> dict[str, str]: + """Return proxy configs.""" + return { + "HTTPS_PROXY": os.environ.get("JUJU_CHARM_HTTPS_PROXY", ""), + "HTTP_PROXY": os.environ.get("JUJU_CHARM_HTTP_PROXY", ""), + "NO_PROXY": os.environ.get("JUJU_CHARM_NO_PROXY", ""), + } + def check_relation_exists(self, relation_name: str) -> bool: """Check if a relation exists or not.""" if self.model.get_relation(relation_name): @@ -325,14 +349,13 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm): if new_snap_settings: self.set_snap_data(new_snap_settings) - def _list_nics_action(self, event: ActionEvent): - """Run list_nics action.""" + def _hypervisor_cli_cmd(self, cmd: str): + """Helper to run cli commands on the snap.""" cache = self.get_snap_cache() hypervisor = cache["openstack-hypervisor"] if not hypervisor.present: - event.fail("Hypervisor is not installed") - return + raise HypervisorError("Hypervisor is not installed") process = subprocess.run( [ @@ -340,10 +363,8 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm): "run", "openstack-hypervisor", "--verbose", - "list-nics", - "--format", - "json", - ], + ] + + cmd.split(), capture_output=True, ) @@ -352,12 +373,54 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm): stdout = process.stdout.decode("utf-8") logger.debug("stdout: %s", stdout) if process.returncode != 0: - event.fail(stderr) + raise HypervisorError(stderr) + + return stdout + + def _list_nics_action(self, event: ActionEvent): + """Run list_nics action.""" + try: + stdout = self._hypervisor_cli_cmd("list-nics --format json") + except HypervisorError as e: + event.fail(str(e)) return # cli returns a json dict with keys "nics" and "candidate" event.set_results({"result": stdout}) + def _enable_action(self, event: ActionEvent): + """Run enable action.""" + try: + stdout = self._hypervisor_cli_cmd("hypervisor enable") + except HypervisorError as e: + event.fail(str(e)) + return + + event.set_results({"result": stdout}) + + def _disable_action(self, event: ActionEvent): + """Run disable action.""" + try: + stdout = self._hypervisor_cli_cmd("hypervisor disable") + except HypervisorError as e: + event.fail(str(e)) + return + + event.set_results({"result": stdout}) + + def _running_guests_action(self, event: ActionEvent): + """List running openstack guests.""" + try: + stdout = self._hypervisor_cli_cmd( + "hypervisor running-guests --format json" + ) + except HypervisorError as e: + event.fail(str(e)) + return + + # cli returns a json list + event.set_results({"result": stdout}) + def ensure_services_running(self): """Ensure systemd services running.""" # This should taken care of by the snap @@ -476,6 +539,7 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm): "identity.password": contexts.identity_credentials.password, "identity.project-domain-id": contexts.identity_credentials.project_domain_id, "identity.project-domain-name": contexts.identity_credentials.project_domain_name, + "identity.project-id": contexts.identity_credentials.project_id, "identity.project-name": contexts.identity_credentials.project_name, "identity.region-name": contexts.identity_credentials.region, "identity.user-domain-id": contexts.identity_credentials.user_domain_id, diff --git a/charms/openstack-hypervisor/tests/unit/test_charm.py b/charms/openstack-hypervisor/tests/unit/test_charm.py index 5441c46e..092200aa 100644 --- a/charms/openstack-hypervisor/tests/unit/test_charm.py +++ b/charms/openstack-hypervisor/tests/unit/test_charm.py @@ -150,6 +150,7 @@ class TestCharm(test_utils.CharmTestCase): "identity.password": "user-password", "identity.project-domain-id": "pdomain-id", "identity.project-domain-name": "pdomain_-ame", + "identity.project-id": "uproj-id", "identity.project-name": "user-project", "identity.region-name": "region12", "identity.user-domain-id": "udomain-id", @@ -261,6 +262,7 @@ class TestCharm(test_utils.CharmTestCase): "identity.password": "user-password", "identity.project-domain-id": "pdomain-id", "identity.project-domain-name": "pdomain_-ame", + "identity.project-id": "uproj-id", "identity.project-name": "user-project", "identity.region-name": "region12", "identity.user-domain-id": "udomain-id",