Integrate NBI and Prometheus
[osm/devops.git] / installers / charm / osm-nbi / src / charm.py
index 964050a..00ef425 100755 (executable)
@@ -29,7 +29,9 @@ See more: https://charmhub.io/osm
 
 import logging
 from typing import Any, Dict
 
 import logging
 from typing import Any, Dict
+from urllib.parse import urlparse
 
 
+from charms.data_platform_libs.v0.data_interfaces import DatabaseRequires
 from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires
 from charms.nginx_ingress_integrator.v0.ingress import IngressRequires
 from charms.observability_libs.v1.kubernetes_service_patch import KubernetesServicePatch
 from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires
 from charms.nginx_ingress_integrator.v0.ingress import IngressRequires
 from charms.observability_libs.v1.kubernetes_service_patch import KubernetesServicePatch
@@ -40,13 +42,14 @@ from charms.osm_libs.v0.utils import (
     check_container_ready,
     check_service_active,
 )
     check_container_ready,
     check_service_active,
 )
+from charms.osm_nbi.v0.nbi import NbiProvides
 from lightkube.models.core_v1 import ServicePort
 from lightkube.models.core_v1 import ServicePort
-from ops.charm import ActionEvent, CharmBase
+from ops.charm import ActionEvent, CharmBase, RelationJoinedEvent
 from ops.framework import StoredState
 from ops.main import main
 from ops.model import ActiveStatus, Container
 
 from ops.framework import StoredState
 from ops.main import main
 from ops.model import ActiveStatus, Container
 
-from legacy_interfaces import KeystoneClient, MongoClient, PrometheusClient
+from legacy_interfaces import KeystoneClient
 
 HOSTPATHS = [
     HostPath(
 
 HOSTPATHS = [
     HostPath(
@@ -80,8 +83,10 @@ class OsmNbiCharm(CharmBase):
             },
         )
         self.kafka = KafkaRequires(self)
             },
         )
         self.kafka = KafkaRequires(self)
-        self.mongodb_client = MongoClient(self, "mongodb")
-        self.prometheus_client = PrometheusClient(self, "prometheus")
+        self.nbi = NbiProvides(self)
+        self.mongodb_client = DatabaseRequires(
+            self, "mongodb", database_name="osm", extra_user_roles="admin"
+        )
         self.keystone_client = KeystoneClient(self, "keystone")
         self._observe_charm_events()
         self.container: Container = self.unit.get_container("nbi")
         self.keystone_client = KeystoneClient(self, "keystone")
         self._observe_charm_events()
         self.container: Container = self.unit.get_container("nbi")
@@ -111,9 +116,10 @@ class OsmNbiCharm(CharmBase):
             # Eventually it will become ready after the first pebble-ready event.
             check_container_ready(self.container)
 
             # Eventually it will become ready after the first pebble-ready event.
             check_container_ready(self.container)
 
-            self._configure_service(self.container)
+            if not self.debug_mode.started:
+                self._configure_service(self.container)
             self._update_ingress_config()
             self._update_ingress_config()
-
+            self._update_nbi_relation()
             # Update charm status
             self._on_update_status()
         except CharmError as e:
             # Update charm status
             self._on_update_status()
         except CharmError as e:
@@ -123,6 +129,7 @@ class OsmNbiCharm(CharmBase):
     def _on_update_status(self, _=None) -> None:
         """Handler for the update-status event."""
         try:
     def _on_update_status(self, _=None) -> None:
         """Handler for the update-status event."""
         try:
+            self._validate_config()
             self._check_relations()
             if self.debug_mode.started:
                 return
             self._check_relations()
             if self.debug_mode.started:
                 return
@@ -145,6 +152,11 @@ class OsmNbiCharm(CharmBase):
         finally:
             self._on_update_status()
 
         finally:
             self._on_update_status()
 
+    def _update_nbi_relation(self, event: RelationJoinedEvent = None) -> None:
+        """Handler for the nbi-relation-joined event."""
+        if self.unit.is_leader():
+            self.nbi.set_host_info(self.app.name, SERVICE_PORT, event.relation if event else None)
+
     def _on_get_debug_mode_information_action(self, event: ActionEvent) -> None:
         """Handler for the get-debug-mode-information action event."""
         if not self.debug_mode.started:
     def _on_get_debug_mode_information_action(self, event: ActionEvent) -> None:
         """Handler for the get-debug-mode-information action event."""
         if not self.debug_mode.started:
@@ -171,16 +183,24 @@ class OsmNbiCharm(CharmBase):
             # Relation events
             self.on.kafka_available: self._on_config_changed,
             self.on["kafka"].relation_broken: self._on_required_relation_broken,
             # Relation events
             self.on.kafka_available: self._on_config_changed,
             self.on["kafka"].relation_broken: self._on_required_relation_broken,
+            self.mongodb_client.on.database_created: self._on_config_changed,
+            self.on["mongodb"].relation_broken: self._on_required_relation_broken,
+            self.on["keystone"].relation_changed: self._on_config_changed,
+            self.on["keystone"].relation_broken: self._on_required_relation_broken,
             # Action events
             self.on.get_debug_mode_information_action: self._on_get_debug_mode_information_action,
             # Action events
             self.on.get_debug_mode_information_action: self._on_get_debug_mode_information_action,
+            self.on.nbi_relation_joined: self._update_nbi_relation,
         }
         }
-        for relation in [self.on[rel_name] for rel_name in ["mongodb", "prometheus", "keystone"]]:
-            event_handler_mapping[relation.relation_changed] = self._on_config_changed
-            event_handler_mapping[relation.relation_broken] = self._on_required_relation_broken
 
         for event, handler in event_handler_mapping.items():
             self.framework.observe(event, handler)
 
 
         for event, handler in event_handler_mapping.items():
             self.framework.observe(event, handler)
 
