]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
src/python_common: Add CustomConfig class
authorAdam King <adking@redhat.com>
Mon, 27 Jun 2022 18:26:37 +0000 (14:26 -0400)
committerAdam King <adking@redhat.com>
Tue, 16 May 2023 19:15:36 +0000 (15:15 -0400)
Signed-off-by: Adam King <adking@redhat.com>
(cherry picked from commit 61a6ec0399ac33843d39c9b4bd9ca23ad72e4154)

Conflicts:
src/python-common/ceph/deployment/drive_group.py
src/python-common/ceph/deployment/service_spec.py

src/python-common/ceph/deployment/drive_group.py
src/python-common/ceph/deployment/service_spec.py

index 4b6b1099080728271f8da86f0858d4fbe7f36115..7378797c6951d15be8babb1761067a2ad36cc24e 100644 (file)
@@ -2,7 +2,7 @@ import enum
 import yaml
 
 from ceph.deployment.inventory import Device
-from ceph.deployment.service_spec import ServiceSpec, PlacementSpec
+from ceph.deployment.service_spec import ServiceSpec, PlacementSpec, CustomConfig
 from ceph.deployment.hostspec import SpecValidationError
 
 try:
@@ -179,6 +179,7 @@ class DriveGroupSpec(ServiceSpec):
                  data_allocate_fraction=None,  # type: Optional[float]
                  method=None,  # type: Optional[OSDMethod]
                  crush_device_class=None,  # type: Optional[str]
+                 custom_configs=None,  # type: Optional[List[CustomConfig]]
                  ):
         assert service_type is None or service_type == 'osd'
         super(DriveGroupSpec, self).__init__('osd', service_id=service_id,
@@ -186,7 +187,8 @@ class DriveGroupSpec(ServiceSpec):
                                              unmanaged=unmanaged,
                                              preview_only=preview_only,
                                              extra_container_args=extra_container_args,
-                                             extra_entrypoint_args=extra_entrypoint_args)
+                                             extra_entrypoint_args=extra_entrypoint_args,
+                                             custom_configs=custom_configs)
 
         #: A :class:`ceph.deployment.drive_group.DeviceSelection`
         self.data_devices = data_devices
index 47d61616f33ecfb93f013c251a1fc16979d4e7f1..8cd3ef0f0364f52ed031a0028f3dc01f2c125789 100644 (file)
@@ -1,4 +1,5 @@
 import fnmatch
+import os
 import re
 import enum
 from collections import OrderedDict
@@ -420,6 +421,58 @@ tPlacementSpec(hostname='host2', network='', name='')])
 _service_spec_from_json_validate = True
 
 
