]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
python-common: multi-line for yaml 68579/head
authorTimothy Q Nguyen <timqn22@gmail.com>
Thu, 26 Mar 2026 19:16:51 +0000 (12:16 -0700)
committerTimothy Q Nguyen <timqn22@gmail.com>
Thu, 23 Apr 2026 21:51:03 +0000 (14:51 -0700)
Currently when running the command ceph orch ls --export
multi-line strings are processed using default interpretation
meaning they could be printed with quotations instead of
the expected | . This causes a hassle for users who want to
copy their specifications to new yaml files as they have to
manually modify the yaml to remove the quotes and add | .
My code fixes this by modifying the yaml representer to
check for multi-line strings and wrapping them in a
YamlLiteralString class which will mark the multi-line string
to be processed with | . I've also added unit tests.

Signed-off-by: Timothy Q Nguyen <timqn22@gmail.com>
(cherry picked from commit 0a73f686c8734e6545fef48275af56502300a4e7)

src/python-common/ceph/deployment/service_spec.py
src/python-common/ceph/tests/test_service_spec.py

index 98dc900543372645a7e236b5a8def82cc999246a..0ac8833953b0ed76565df588b0f8090db675d660 100644 (file)
@@ -52,6 +52,19 @@ def handle_type_error(method: FuncT) -> FuncT:
     return cast(FuncT, inner)
 
 
+class YamlLiteralString(str):
+    """
+    Class used as marker for yaml representer to properly format
+    multi-line strings for yaml export
+    """
+    @staticmethod
+    def represent_as_literal(dumper: 'yaml.SafeDumper', data: str) -> Any:
+        return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
+
+
+yaml.add_representer(YamlLiteralString, YamlLiteralString.represent_as_literal)
+
+
 class HostPlacementSpec(NamedTuple):
     hostname: str
     network: str
@@ -1132,7 +1145,15 @@ class ServiceSpec(object):
 
     @staticmethod
     def yaml_representer(dumper: 'yaml.SafeDumper', data: 'ServiceSpec') -> Any:
-        return dumper.represent_dict(cast(Mapping, data.to_json().items()))
+        json_data = data.to_json()
+        spec = json_data.get('spec', {})
+
+        # Marking multi-line strings with the YamlLiteralString class
+        for key, value in spec.items():
+            if isinstance(value, str) and '\n' in value:
+                spec[key] = YamlLiteralString(value)
+
+        return dumper.represent_dict(cast(Mapping, json_data.items()))
 
 
 yaml.add_representer(ServiceSpec, ServiceSpec.yaml_representer)
index 0c5cd313013c6ec5b62914866d97141f7a6ad585..5332e60d023210c0400c70837977e166140160fa 100644 (file)
@@ -18,6 +18,7 @@ from ceph.deployment.service_spec import (
     PrometheusSpec,
     RGWSpec,
     ServiceSpec,
+    YamlLiteralString,
 )
 from ceph.deployment.drive_group import DriveGroupSpec
 from ceph.deployment.hostspec import SpecValidationError
@@ -1296,3 +1297,39 @@ def test_extra_args_handling(y, ec_args, ee_args, ec_final_args, ee_final_args):
         for args in spec_obj.extra_entrypoint_args:
             ee_res.extend(args.to_args())
         assert ee_res == ee_final_args
+
+
+def test_yaml_literal_string_class_represents_multiline_strings_as_literal():
+    multiline_string = "test1\ntest2\n"
+    dumped = yaml.dump(YamlLiteralString(multiline_string))
+
+    assert dumped.startswith('|')
+
+
+def test_yaml_representer_can_handle_multiline_strings_for_export():
+    spec_data = """
+service_type: iscsi
+service_id: iscsi
+placement:
+  label: iscsi
+spec:
+  pool: testpool
+  ssl_cert: |
+    -----BEGIN CERTIFICATE-----
+    FILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLER
+    FILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLER
+    -----END CERTIFICATE-----
+  ssl_key: |
+    -----BEGIN PRIVATE KEY-----
+    FILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLER
+    FILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLERFILLER
+    -----END PRIVATE KEY-----
+"""
+
+    data = yaml.safe_load(spec_data)
+    spec_obj = ServiceSpec.from_json(data)
+
+    dumped = yaml.dump(spec_obj, default_flow_style=False)
+
+    assert 'ssl_cert: |' in dumped
+    assert 'ssl_key: |' in dumped