]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
python-common: Don't valiate ServiceSpec.from_json() in `orch ls`
authorSebastian Wagner <sewagner@redhat.com>
Wed, 10 Nov 2021 14:54:42 +0000 (15:54 +0100)
committerSebastian Wagner <sewagner@redhat.com>
Wed, 1 Dec 2021 08:57:31 +0000 (09:57 +0100)
unfortunately `ceph orch ls` may return invalid OSD specs for
OSDs not associated to and specs.

Signed-off-by: Sebastian Wagner <sewagner@redhat.com>
src/pybind/mgr/orchestrator/module.py
src/pybind/mgr/orchestrator/tests/test_orchestrator.py
src/python-common/ceph/deployment/service_spec.py

index c67685a04727d1bb4b0b564351bf3f28ba85504e..353d0d78fcfdf049e2511a6d0665c7fc3ea534e2 100644 (file)
@@ -10,7 +10,7 @@ from prettytable import PrettyTable
 
 from ceph.deployment.inventory import Device
 from ceph.deployment.drive_group import DriveGroupSpec, DeviceSelection, OSDMethod
-from ceph.deployment.service_spec import PlacementSpec, ServiceSpec
+from ceph.deployment.service_spec import PlacementSpec, ServiceSpec, service_spec_allow_invalid_from_json
 from ceph.deployment.hostspec import SpecValidationError
 from ceph.utils import datetime_now
 
@@ -563,11 +563,12 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
         if len(services) == 0:
             return HandleCommandResult(stdout="No services reported")
         elif format != Format.plain:
-            if export:
-                data = [s.spec for s in services if s.deleted is None]
-                return HandleCommandResult(stdout=to_format(data, format, many=True, cls=ServiceSpec))
-            else:
-                return HandleCommandResult(stdout=to_format(services, format, many=True, cls=ServiceDescription))
+            with service_spec_allow_invalid_from_json():
+                if export:
+                    data = [s.spec for s in services if s.deleted is None]
+                    return HandleCommandResult(stdout=to_format(data, format, many=True, cls=ServiceSpec))
+                else:
+                    return HandleCommandResult(stdout=to_format(services, format, many=True, cls=ServiceDescription))
         else:
             now = datetime_now()
             table = PrettyTable(
index 5e3f2883b8aae8e4933b69aa2121e03be5ba3b78..6c847b86881720640742702193af8c0fdaceb0e6 100644 (file)
@@ -1,5 +1,6 @@
 
 import json
+import textwrap
 
 import pytest
 import yaml
@@ -156,6 +157,24 @@ def test_orch_ls(_describe_service):
           'osd              123  -          -               '
     assert r == HandleCommandResult(retval=0, stdout=out, stderr='')
 
+    cmd = {
+        'prefix': 'orch ls',
+        'format': 'yaml',
+    }
+    m = OrchestratorCli('orchestrator', 0, 0)
+    r = m._handle_command(None, cmd)
+    out = textwrap.dedent("""
+        service_type: osd
+        service_name: osd
+        spec:
+          filter_logic: AND
+          objectstore: bluestore
+        status:
+          running: 123
+          size: 0
+        """).lstrip()
+    assert r == HandleCommandResult(retval=0, stdout=out, stderr='')
+
 
 def test_preview_table_osd_smoke():
     data = [
index 0735bb6e30c1474bb8fc5aaf280996ab9f026d92..683e5b70d4b7d1720e59bf25c6d60a90e780f2b3 100644 (file)
@@ -1,10 +1,11 @@
 import fnmatch
 import re
 from collections import OrderedDict
+from contextlib import contextmanager
 from functools import wraps
 from ipaddress import ip_network, ip_address
 from typing import Optional, Dict, Any, List, Union, Callable, Iterable, Type, TypeVar, cast, \
-    NamedTuple, Mapping
+    NamedTuple, Mapping, Iterator
 
 import yaml
 
@@ -394,6 +395,22 @@ tPlacementSpec(hostname='host2', network='', name='')])
         return ps
 
 
+_service_spec_from_json_validate = True
+
+
+@contextmanager
+def service_spec_allow_invalid_from_json() -> Iterator[None]:
+    """
+    I know this is evil, but unfortunately `ceph orch ls`
+    may return invalid OSD specs for OSDs not associated to
+    and specs. If you have a better idea, please!
+    """
+    global _service_spec_from_json_validate
+    _service_spec_from_json_validate = False
+    yield
+    _service_spec_from_json_validate = True
+
+
 class ServiceSpec(object):
     """
     Details of service creation.
@@ -535,7 +552,6 @@ class ServiceSpec(object):
 
         :meta private:
         """
-
         if not isinstance(json_spec, dict):
             raise SpecValidationError(
                 f'Service Spec is not an (JSON or YAML) object. got "{str(json_spec)}"')
@@ -586,7 +602,8 @@ class ServiceSpec(object):
                 continue
             args.update({k: v})
         _cls = cls(**args)
-        _cls.validate()
+        if _service_spec_from_json_validate:
+            _cls.validate()
         return _cls
 
     def service_name(self) -> str: