]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr: add Orchestrator.available method + CLI 23412/head
authorJohn Spray <john.spray@redhat.com>
Mon, 6 Aug 2018 15:20:39 +0000 (16:20 +0100)
committerJohn Spray <john.spray@redhat.com>
Mon, 6 Aug 2018 15:22:30 +0000 (16:22 +0100)
The CLI `status` method tells you what backend
is selected, and also gives the backend
an opportunity to complain if something
seems wrong with its configuration.

Signed-off-by: John Spray <john.spray@redhat.com>
doc/mgr/orchestrator_cli.rst
doc/mgr/orchestrator_modules.rst
src/pybind/mgr/orchestrator.py
src/pybind/mgr/orchestrator_cli/module.py
src/pybind/mgr/rook/module.py

index 5b4e4de497fe0b58c9af58c44a01443985d0aaf7..67ff07269916371088183f307e99b9a4eb7a0858 100644 (file)
@@ -26,6 +26,13 @@ For example, to enable the Rook orchestrator module and use it with the CLI:
     ceph orchestrator set backend rook
 
 
+You can then check backend is properly configured:
+
+::
+
+    ceph orchestrator status
+
+
 Usage
 =====
 
index d417e573c647249bea45f3ef5ba4328ef61ee780..93d4844a5c898747b25db30815497c9e32a58efb 100644 (file)
@@ -156,3 +156,9 @@ Upgrades
 .. automethod:: Orchestrator.upgrade_status
 .. autoclass:: UpgradeSpec
 .. autoclass:: UpgradeStatusSpec
+
+Utility
+-------
+
+.. automethod:: Orchestrator.available
+
index 38033a83203259f2dcbad6579608f7652aa22b74..0660f05e970e04fe1e6adc731dc4709c79ab2d49 100644 (file)
@@ -106,9 +106,31 @@ class Orchestrator(object):
         """
         Enable other modules to interrogate this module to discover
         whether it's usable as an orchestrator module.
+
+        Subclasses do not need to override this.
         """
         return True
 
+    def available(self):
+        """
+        Report whether we can talk to the orchestrator.  This is the
+        place to give the user a meaningful message if the orchestrator
+        isn't running or can't be contacted.
+
+        This method may be called frequently (e.g. every page load
+        to conditionally display a warning banner), so make sure it's
+        not too expensive.  It's okay to give a slightly stale status
+        (e.g. based on a periodic background ping of the orchestrator)
+        if that's necessary to make this method fast.
+
+        Do not override this method if you don't have a meaningful
+        status to return: the default None, None return value is used
+        to indicate that a module is unable to indicate its availability.
+
+        @return two-tuple of boolean, string
+        """
+        return None, None
+
     def wait(self, completions):
         """
         Given a list of Completion instances, progress any which are
index 5e1bcf015f7b469479f0a6067b09b888193f1407..dffe367a938aaf3558f576f24f1069ed23f41316 100644 (file)
@@ -40,6 +40,11 @@ class OrchestratorCli(MgrModule):
             "desc": "Select orchestrator module backend",
             "perm": "rw"
         },
+        {
+            "cmd": "orchestrator status",
+            "desc": "Report configured backend and its status",
+            "perm": "r"
+        }
     ]
 
     def _select_orchestrator(self):
@@ -222,6 +227,24 @@ class OrchestratorCli(MgrModule):
             module_name
         )
 
+    def _status(self):
+        try:
+            avail, why = self._oremote("available")
+        except NoOrchestrator:
+            return 0, "No orchestrator configured (try " \
+                      "`ceph orchestrator set backend`)", ""
+
+        if avail is None:
+            # The module does not report its availability
+            return 0, "Backend: {0}".format(
+                self._select_orchestrator()), ""
+        else:
+            return 0, "Backend: {0}\nAvailable: {1}{2}".format(
+                self._select_orchestrator(),
+                avail,
+                " ({0})".format(why) if not avail else ""
+            ), ""
+
     def handle_command(self, inbuf, cmd):
         try:
             return self._handle_command(inbuf, cmd)
@@ -239,7 +262,9 @@ class OrchestratorCli(MgrModule):
             return self._service_status(cmd)
         elif cmd['prefix'] == "orchestrator service add":
             return self._service_add(cmd)
-        if cmd['prefix'] == "orchestrator set backend":
+        elif cmd['prefix'] == "orchestrator set backend":
             return self._set_backend(cmd)
+        elif cmd['prefix'] == "orchestrator status":
+            return self._status()
         else:
             raise NotImplementedError()
index 0120e916af140f820c549c1884969d53066ec3d8..33dfe7cc3eaa0c6e39912f86cf5a77af8fe02afb 100644 (file)
@@ -9,6 +9,7 @@ import orchestrator
 
 try:
     from kubernetes import client, config
+    from kubernetes.client.rest import ApiException
 
     kubernetes_imported = True
 except ImportError:
@@ -180,7 +181,20 @@ class RookOrchestrator(MgrModule, orchestrator.Orchestrator):
         if kubernetes_imported:
             return True, ""
         else:
-            return False, "kubernetes module not found"
+            return False, "Kubernetes module not found"
+
+    def available(self):
+        if not kubernetes_imported:
+            return False, "Kubernetes module not found"
+        elif not self._in_cluster():
+            return False, "ceph-mgr not running in Rook cluster"
+
+        try:
+            self.k8s.list_namespaced_pod(self.rook_cluster.cluster_name)
+        except ApiException:
+            return False, "Cannot reach Kubernetes API"
+        else:
+            return True, ""
 
     def __init__(self, *args, **kwargs):
         super(RookOrchestrator, self).__init__(*args, **kwargs)
@@ -204,12 +218,21 @@ class RookOrchestrator(MgrModule, orchestrator.Orchestrator):
         self._initialized.wait()
         return self._rook_cluster
 
+    def _in_cluster(self):
+        """
+        Check if we appear to be running inside a Kubernetes/Rook
+        cluster
+
+        :return: bool
+        """
+        return 'ROOK_CLUSTER_NAME' in os.environ
+
     def serve(self):
         # For deployed clusters, we should always be running inside
         # a Rook cluster.  For development convenience, also support
         # running outside (reading ~/.kube config)
-        in_cluster = 'ROOK_CLUSTER_NAME' in os.environ
-        if in_cluster:
+
+        if self._in_cluster():
             config.load_incluster_config()
             cluster_name = os.environ['ROOK_CLUSTER_NAME']
         else: