]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard_v2: Added tcmu iSCSI controller.
authorSebastian Wagner <sebastian.wagner@suse.com>
Tue, 13 Feb 2018 16:22:26 +0000 (17:22 +0100)
committerRicardo Dias <rdias@suse.com>
Mon, 5 Mar 2018 13:07:11 +0000 (13:07 +0000)
* Based on `mgr/dashboard/rbd_iscsi.py`.
* Fixed Python3 compatibility issues.
* Added test.

Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
src/pybind/mgr/dashboard_v2/controllers/tcmu_iscsi.py [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/tests/test_tcmu_iscsi.py [new file with mode: 0644]

diff --git a/src/pybind/mgr/dashboard_v2/controllers/tcmu_iscsi.py b/src/pybind/mgr/dashboard_v2/controllers/tcmu_iscsi.py
new file mode 100644 (file)
index 0000000..db1c146
--- /dev/null
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+from ..tools import ApiController, AuthRequired, RESTController
+
+SERVICE_TYPE = 'tcmu-runner'
+
+
+@ApiController('tcmuiscsi')
+@AuthRequired()
+class TcmuIscsi(RESTController):
+    # pylint: disable=too-many-locals,too-many-nested-blocks
+    def list(self):  # pylint: disable=unused-argument
+        daemons = {}
+        images = {}
+        for server in self.mgr.list_servers():
+            for service in server['services']:
+                if service['type'] == SERVICE_TYPE:
+                    metadata = self.mgr.get_metadata(SERVICE_TYPE,
+                                                     service['id'])
+                    status = self.mgr.get_daemon_status(SERVICE_TYPE,
+                                                        service['id'])
+
+                    daemon = daemons.get(server['hostname'], None)
+                    if daemon is None:
+                        daemon = {
+                            'server_hostname': server['hostname'],
+                            'version': metadata['ceph_version'],
+                            'optimized_paths': 0,
+                            'non_optimized_paths': 0
+                        }
+                        daemons[server['hostname']] = daemon
+
+                    service_id = service['id']
+                    device_id = service_id.split(':')[-1]
+                    image = images.get(device_id)
+                    if image is None:
+                        image = {
+                            'device_id': device_id,
+                            'pool_name': metadata['pool_name'],
+                            'name': metadata['image_name'],
+                            'id': metadata.get('image_id', None),
+                            'optimized_paths': [],
+                            'non_optimized_paths': []
+                        }
+                        images[device_id] = image
+                    if status.get('lock_owner', 'false') == 'true':
+                        daemon['optimized_paths'] += 1
+                        image['optimized_paths'].append(server['hostname'])
+
+                        perf_key_prefix = "librbd-{id}-{pool}-{name}.".format(
+                            id=metadata.get('image_id', ''),
+                            pool=metadata['pool_name'],
+                            name=metadata['image_name'])
+                        perf_key = "{}lock_acquired_time".format(perf_key_prefix)
+                        lock_acquired_time = (self.mgr.get_counter(
+                            'tcmu-runner', service_id, perf_key)[perf_key] or
+                                              [[0, 0]])[-1][1] / 1000000000
+                        if lock_acquired_time > image.get('optimized_since', 0):
+                            image['optimized_since'] = lock_acquired_time
+                            image['stats'] = {}
+                            image['stats_history'] = {}
+                            for s in ['rd', 'wr', 'rd_bytes', 'wr_bytes']:
+                                perf_key = "{}{}".format(perf_key_prefix, s)
+                                image['stats'][s] = self.mgr.get_rate(
+                                    'tcmu-runner', service_id, perf_key)
+                                image['stats_history'][s] = self.mgr.get_counter(
+                                    'tcmu-runner', service_id, perf_key)[perf_key]
+                    else:
+                        daemon['non_optimized_paths'] += 1
+                        image['non_optimized_paths'].append(server['hostname'])
+
+        return {
+            'daemons': sorted(daemons.values(), key=lambda d: d['server_hostname']),
+            'images': sorted(images.values(), key=lambda i: ['id']),
+        }
diff --git a/src/pybind/mgr/dashboard_v2/tests/test_tcmu_iscsi.py b/src/pybind/mgr/dashboard_v2/tests/test_tcmu_iscsi.py
new file mode 100644 (file)
index 0000000..a8b48db
--- /dev/null
@@ -0,0 +1,87 @@
+from cherrypy.test.helper import CPWebCase
+import cherrypy
+import mock
+
+from ..controllers.auth import Auth
+from ..tools import SessionExpireAtBrowserCloseTool
+from ..controllers.tcmu_iscsi import TcmuIscsi
+from .helper import ControllerTestCase
+
+mocked_servers = [{
+    'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)',
+    'hostname': 'ceph-dev',
+    'services': [{'id': 'a:b', 'type': 'tcmu-runner'}]
+}]
+
+mocked_metadata = {
+    'ceph_version': 'ceph version 13.0.0-5083- () mimic (dev)',
+    'pool_name': 'pool1',
+    'image_name': 'image1',
+    'image_id': '42',
+    'optimized_since': 100.0,
+}
+
+mocked_get_daemon_status = {
+    'lock_owner': 'true',
+}
+
+mocked_get_counter = {
+    'librbd-42-pool1-image1.lock_acquired_time': [[10000.0, 10000.0]],
+    'librbd-42-pool1-image1.rd': 43,
+    'librbd-42-pool1-image1.wr': 44,
+    'librbd-42-pool1-image1.rd_bytes': 45,
+    'librbd-42-pool1-image1.wr_bytes': 46,
+}
+
+mocked_get_rate = 47
+
+
+class TcmuIscsiControllerTest(ControllerTestCase, CPWebCase):
+
+    @classmethod
+    def setup_server(cls):
+        # Initialize custom handlers.
+        cherrypy.tools.authenticate = cherrypy.Tool('before_handler', Auth.check_auth)
+        cherrypy.tools.session_expire_at_browser_close = SessionExpireAtBrowserCloseTool()
+
+        cls._mgr_module = mock.Mock()
+        cls.setup_test()
+
+    @classmethod
+    def setup_test(cls):
+        mgr_mock = mock.Mock()
+        mgr_mock.list_servers.return_value = mocked_servers
+        mgr_mock.get_metadata.return_value = mocked_metadata
+        mgr_mock.get_daemon_status.return_value = mocked_get_daemon_status
+        mgr_mock.get_counter.return_value = mocked_get_counter
+        mgr_mock.get_rate.return_value = mocked_get_rate
+        mgr_mock.url_prefix = ''
+        TcmuIscsi.mgr = mgr_mock
+        TcmuIscsi._cp_config['tools.authenticate.on'] = False  # pylint: disable=protected-access
+
+        cherrypy.tree.mount(TcmuIscsi(), "/api/test/tcmu")
+
+    def __init__(self, *args, **kwargs):
+        super(TcmuIscsiControllerTest, self).__init__(*args, dashboard_port=54583, **kwargs)
+
+    def test_list(self):
+        self._post("/api/auth", {'username': 'admin', 'password': 'admin'})
+        self._get('/api/test/tcmu')
+        self.assertStatus(200)
+        self.assertJsonBody({
+            'daemons': [{
+                'server_hostname': 'ceph-dev',
+                'version': 'ceph version 13.0.0-5083- () mimic (dev)',
+                'optimized_paths': 1, 'non_optimized_paths': 0}],
+            'images': [{
+                'device_id': 'b',
+                'pool_name': 'pool1',
+                'name': 'image1',
+                'id': '42', 'optimized_paths': ['ceph-dev'],
+                'non_optimized_paths': [],
+                'optimized_since': 1e-05,
+                'stats': {'rd': 47, 'rd_bytes': 47, 'wr': 47, 'wr_bytes': 47},
+                'stats_history': {
+                    'rd': 43, 'wr': 44, 'rd_bytes': 45, 'wr_bytes': 46}
+            }]
+        })