]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/rook: translate placement spec to node selector and vice versa
authorJoseph Sawaya <jsawaya@redhat.com>
Fri, 3 Sep 2021 14:24:24 +0000 (10:24 -0400)
committerJoseph Sawaya <jsawaya@redhat.com>
Mon, 13 Sep 2021 15:00:07 +0000 (11:00 -0400)
This commit creates methods for translating PlacementSpecs to NodeSelectors
and vice versa, this is a general method that would be used by orch commands that
have a placement option. It's able to translate specs that specify a label, hosts
and the host_pattern '*'.

Signed-off-by: Joseph Sawaya <jsawaya@redhat.com>
src/pybind/mgr/rook/rook_cluster.py
src/pybind/mgr/rook/tests/test_placement.py [new file with mode: 0644]

index c0386c849d77a8f8ae9f9e431a8463be4776793b..f5cecfa52bcb24fd2c75416e98412443dbbc7fd1 100644 (file)
@@ -23,7 +23,7 @@ from urllib3.exceptions import ProtocolError
 
 from ceph.deployment.inventory import Device
 from ceph.deployment.drive_group import DriveGroupSpec
-from ceph.deployment.service_spec import ServiceSpec, NFSServiceSpec, RGWSpec
+from ceph.deployment.service_spec import ServiceSpec, NFSServiceSpec, RGWSpec, PlacementSpec, HostPlacementSpec
 from ceph.utils import datetime_now
 from ceph.deployment.drive_selection.matchers import SizeMatcher
 from mgr_module import NFS_POOL_NAME
@@ -1264,3 +1264,45 @@ class RookCluster(object):
     def blink_light(self, ident_fault, on, locs):
         # type: (str, bool, List[orchestrator.DeviceLightLoc]) -> List[str]
         return [self._execute_blight_job(ident_fault, on, loc) for loc in locs]
+
+def placement_spec_to_node_selector(spec: PlacementSpec, all_hosts: List) -> ccl.NodeSelectorTermsItem:
+    all_hostnames = [hs.hostname for hs in all_hosts]
+    res = ccl.NodeSelectorTermsItem(matchExpressions=ccl.MatchExpressionsList())
+    if spec.host_pattern and spec.host_pattern != "*":
+        raise RuntimeError("The Rook orchestrator only supports a host_pattern of * for placements")
+    if spec.label:
+        res.matchExpressions.append(
+            ccl.MatchExpressionsItem(
+                key="ceph-label/" + spec.label,
+                operator="Exists"
+            )
+        )
+    if spec.hosts:
+        host_list = [h.hostname for h in spec.hosts if h.hostname in all_hostnames]
+        res.matchExpressions.append(
+            ccl.MatchExpressionsItem(
+                key="kubernetes.io/hostname",
+                operator="In",
+                values=ccl.CrdObjectList(host_list)
+            )
+        ) 
+    if spec.host_pattern == "*":
+        res.matchExpressions.append(
+            ccl.MatchExpressionsItem(
+                key="kubernetes.io/hostname",
+                operator="Exists",
+            )
+        )
+    return res
+    
+def node_selector_to_placement_spec(node_selector: ccl.NodeSelectorTermsItem) -> PlacementSpec:
+    res = PlacementSpec()
+    for expression in node_selector.matchExpressions:
+        if expression.key.startswith("ceph-label/"):
+            res.label = expression.key.split('/')[1]
+        elif expression.key == "kubernetes.io/hostname":
+            if expression.operator == "Exists":
+                res.host_pattern = "*"
+            elif expression.operator == "In": 
+                res.hosts = [HostPlacementSpec(hostname=value, network='', name='')for value in expression.values]
+    return res
diff --git a/src/pybind/mgr/rook/tests/test_placement.py b/src/pybind/mgr/rook/tests/test_placement.py
new file mode 100644 (file)
index 0000000..eeaf191
--- /dev/null
@@ -0,0 +1,100 @@
+# flake8: noqa
+
+from rook.rook_cluster import placement_spec_to_node_selector, node_selector_to_placement_spec
+from rook.rook_client.ceph.cephcluster import MatchExpressionsItem, MatchExpressionsList, NodeSelectorTermsItem
+import pytest
+from orchestrator import HostSpec
+from ceph.deployment.service_spec import PlacementSpec
+
+@pytest.mark.parametrize("hosts",
+    [   # noqa: E128
+        [
+            HostSpec(
+                hostname="node1",
+                labels=["label1"]
+            ),
+            HostSpec(
+                hostname="node2",
+                labels=[]
+            ),
+            HostSpec(
+                hostname="node3",
+                labels=["label1"]
+            )
+        ]
+    ])
+@pytest.mark.parametrize("expected_placement_spec, expected_node_selector",
+    [   # noqa: E128
+        (
+            PlacementSpec(
+                label="label1"
+            ), 
+            NodeSelectorTermsItem(
+                matchExpressions=MatchExpressionsList(
+                    [
+                        MatchExpressionsItem(
+                            key="ceph-label/label1",
+                            operator="Exists"
+                        )
+                    ]
+                )
+            )
+        ),
+        (
+            PlacementSpec(
+                label="label1",
+                host_pattern="*"
+            ), 
+            NodeSelectorTermsItem(
+                matchExpressions=MatchExpressionsList(
+                    [
+                        MatchExpressionsItem(
+                            key="ceph-label/label1",
+                            operator="Exists"
+                        ),
+                        MatchExpressionsItem(
+                            key="kubernetes.io/hostname",
+                            operator="Exists",
+                        )
+                    ]
+                )
+            )
+        ),
+        (
+            PlacementSpec(
+                host_pattern="*"
+            ), 
+            NodeSelectorTermsItem(
+                matchExpressions=MatchExpressionsList(
+                    [
+                        MatchExpressionsItem(
+                            key="kubernetes.io/hostname",
+                            operator="Exists",
+                        )
+                    ]
+                )
+            )
+        ),
+        (
+            PlacementSpec(
+                hosts=["node1", "node2", "node3"]
+            ), 
+            NodeSelectorTermsItem(
+                matchExpressions=MatchExpressionsList(
+                    [
+                        MatchExpressionsItem(
+                            key="kubernetes.io/hostname",
+                            operator="In",
+                            values=["node1", "node2", "node3"]
+                        )
+                    ]
+                )
+            )
+        ),
+    ])
+def test_placement_spec_translate(hosts, expected_placement_spec, expected_node_selector):
+    node_selector = placement_spec_to_node_selector(expected_placement_spec, hosts)
+    assert [(getattr(expression, 'key', None), getattr(expression, 'operator', None), getattr(expression, 'values', None)) for expression in node_selector.matchExpressions] == [(getattr(expression, 'key', None), getattr(expression, 'operator', None), getattr(expression, 'values', None)) for expression in expected_node_selector.matchExpressions]
+    placement_spec = node_selector_to_placement_spec(expected_node_selector)
+    assert placement_spec == expected_placement_spec
+    assert (getattr(placement_spec, 'label', None), getattr(placement_spec, 'hosts', None), getattr(placement_spec, 'host_pattern', None)) == (getattr(expected_placement_spec, 'label', None), getattr(expected_placement_spec, 'hosts', None), getattr(expected_placement_spec, 'host_pattern', None))