X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;ds=inline;f=installers%2Fcharm%2Fro%2Fsrc%2Fcharm.py;fp=installers%2Fcharm%2Fro%2Fsrc%2Fcharm.py;h=0000000000000000000000000000000000000000;hb=e93311d0d1e614b2ee73cc88d8a04fdec217cff8;hp=028dc0a4f0c1afd6cd7758fa2c88952fa35cacab;hpb=b26337f4b399b8d2307002aa7ca98c23e2d3a1ed;p=osm%2Fdevops.git diff --git a/installers/charm/ro/src/charm.py b/installers/charm/ro/src/charm.py deleted file mode 100755 index 028dc0a4..00000000 --- a/installers/charm/ro/src/charm.py +++ /dev/null @@ -1,465 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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 -## - -# pylint: disable=E0213 - -import base64 -import logging -from typing import Dict, NoReturn, Optional - -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.mongo import MongoClient -from opslib.osm.interfaces.mysql import MysqlClient -from opslib.osm.pod import ( - ContainerV3Builder, - FilesV3Builder, - PodRestartPolicy, - PodSpecV3Builder, -) -from opslib.osm.validator import ModelValidator, validator - -logger = logging.getLogger(__name__) - -PORT = 9090 - - -def _check_certificate_data(name: str, content: str): - if not name or not content: - raise ValueError("certificate name and content must be a non-empty string") - - -def _extract_certificates(certs_config: str): - certificates = {} - if certs_config: - cert_list = certs_config.split(",") - for cert in cert_list: - name, content = cert.split(":") - _check_certificate_data(name, content) - certificates[name] = content - return certificates - - -def decode(content: str): - return base64.b64decode(content.encode("utf-8")).decode("utf-8") - - -class ConfigModel(ModelValidator): - enable_ng_ro: bool - database_commonkey: str - mongodb_uri: Optional[str] - log_level: str - mysql_host: Optional[str] - mysql_port: Optional[int] - mysql_user: Optional[str] - mysql_password: Optional[str] - mysql_root_password: Optional[str] - vim_database: str - ro_database: str - openmano_tenant: str - certificates: Optional[str] - image_pull_policy: str - debug_mode: bool - security_context: bool - period_refresh_active: Optional[int] - - @validator("log_level") - def validate_log_level(cls, v): - if v not in {"INFO", "DEBUG"}: - raise ValueError("value must be INFO or DEBUG") - return v - - @validator("certificates") - def validate_certificates(cls, v): - # Raises an exception if it cannot extract the certificates - _extract_certificates(v) - return v - - @validator("mongodb_uri") - def validate_mongodb_uri(cls, v): - if v and not v.startswith("mongodb://"): - raise ValueError("mongodb_uri is not properly formed") - return v - - @validator("mysql_port") - def validate_mysql_port(cls, v): - if v and (v <= 0 or v >= 65535): - raise ValueError("Mysql port out of range") - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - @property - def certificates_dict(cls): - return _extract_certificates(cls.certificates) if cls.certificates else {} - - @validator("period_refresh_active") - def validate_vim_refresh_period(cls, v): - if v and v < 60 and v != -1: - raise ValueError( - "Refresh Period is too tight, insert >= 60 seconds or disable using -1" - ) - return v - - -class RoCharm(CharmedOsmBase): - """GrafanaCharm Charm.""" - - on = KafkaEvents() - - def __init__(self, *args) -> NoReturn: - """Prometheus Charm constructor.""" - super().__init__( - *args, - oci_image="image", - vscode_workspace=VSCODE_WORKSPACE, - ) - if self.config.get("debug_mode"): - self.enable_debug_mode( - pubkey=self.config.get("debug_pubkey"), - hostpaths={ - "osm_common": { - "hostpath": self.config.get("debug_common_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_common", - }, - **_get_ro_host_paths(self.config.get("debug_ro_local_path")), - }, - ) - self.kafka = KafkaRequires(self) - self.framework.observe(self.on.kafka_available, self.configure_pod) - self.framework.observe(self.on.kafka_broken, self.configure_pod) - - self.mysql_client = MysqlClient(self, "mysql") - self.framework.observe(self.on["mysql"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mysql"].relation_broken, self.configure_pod) - - self.mongodb_client = MongoClient(self, "mongodb") - self.framework.observe(self.on["mongodb"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mongodb"].relation_broken, self.configure_pod) - - self.framework.observe(self.on["ro"].relation_joined, self._publish_ro_info) - - def _publish_ro_info(self, event): - """Publishes RO information. - - Args: - event (EventBase): RO relation event. - """ - if self.unit.is_leader(): - rel_data = { - "host": self.model.app.name, - "port": str(PORT), - } - for k, v in rel_data.items(): - event.relation.data[self.app][k] = v - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if config.enable_ng_ro: - if not self.kafka.host or not self.kafka.port: - missing_relations.append("kafka") - if not config.mongodb_uri and self.mongodb_client.is_missing_data_in_unit(): - missing_relations.append("mongodb") - else: - if not config.mysql_host and self.mysql_client.is_missing_data_in_unit(): - missing_relations.append("mysql") - if missing_relations: - raise RelationsMissing(missing_relations) - - def _validate_mysql_config(self, config: ConfigModel): - invalid_values = [] - if not config.mysql_user: - invalid_values.append("Mysql user is empty") - if not config.mysql_password: - invalid_values.append("Mysql password is empty") - if not config.mysql_root_password: - invalid_values.append("Mysql root password empty") - - if invalid_values: - raise ValueError("Invalid values: " + ", ".join(invalid_values)) - - def _build_cert_files( - self, - config: ConfigModel, - ): - cert_files_builder = FilesV3Builder() - for name, content in config.certificates_dict.items(): - cert_files_builder.add_file(name, decode(content), mode=0o600) - return cert_files_builder.build() - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - - if config.enable_ng_ro: - if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit(): - raise Exception( - "Mongodb data cannot be provided via config and relation" - ) - else: - if config.mysql_host and not self.mysql_client.is_missing_data_in_unit(): - raise Exception("Mysql data cannot be provided via config and relation") - - if config.mysql_host: - self._validate_mysql_config(config) - - # Check relations - self._check_missing_dependencies(config) - - security_context_enabled = ( - config.security_context if not config.debug_mode else False - ) - - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=security_context_enabled - ) - - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=security_context_enabled, - ) - certs_files = self._build_cert_files(config) - - if certs_files: - container_builder.add_volume_config("certs", "/certs", certs_files) - - container_builder.add_port(name=self.app.name, port=PORT) - container_builder.add_http_readiness_probe( - "/ro/" if config.enable_ng_ro else "/openmano/tenants", - PORT, - initial_delay_seconds=10, - period_seconds=10, - timeout_seconds=5, - failure_threshold=3, - ) - container_builder.add_http_liveness_probe( - "/ro/" if config.enable_ng_ro else "/openmano/tenants", - PORT, - initial_delay_seconds=600, - period_seconds=10, - timeout_seconds=5, - failure_threshold=3, - ) - container_builder.add_envs( - { - "OSMRO_LOG_LEVEL": config.log_level, - } - ) - if config.period_refresh_active: - container_builder.add_envs( - { - "OSMRO_PERIOD_REFRESH_ACTIVE": config.period_refresh_active, - } - ) - if config.enable_ng_ro: - # Add secrets to the pod - mongodb_secret_name = f"{self.app.name}-mongodb-secret" - pod_spec_builder.add_secret( - mongodb_secret_name, - { - "uri": config.mongodb_uri or self.mongodb_client.connection_string, - "commonkey": config.database_commonkey, - }, - ) - container_builder.add_envs( - { - "OSMRO_MESSAGE_DRIVER": "kafka", - "OSMRO_MESSAGE_HOST": self.kafka.host, - "OSMRO_MESSAGE_PORT": self.kafka.port, - # MongoDB configuration - "OSMRO_DATABASE_DRIVER": "mongo", - } - ) - container_builder.add_secret_envs( - secret_name=mongodb_secret_name, - envs={ - "OSMRO_DATABASE_URI": "uri", - "OSMRO_DATABASE_COMMONKEY": "commonkey", - }, - ) - restart_policy = PodRestartPolicy() - restart_policy.add_secrets(secret_names=(mongodb_secret_name,)) - pod_spec_builder.set_restart_policy(restart_policy) - - else: - container_builder.add_envs( - { - "RO_DB_HOST": config.mysql_host or self.mysql_client.host, - "RO_DB_OVIM_HOST": config.mysql_host or self.mysql_client.host, - "RO_DB_PORT": config.mysql_port or self.mysql_client.port, - "RO_DB_OVIM_PORT": config.mysql_port or self.mysql_client.port, - "RO_DB_USER": config.mysql_user or self.mysql_client.user, - "RO_DB_OVIM_USER": config.mysql_user or self.mysql_client.user, - "RO_DB_PASSWORD": config.mysql_password - or self.mysql_client.password, - "RO_DB_OVIM_PASSWORD": config.mysql_password - or self.mysql_client.password, - "RO_DB_ROOT_PASSWORD": config.mysql_root_password - or self.mysql_client.root_password, - "RO_DB_OVIM_ROOT_PASSWORD": config.mysql_root_password - or self.mysql_client.root_password, - "RO_DB_NAME": config.ro_database, - "RO_DB_OVIM_NAME": config.vim_database, - "OPENMANO_TENANT": config.openmano_tenant, - } - ) - container = container_builder.build() - - # Add container to pod spec - pod_spec_builder.add_container(container) - - return pod_spec_builder.build() - - -VSCODE_WORKSPACE = { - "folders": [ - {"path": "/usr/lib/python3/dist-packages/osm_ng_ro"}, - {"path": "/usr/lib/python3/dist-packages/osm_common"}, - {"path": "/usr/lib/python3/dist-packages/osm_ro_plugin"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_arista_cloudvision"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_dpb"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_dynpac"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_floodlightof"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_ietfl2vpn"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_juniper_contrail"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_odlof"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_onos_vpls"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_onosof"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_aws"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_azure"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_gcp"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_openstack"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_openvim"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_vmware"}, - ], - "launch": { - "configurations": [ - { - "module": "osm_ng_ro.ro_main", - "name": "NG RO", - "request": "launch", - "type": "python", - "justMyCode": False, - } - ], - "version": "0.2.0", - }, - "settings": {}, -} - - -def _get_ro_host_paths(ro_host_path: str) -> Dict: - """Get RO host paths""" - return ( - { - "NG-RO": { - "hostpath": f"{ro_host_path}/NG-RO", - "container-path": "/usr/lib/python3/dist-packages/osm_ng_ro", - }, - "RO-plugin": { - "hostpath": f"{ro_host_path}/RO-plugin", - "container-path": "/usr/lib/python3/dist-packages/osm_ro_plugin", - }, - "RO-SDN-arista_cloudvision": { - "hostpath": f"{ro_host_path}/RO-SDN-arista_cloudvision", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_arista_cloudvision", - }, - "RO-SDN-dpb": { - "hostpath": f"{ro_host_path}/RO-SDN-dpb", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_dpb", - }, - "RO-SDN-dynpac": { - "hostpath": f"{ro_host_path}/RO-SDN-dynpac", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_dynpac", - }, - "RO-SDN-floodlight_openflow": { - "hostpath": f"{ro_host_path}/RO-SDN-floodlight_openflow", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_floodlightof", - }, - "RO-SDN-ietfl2vpn": { - "hostpath": f"{ro_host_path}/RO-SDN-ietfl2vpn", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_ietfl2vpn", - }, - "RO-SDN-juniper_contrail": { - "hostpath": f"{ro_host_path}/RO-SDN-juniper_contrail", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_juniper_contrail", - }, - "RO-SDN-odl_openflow": { - "hostpath": f"{ro_host_path}/RO-SDN-odl_openflow", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_odlof", - }, - "RO-SDN-onos_openflow": { - "hostpath": f"{ro_host_path}/RO-SDN-onos_openflow", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_onosof", - }, - "RO-SDN-onos_vpls": { - "hostpath": f"{ro_host_path}/RO-SDN-onos_vpls", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_onos_vpls", - }, - "RO-VIM-aws": { - "hostpath": f"{ro_host_path}/RO-VIM-aws", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_aws", - }, - "RO-VIM-azure": { - "hostpath": f"{ro_host_path}/RO-VIM-azure", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_azure", - }, - "RO-VIM-gcp": { - "hostpath": f"{ro_host_path}/RO-VIM-gcp", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_gcp", - }, - "RO-VIM-openstack": { - "hostpath": f"{ro_host_path}/RO-VIM-openstack", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_openstack", - }, - "RO-VIM-openvim": { - "hostpath": f"{ro_host_path}/RO-VIM-openvim", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_openvim", - }, - "RO-VIM-vmware": { - "hostpath": f"{ro_host_path}/RO-VIM-vmware", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_vmware", - }, - } - if ro_host_path - else {} - ) - - -if __name__ == "__main__": - main(RoCharm)