+class CustomConfig:
+    """
+    Class to specify custom config files to be mounted in daemon's container
+    """
+
+    _fields = ['content', 'mount_path']
+
+    def __init__(self, content: str, mount_path: str) -> None:
+        self.content: str = content
+        self.mount_path: str = mount_path
+        self.validate()
+
+    def to_json(self) -> Dict[str, Any]:
+        return {
+            'content': self.content,
+            'mount_path': self.mount_path,
+        }
+
+    @classmethod
+    def from_json(cls, data: Dict[str, Any]) -> "CustomConfig":
+        for k in cls._fields:
+            if k not in data:
+                raise SpecValidationError(f'CustomConfig must have "{k}" field')
+        for k in data.keys():
+            if k not in cls._fields:
+                raise SpecValidationError(f'CustomConfig got unknown field "{k}"')
+        return cls(**data)
+
+    @property
+    def filename(self) -> str:
+        return os.path.basename(self.mount_path)
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, CustomConfig):
+            return (
+                self.content == other.content
+                and self.mount_path == other.mount_path
+            )
+        return NotImplemented
+
+    def __repr__(self) -> str:
+        return f'CustomConfig({self.mount_path})'
+
+    def validate(self) -> None:
+        if not isinstance(self.content, str):
+            raise SpecValidationError(
+                    f'CustomConfig content must be a string. Got {type(self.content)}')
+        if not isinstance(self.mount_path, str):
+            raise SpecValidationError(
+                    f'CustomConfig content must be a string. Got {type(self.mount_path)}')
+
+
 @contextmanager
 def service_spec_allow_invalid_from_json() -> Iterator[None]:
     """
@@ -499,6 +552,7 @@ class ServiceSpec(object):
                  networks: Optional[List[str]] = None,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
 
         #: See :ref:`orchestrator-cli-placement-spec`.
@@ -539,6 +593,7 @@ class ServiceSpec(object):
 
         self.extra_container_args: Optional[List[str]] = extra_container_args
         self.extra_entrypoint_args: Optional[List[str]] = extra_entrypoint_args
+        self.custom_configs: Optional[List[CustomConfig]] = custom_configs
 
     @classmethod
     @handle_type_error
@@ -626,6 +681,8 @@ class ServiceSpec(object):
         for k, v in json_spec.items():
             if k == 'placement':
                 v = PlacementSpec.from_json(v)
+            if k == 'custom_configs':
+                v = [CustomConfig.from_json(c) for c in v]
             if k == 'spec':
                 args.update(v)
                 continue
@@ -666,6 +723,8 @@ class ServiceSpec(object):
             ret['extra_container_args'] = self.extra_container_args
         if self.extra_entrypoint_args:
             ret['extra_entrypoint_args'] = self.extra_entrypoint_args
+        if self.custom_configs:
+            ret['custom_configs'] = [c.to_json() for c in self.custom_configs]
 
         c = {}
         for key, val in sorted(self.__dict__.items(), key=lambda tpl: tpl[0]):
@@ -743,13 +802,14 @@ class NFSServiceSpec(ServiceSpec):
                  port: Optional[int] = None,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'nfs'
         super(NFSServiceSpec, self).__init__(
             'nfs', service_id=service_id,
             placement=placement, unmanaged=unmanaged, preview_only=preview_only,
             config=config, networks=networks, extra_container_args=extra_container_args,
-            extra_entrypoint_args=extra_entrypoint_args)
+            extra_entrypoint_args=extra_entrypoint_args, custom_configs=custom_configs)
 
         self.port = port
 
@@ -808,6 +868,7 @@ class RGWSpec(ServiceSpec):
                  subcluster: Optional[str] = None,  # legacy, only for from_json on upgrade
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'rgw', service_type
 
@@ -819,7 +880,8 @@ class RGWSpec(ServiceSpec):
             'rgw', service_id=service_id,
             placement=placement, unmanaged=unmanaged,
             preview_only=preview_only, config=config, networks=networks,
-            extra_container_args=extra_container_args, extra_entrypoint_args=extra_entrypoint_args)
+            extra_container_args=extra_container_args, extra_entrypoint_args=extra_entrypoint_args,
+            custom_configs=custom_configs)
 
         #: The RGW realm associated with this service. Needs to be manually created
         self.rgw_realm: Optional[str] = rgw_realm
@@ -878,6 +940,7 @@ class IscsiServiceSpec(ServiceSpec):
                  networks: Optional[List[str]] = None,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'iscsi'
         super(IscsiServiceSpec, self).__init__('iscsi', service_id=service_id,
@@ -885,7 +948,8 @@ class IscsiServiceSpec(ServiceSpec):
                                                preview_only=preview_only,
                                                config=config, networks=networks,
                                                extra_container_args=extra_container_args,
-                                               extra_entrypoint_args=extra_entrypoint_args)
+                                               extra_entrypoint_args=extra_entrypoint_args,
+                                               custom_configs=custom_configs)
 
         #: RADOS pool where ceph-iscsi config data is stored.
         self.pool = pool
@@ -953,6 +1017,7 @@ class IngressSpec(ServiceSpec):
                  ssl: bool = False,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'ingress'
 
@@ -961,7 +1026,8 @@ class IngressSpec(ServiceSpec):
             placement=placement, config=config,
             networks=networks,
             extra_container_args=extra_container_args,
-            extra_entrypoint_args=extra_entrypoint_args
+            extra_entrypoint_args=extra_entrypoint_args,
+            custom_configs=custom_configs
         )
         self.backend_service = backend_service
         self.frontend_port = frontend_port
@@ -1090,6 +1156,7 @@ class MonitoringSpec(ServiceSpec):
                  port: Optional[int] = None,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type in ['grafana', 'node-exporter', 'prometheus', 'alertmanager']
 
@@ -1098,7 +1165,8 @@ class MonitoringSpec(ServiceSpec):
             placement=placement, unmanaged=unmanaged,
             preview_only=preview_only, config=config,
             networks=networks, extra_container_args=extra_container_args,
-            extra_entrypoint_args=extra_entrypoint_args)
+            extra_entrypoint_args=extra_entrypoint_args,
+            custom_configs=custom_configs)
 
         self.service_type = service_type
         self.port = port
@@ -1133,13 +1201,15 @@ class AlertManagerSpec(MonitoringSpec):
                  secure: bool = False,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'alertmanager'
         super(AlertManagerSpec, self).__init__(
             'alertmanager', service_id=service_id,
             placement=placement, unmanaged=unmanaged,
             preview_only=preview_only, config=config, networks=networks, port=port,
-            extra_container_args=extra_container_args, extra_entrypoint_args=extra_entrypoint_args)
+            extra_container_args=extra_container_args, extra_entrypoint_args=extra_entrypoint_args,
+            custom_configs=custom_configs)
 
         # Custom configuration.
         #
@@ -1185,13 +1255,15 @@ class GrafanaSpec(MonitoringSpec):
                  initial_admin_password: Optional[str] = None,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'grafana'
         super(GrafanaSpec, self).__init__(
             'grafana', service_id=service_id,
             placement=placement, unmanaged=unmanaged,
             preview_only=preview_only, config=config, networks=networks, port=port,
-            extra_container_args=extra_container_args, extra_entrypoint_args=extra_entrypoint_args)
+            extra_container_args=extra_container_args, extra_entrypoint_args=extra_entrypoint_args,
+            custom_configs=custom_configs)
 
         self.initial_admin_password = initial_admin_password
 
@@ -1267,6 +1339,7 @@ class SNMPGatewaySpec(ServiceSpec):
                  port: Optional[int] = None,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'snmp-gateway'
 
@@ -1276,7 +1349,8 @@ class SNMPGatewaySpec(ServiceSpec):
             unmanaged=unmanaged,
             preview_only=preview_only,
             extra_container_args=extra_container_args,
-            extra_entrypoint_args=extra_entrypoint_args)
+            extra_entrypoint_args=extra_entrypoint_args,
+            custom_configs=custom_configs)
 
         self.service_type = service_type
         self.snmp_version = snmp_version
@@ -1388,6 +1462,7 @@ class MDSSpec(ServiceSpec):
                  preview_only: bool = False,
                  extra_container_args: Optional[List[str]] = None,
                  extra_entrypoint_args: Optional[List[str]] = None,
+                 custom_configs: Optional[List[CustomConfig]] = None,
                  ):
         assert service_type == 'mds'
         super(MDSSpec, self).__init__('mds', service_id=service_id,
@@ -1396,7 +1471,8 @@ class MDSSpec(ServiceSpec):
                                       unmanaged=unmanaged,
                                       preview_only=preview_only,
                                       extra_container_args=extra_container_args,
-                                      extra_entrypoint_args=extra_entrypoint_args)
+                                      extra_entrypoint_args=extra_entrypoint_args,
+                                      custom_configs=custom_configs)
 
     def validate(self) -> None:
         super(MDSSpec, self).validate()