+    def _is_database_available(self) -> bool:
+        try:
+            return self.mongodb_client.is_resource_created()
+        except KeyError:
+            return False
+
     def _validate_config(self) -> None:
         """Validate charm configuration.
 
     def _validate_config(self) -> None:
         """Validate charm configuration.
 
@@ -188,6 +208,14 @@ class OsmNbiCharm(CharmBase):
             CharmError: if charm configuration is invalid.
         """
         logger.debug("validating charm config")
             CharmError: if charm configuration is invalid.
         """
         logger.debug("validating charm config")
+        url = self.config.get("prometheus-url")
+        if not url:
+            raise CharmError("need prometheus-url config")
+        if not self._is_valid_url(url):
+            raise CharmError(f"Invalid value for prometheus-url config: '{url}'")
+
+    def _is_valid_url(self, url) -> bool:
+        return urlparse(url).hostname is not None
 
     def _check_relations(self) -> None:
         """Validate charm relations.
 
     def _check_relations(self) -> None:
         """Validate charm relations.
@@ -200,10 +228,8 @@ class OsmNbiCharm(CharmBase):
 
         if not self.kafka.host or not self.kafka.port:
             missing_relations.append("kafka")
 
         if not self.kafka.host or not self.kafka.port:
             missing_relations.append("kafka")
-        if self.mongodb_client.is_missing_data_in_unit():
+        if not self._is_database_available():
             missing_relations.append("mongodb")
             missing_relations.append("mongodb")
-        if self.prometheus_client.is_missing_data_in_app():
-            missing_relations.append("prometheus")
         if self.keystone_client.is_missing_data_in_app():
             missing_relations.append("keystone")
 
         if self.keystone_client.is_missing_data_in_app():
             missing_relations.append("keystone")
 
@@ -233,6 +259,7 @@ class OsmNbiCharm(CharmBase):
 
     def _get_layer(self) -> Dict[str, Any]:
         """Get layer for Pebble."""
 
     def _get_layer(self) -> Dict[str, Any]:
         """Get layer for Pebble."""
+        prometheus = urlparse(self.config["prometheus-url"])
         return {
             "summary": "nbi layer",
             "description": "pebble config layer for nbi",
         return {
             "summary": "nbi layer",
             "description": "pebble config layer for nbi",
@@ -254,16 +281,16 @@ class OsmNbiCharm(CharmBase):
                         "OSMNBI_MESSAGE_DRIVER": "kafka",
                         # Database configuration
                         "OSMNBI_DATABASE_DRIVER": "mongo",
                         "OSMNBI_MESSAGE_DRIVER": "kafka",
                         # Database configuration
                         "OSMNBI_DATABASE_DRIVER": "mongo",
-                        "OSMNBI_DATABASE_URI": self.mongodb_client.connection_string,
+                        "OSMNBI_DATABASE_URI": self._get_mongodb_uri(),
                         "OSMNBI_DATABASE_COMMONKEY": self.config["database-commonkey"],
                         # Storage configuration
                         "OSMNBI_STORAGE_DRIVER": "mongo",
                         "OSMNBI_STORAGE_PATH": "/app/storage",
                         "OSMNBI_STORAGE_COLLECTION": "files",
                         "OSMNBI_DATABASE_COMMONKEY": self.config["database-commonkey"],
                         # Storage configuration
                         "OSMNBI_STORAGE_DRIVER": "mongo",
                         "OSMNBI_STORAGE_PATH": "/app/storage",
                         "OSMNBI_STORAGE_COLLECTION": "files",
-                        "OSMNBI_STORAGE_URI": self.mongodb_client.connection_string,
+                        "OSMNBI_STORAGE_URI": self._get_mongodb_uri(),
                         # Prometheus configuration
                         # Prometheus configuration
-                        "OSMNBI_PROMETHEUS_HOST": self.prometheus_client.hostname,
-                        "OSMNBI_PROMETHEUS_PORT": self.prometheus_client.port,
+                        "OSMNBI_PROMETHEUS_HOST": prometheus.hostname,
+                        "OSMNBI_PROMETHEUS_PORT": prometheus.port if prometheus.port else "",
                         # Log configuration
                         "OSMNBI_LOG_LEVEL": self.config["log-level"],
                         # Authentication environments
                         # Log configuration
                         "OSMNBI_LOG_LEVEL": self.config["log-level"],
                         # Authentication environments
@@ -275,11 +302,19 @@ class OsmNbiCharm(CharmBase):
                         "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": self.keystone_client.username,
                         "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": self.keystone_client.password,
                         "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": self.keystone_client.service,
                         "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": self.keystone_client.username,
                         "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": self.keystone_client.password,
                         "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": self.keystone_client.service,
+                        # DISABLING INTERNAL SSL SERVER
+                        "OSMNBI_SERVER_SSL_MODULE": "",
+                        "OSMNBI_SERVER_SSL_CERTIFICATE": "",
+                        "OSMNBI_SERVER_SSL_PRIVATE_KEY": "",
+                        "OSMNBI_SERVER_SSL_PASS_PHRASE": "",
                     },
                 }
             },
         }
 
                     },
                 }
             },
         }
 
+    def _get_mongodb_uri(self):
+        return list(self.mongodb_client.fetch_relation_data().values())[0]["uris"]
+
 
 if __name__ == "__main__":  # pragma: no cover
     main(OsmNbiCharm)
 
 if __name__ == "__main__":  # pragma: no cover
     main(OsmNbiCharm)