]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard_v2: removed mock classes, unit tests now run against real cluster
authorRicardo Dias <rdias@suse.com>
Thu, 8 Feb 2018 08:24:01 +0000 (08:24 +0000)
committerRicardo Dias <rdias@suse.com>
Mon, 5 Mar 2018 13:07:07 +0000 (13:07 +0000)
We changed tox.ini to run a vstart.sh cluster and run the unit tests
against that cluster. All unit tests code was adapted to make requests
to the real dashboard_v2 backend using the `requests` python library

Signed-off-by: Ricardo Dias <rdias@suse.com>
17 files changed:
src/pybind/mgr/dashboard_v2/.gitignore
src/pybind/mgr/dashboard_v2/__init__.py
src/pybind/mgr/dashboard_v2/cephmock/__init__.py [deleted file]
src/pybind/mgr/dashboard_v2/cephmock/ceph_module_mock.py [deleted file]
src/pybind/mgr/dashboard_v2/cephmock/rados_mock.py [deleted file]
src/pybind/mgr/dashboard_v2/cephmock/rbd_mock.py [deleted file]
src/pybind/mgr/dashboard_v2/controllers/ping.py [deleted file]
src/pybind/mgr/dashboard_v2/module.py
src/pybind/mgr/dashboard_v2/requirements.txt
src/pybind/mgr/dashboard_v2/tests/helper.py
src/pybind/mgr/dashboard_v2/tests/test_auth.py
src/pybind/mgr/dashboard_v2/tests/test_host.py [new file with mode: 0644]
src/pybind/mgr/dashboard_v2/tests/test_module.py [deleted file]
src/pybind/mgr/dashboard_v2/tests/test_ping.py [deleted file]
src/pybind/mgr/dashboard_v2/tests/test_rgw.py
src/pybind/mgr/dashboard_v2/tests/test_tools.py
src/pybind/mgr/dashboard_v2/tox.ini

index 15922e4b09fd3492ef5041b6fe27a2215ddc4662..16815d17d1b507f5c67db784093a058ee5d68058 100644 (file)
@@ -5,6 +5,7 @@ coverage.xml
 junit*xml
 __pycache__
 .cache
+ceph.conf
 
 # IDE
 .vscode
index 51e53764ee80104775c8277886375d6a1ef63e1a..248abaabc83893431914590e5f3f92c6c07f647f 100644 (file)
@@ -6,8 +6,8 @@ from __future__ import absolute_import
 
 import os
 
-if 'UNITTEST' not in os.environ:
 
+if 'UNITTEST' not in os.environ:
     class _LoggerProxy(object):
         def __init__(self):
             self.logger = None
@@ -18,18 +18,10 @@ if 'UNITTEST' not in os.environ:
             return getattr(self.logger, item)
 
     logger = _LoggerProxy()
-
-    # pylint: disable=W0403,W0401
-
+    # pylint: disable=wildcard-import, wrong-import-position
     from .module import *  # NOQA
 else:
     import logging
-    import sys
-    # pylint: disable=W0403
-    from .cephmock import ceph_module_mock, rados_mock, rbd_mock
-    sys.modules['ceph_module'] = ceph_module_mock
-    sys.modules['rados'] = rados_mock
-    sys.modules['rbd'] = rbd_mock
-    logging.basicConfig(level=logging.WARNING)
+    logging.basicConfig(level=logging.DEBUG)
     logger = logging.getLogger(__name__)
-    logging.root.handlers[0].setLevel(logging.WARNING)
+    logging.root.handlers[0].setLevel(logging.DEBUG)
diff --git a/src/pybind/mgr/dashboard_v2/cephmock/__init__.py b/src/pybind/mgr/dashboard_v2/cephmock/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard_v2/cephmock/ceph_module_mock.py b/src/pybind/mgr/dashboard_v2/cephmock/ceph_module_mock.py
deleted file mode 100644 (file)
index edb3c34..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-
-class BasePyOSDMap(object):
-    pass
-
-
-class BasePyOSDMapIncremental(object):
-    pass
-
-
-class BasePyCRUSH(object):
-    pass
-
-
-class BaseMgrStandbyModule(object):
-    pass
-
-
-class BaseMgrModule(object):
-    # pylint: disable=W0613
-    def __init__(self, py_modules_ptr, this_ptr):
-        self.config_key_map = {}
-
-    def _ceph_get_version(self):
-        return 'ceph-13.0.0'
-
-    def _ceph_get_mgr_id(self):
-        return 'x'
-
-    def _ceph_set_config(self, key, value):
-        self.config_key_map[key] = value
-
-    def _ceph_get_config(self, key):
-        return self.config_key_map.get(key, None)
-
-    def _ceph_log(self, *args):
-        pass
-
-    def _ceph_get_context(self):
-        return None
diff --git a/src/pybind/mgr/dashboard_v2/cephmock/rados_mock.py b/src/pybind/mgr/dashboard_v2/cephmock/rados_mock.py
deleted file mode 100644 (file)
index 3e51f40..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-
-
-class Rados(object):
-    def __init__(self, *args, **kwargs):
-        pass
-
-    def connect(self):
-        pass
-
-    def open_ioctx(self, pool_name):
-        pass
diff --git a/src/pybind/mgr/dashboard_v2/cephmock/rbd_mock.py b/src/pybind/mgr/dashboard_v2/cephmock/rbd_mock.py
deleted file mode 100644 (file)
index 3b433ed..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-
-
-RBD_FEATURE_LAYERING = "RBD_FEATURE_LAYERING"
-RBD_FEATURE_STRIPINGV2 = "RBD_FEATURE_STRIPINGV2"
-RBD_FEATURE_EXCLUSIVE_LOCK = "RBD_FEATURE_EXCLUSIVE_LOCK"
-RBD_FEATURE_OBJECT_MAP = "RBD_FEATURE_OBJECT_MAP"
-RBD_FEATURE_FAST_DIFF = "RBD_FEATURE_FAST_DIFF"
-RBD_FEATURE_DEEP_FLATTEN = "RBD_FEATURE_DEEP_FLATTEN"
-RBD_FEATURE_JOURNALING = "RBD_FEATURE_JOURNALING"
-RBD_FEATURE_DATA_POOL = "RBD_FEATURE_DATA_POOL"
-RBD_FEATURE_OPERATIONS = "RBD_FEATURE_OPERATIONS"
-
-
-class RBD(object):
-    def __init__(self, *args, **kwargs):
-        pass
-
-    def list(self, ioctx):
-        pass
-
-
-class Image(object):
-    def __init__(self, *args, **kwargs):
-        pass
-
-    def stat(self):
-        pass
-
-    def features(self):
-        pass
diff --git a/src/pybind/mgr/dashboard_v2/controllers/ping.py b/src/pybind/mgr/dashboard_v2/controllers/ping.py
deleted file mode 100644 (file)
index d4a419a..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-
-import cherrypy
-
-from ..tools import ApiController, AuthRequired, RESTController, BaseController
-
-
-@ApiController('ping')
-@AuthRequired()
-class Ping(BaseController):
-    @cherrypy.expose
-    def default(self):
-        return 'pong'
-
-
-@ApiController('echo1')
-class EchoArgs(RESTController):
-    @RESTController.args_from_json
-    def create(self, msg):
-        return {'echo': msg}
-
-
-@ApiController('echo2')
-class Echo(RESTController):
-    def create(self, data):
-        return {'echo': data['msg']}
index 37cd99323a40d46c299efe0ec907f364fa289bb1..6c48e37cb65155e9d2eec0b8994e0dcb7eb1b38a 100644 (file)
@@ -48,6 +48,12 @@ class Module(MgrModule):
                    'name=password,type=CephString',
             'desc': 'Set the login credentials',
             'perm': 'w'
+        },
+        {
+            'cmd': 'dashboard set-session-expire '
+                   'name=seconds,type=CephInt',
+            'desc': 'Set the session expire timeout',
+            'perm': 'w'
         }
     ]
 
@@ -60,7 +66,7 @@ class Module(MgrModule):
         logger.logger = self._logger
         self._url_prefix = ''
 
-    def configure_cherrypy(self, in_unittest=False):
+    def configure_cherrypy(self):
         server_addr = self.get_localized_config('server_addr', '::')
         server_port = self.get_localized_config('server_port', '8080')
         if server_addr is None:
@@ -87,14 +93,11 @@ class Module(MgrModule):
 
         # Apply the 'global' CherryPy configuration.
         config = {
-            'engine.autoreload.on': False
+            'engine.autoreload.on': False,
+            'server.socket_host': server_addr,
+            'server.socket_port': int(server_port),
+            'error_page.default': json_error_page
         }
-        if not in_unittest:
-            config.update({
-                'server.socket_host': server_addr,
-                'server.socket_port': int(server_port),
-                'error_page.default': json_error_page
-            })
         cherrypy.config.update(config)
 
         current_dir = os.path.dirname(os.path.abspath(__file__))
@@ -135,6 +138,9 @@ class Module(MgrModule):
         if cmd['prefix'] == 'dashboard set-login-credentials':
             Auth.set_login_credentials(cmd['username'], cmd['password'])
             return 0, 'Username and password updated', ''
+        elif cmd['prefix'] == 'dashboard set-session-expire':
+            self.set_config('session-expire', str(cmd['seconds']))
+            return 0, 'Session expiration timeout updated', ''
 
         return (-errno.EINVAL, '', 'Command not found \'{0}\''
                 .format(cmd['prefix']))
index 04704c0751ead350462569956545b16dee601e8d..f6191ea38572554e0a27c29cdff0a56cb12c6974 100644 (file)
@@ -23,6 +23,7 @@ pytest==3.3.2
 pytest-cov==2.5.1
 python-bcrypt==0.3.2
 pytz==2017.3
+requests==2.18.4
 singledispatch==3.4.0.3
 six==1.11.0
 tempora==1.10
index 2686022133751bfbde94cd2ffdf9eef9467f1d1a..53b9068f39af560cc674f4ba2b33579fb22bfb99 100644 (file)
@@ -2,27 +2,48 @@
 # pylint: disable=W0212
 from __future__ import absolute_import
 
-import json
+import os
+import subprocess
+import sys
+import unittest
 
-from cherrypy.test import helper
-from more_itertools import always_iterable
+import requests
 
-from ..module import Module
 
+def authenticate(func):
+    def decorate(self, *args, **kwargs):
+        self._ceph_cmd(['dashboard', 'set-login-credentials', 'admin', 'admin'])
+        self._post('/api/auth', {'username': 'admin', 'password': 'admin'})
+        self.assertStatus(201)
+        return func(self, *args, **kwargs)
+    return decorate
+
+
+class ControllerTestCase(unittest.TestCase):
+    DASHBOARD_HOST = os.environ.get('DASHBOARD_V2_HOST', "localhost")
+    DASHBOARD_PORT = os.environ.get('DASHBOARD_V2_PORT', 8080)
+
+    def __init__(self, *args, **kwargs):
+        super(ControllerTestCase, self).__init__(*args, **kwargs)
+        self._session = requests.Session()
+        self._resp = None
 
-class RequestHelper(object):
     def _request(self, url, method, data=None):
-        if not data:
-            b = None
-            h = None
-        else:
-            b = json.dumps(data)
-            h = [('Content-Type', 'application/json'),
-                 ('Content-Length', str(len(b)))]
-        self.getPage(url, method=method, body=b, headers=h)
+        url = "http://{}:{}{}".format(self.DASHBOARD_HOST, self.DASHBOARD_PORT,
+                                      url)
+        if method == 'GET':
+            self._resp = self._session.get(url)
+            return self._resp.json()
+        elif method == 'POST':
+            self._resp = self._session.post(url, json=data)
+        elif method == 'DELETE':
+            self._resp = self._session.delete(url, json=data)
+        elif method == 'PUT':
+            self._resp = self._session.put(url, json=data)
+        return None
 
     def _get(self, url):
-        self._request(url, 'GET')
+        return self._request(url, 'GET')
 
     def _post(self, url, data=None):
         self._request(url, 'POST', data)
@@ -33,25 +54,49 @@ class RequestHelper(object):
     def _put(self, url, data=None):
         self._request(url, 'PUT', data)
 
-    def assertJsonBody(self, data, msg=None):
-        """Fail if value != self.body."""
-        body_str = self.body.decode('utf-8') if isinstance(self.body, bytes) else self.body
-        json_body = json.loads(body_str)
-        if data != json_body:
-            if msg is None:
-                msg = 'expected body:\n%r\n\nactual body:\n%r' % (
-                    data, json_body)
-            self._handlewebError(msg)
+    def cookies(self):
+        return self._resp.cookies
+
+    def jsonBody(self):
+        return self._resp.json()
+
+    def reset_session(self):
+        self._session = requests.Session()
+
+    def assertJsonBody(self, data):
+        body = self._resp.json()
+        self.assertEqual(body, data)
+
+    def assertBody(self, body):
+        self.assertEqual(self._resp.text, body)
 
+    def assertStatus(self, status):
+        self.assertEqual(self._resp.status_code, status)
+
+    @classmethod
+    def _cmd(cls, cmd):
+        if sys.version_info > (3, 0):
+            res = subprocess.run(cmd, stdout=subprocess.PIPE).stdout
+        else:
+            res = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
+        return res.decode('utf-8').strip()
+
+    @classmethod
+    def _ceph_cmd(cls, cmd):
+        _cmd = ['ceph']
+        _cmd.extend(cmd)
+        return cls._cmd(_cmd)
+
+    @classmethod
+    def set_config_key(cls, key, value):
+        cls._ceph_cmd(['config-key', 'set', key, value])
 
-class ControllerTestCase(helper.CPWebCase, RequestHelper):
     @classmethod
-    def setup_server(cls):
-        module = Module('dashboard', None, None)
-        cls._mgr_module = module
-        module.configure_cherrypy(True)
-        cls.setup_test()
+    def get_config_key(cls, key):
+        return cls._ceph_cmd(['config-key', 'get', key])
 
     @classmethod
-    def setup_test(cls):
-        pass
+    def _rbd_cmd(cls, cmd):
+        _cmd = ['rbd']
+        _cmd.extend(cmd)
+        return cls._cmd(_cmd)
index 88eacc44a2fed028571460654d5a9c92fa86438b..63000ac206077e73758d222394a3e2809a645ebd 100644 (file)
@@ -4,120 +4,71 @@ from __future__ import absolute_import
 
 import time
 
-import cherrypy
-from cherrypy.lib.sessions import RamSession
-from mock import patch
-
 from .helper import ControllerTestCase
-from ..controllers.auth import Auth
 from ..tools import Session
 
 
-class Ping(object):
-    @cherrypy.expose
-    @cherrypy.tools.allow(methods=['POST'])
-    def ping(self):
-        pass
-
-
 class AuthTest(ControllerTestCase):
-    @classmethod
-    def setup_test(cls):
-        cherrypy.tree.mount(Ping(), "/api/test",
-                            config={'/': {'tools.authenticate.on': True}})
-        cls._mgr_module.set_config('session-expire', '2')
-        cls._mgr_module.set_config('username', 'admin')
-        cls._mgr_module.set_config('password', Auth.password_hash('admin'))
-
     def setUp(self):
-        self._mgr_module.set_config('session-expire', '2')
-        self._mgr_module.set_config('username', 'admin')
-        self._mgr_module.set_config('password', Auth.password_hash('admin'))
+        self.reset_session()
+        self._ceph_cmd(['dashboard', 'set-session-expire', '2'])
+        self._ceph_cmd(['dashboard', 'set-login-credentials', 'admin', 'admin'])
 
     def test_a_set_login_credentials(self):
-        Auth.set_login_credentials('admin2', 'admin2')
-        user = self._mgr_module.get_config('username')
-        passwd = self._mgr_module.get_config('password')
-        self.assertEqual(user, 'admin2')
-        self.assertEqual(passwd, Auth.password_hash('admin2', passwd))
+        self._ceph_cmd(['dashboard', 'set-login-credentials', 'admin2', 'admin2'])
+        self._post("/api/auth", {'username': 'admin2', 'password': 'admin2'})
+        self.assertStatus(201)
+        self.assertJsonBody({"username": "admin2"})
 
     def test_login_valid(self):
-        sess_mock = RamSession()
-        with patch('cherrypy.session', sess_mock, create=True):
-            self._post("/api/auth", {'username': 'admin', 'password': 'admin'})
-            self.assertStatus('201 Created')
-            self.assertJsonBody({"username": "admin"})
-            self.assertEqual(sess_mock.get(Session.USERNAME), 'admin')
+        self._post("/api/auth", {'username': 'admin', 'password': 'admin'})
+        self.assertStatus(201)
+        self.assertJsonBody({"username": "admin"})
 
     def test_login_stay_signed_in(self):
-        sess_mock = RamSession()
-        with patch('cherrypy.session', sess_mock, create=True):
-            self._post("/api/auth", {
-                'username': 'admin',
-                'password': 'admin',
-                'stay_signed_in': True})
-            self.assertStatus('201 Created')
-            self.assertEqual(sess_mock.get(
-                Session.EXPIRE_AT_BROWSER_CLOSE), False)
-            for _, content in self.cookies:
-                parts = map(str.strip, content.split(';'))
-                parts = {k: v for k, v in (part.split('=') for part in parts)}
-                if Session.NAME in parts:
-                    self.assertIn('expires', parts)
-                    self.assertIn('Max-Age', parts)
+        self._post("/api/auth", {
+            'username': 'admin',
+            'password': 'admin',
+            'stay_signed_in': True})
+        self.assertStatus(201)
+        self.assertIn(Session.NAME, self.cookies())
+        for cookie in self.cookies():
+            if cookie.name == Session.NAME:
+                self.assertIsNotNone(cookie.expires)
 
     def test_login_not_stay_signed_in(self):
-        sess_mock = RamSession()
-        with patch('cherrypy.session', sess_mock, create=True):
-            self._post("/api/auth", {
-                'username': 'admin',
-                'password': 'admin',
-                'stay_signed_in': False})
-            self.assertStatus('201 Created')
-            self.assertEqual(sess_mock.get(
-                Session.EXPIRE_AT_BROWSER_CLOSE), True)
-            for _, content in self.cookies:
-                parts = map(str.strip, content.split(';'))
-                parts = {k: v for k, v in (part.split('=') for part in parts)}
-                if Session.NAME in parts:
-                    self.assertNotIn('expires', parts)
-                    self.assertNotIn('Max-Age', parts)
+        self._post("/api/auth", {
+            'username': 'admin',
+            'password': 'admin',
+            'stay_signed_in': False})
+        self.assertStatus(201)
+        self.assertIn(Session.NAME, self.cookies())
+        for cookie in self.cookies():
+            if cookie.name == Session.NAME:
+                self.assertIsNone(cookie.expires)
 
     def test_login_invalid(self):
-        sess_mock = RamSession()
-        with patch('cherrypy.session', sess_mock, create=True):
-            self._post("/api/auth", {'username': 'admin', 'password': 'inval'})
-            self.assertStatus('403 Forbidden')
-            self.assertJsonBody({"detail": "Invalid credentials"})
-            self.assertEqual(sess_mock.get(Session.USERNAME), None)
+        self._post("/api/auth", {'username': 'admin', 'password': 'inval'})
+        self.assertStatus(403)
+        self.assertJsonBody({"detail": "Invalid credentials"})
 
     def test_logout(self):
-        sess_mock = RamSession()
-        with patch('cherrypy.session', sess_mock, create=True):
-            self._post("/api/auth", {'username': 'admin', 'password': 'admin'})
-            self.assertEqual(sess_mock.get(Session.USERNAME), 'admin')
-            self._delete("/api/auth")
-            self.assertStatus('204 No Content')
-            self.assertBody('')
-            self.assertEqual(sess_mock.get(Session.USERNAME), None)
+        self._post("/api/auth", {'username': 'admin', 'password': 'admin'})
+        self._delete("/api/auth")
+        self.assertStatus(204)
+        self.assertBody('')
+        self._get("/api/host")
+        self.assertStatus(401)
 
     def test_session_expire(self):
-        sess_mock = RamSession()
-        with patch('cherrypy.session', sess_mock, create=True):
-            self._post("/api/auth", {'username': 'admin', 'password': 'admin'})
-            self.assertStatus('201 Created')
-            self.assertEqual(sess_mock.get(Session.USERNAME), 'admin')
-            self._post("/api/test/ping")
-            self.assertStatus('200 OK')
-            self.assertEqual(sess_mock.get(Session.USERNAME), 'admin')
-            time.sleep(3)
-            self._post("/api/test/ping")
-            self.assertStatus('401 Unauthorized')
-            self.assertEqual(sess_mock.get(Session.USERNAME), None)
+        self._post("/api/auth", {'username': 'admin', 'password': 'admin'})
+        self.assertStatus(201)
+        self._get("/api/host")
+        self.assertStatus(200)
+        time.sleep(3)
+        self._get("/api/host")
+        self.assertStatus(401)
 
     def test_unauthorized(self):
-        sess_mock = RamSession()
-        with patch('cherrypy.session', sess_mock, create=True):
-            self._post("/api/test/ping")
-            self.assertStatus('401 Unauthorized')
-            self.assertEqual(sess_mock.get(Session.USERNAME), None)
+        self._get("/api/host")
+        self.assertStatus(401)
diff --git a/src/pybind/mgr/dashboard_v2/tests/test_host.py b/src/pybind/mgr/dashboard_v2/tests/test_host.py
new file mode 100644 (file)
index 0000000..5726435
--- /dev/null
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+from .helper import ControllerTestCase, authenticate
+
+
+class HostControllerTest(ControllerTestCase):
+
+    @authenticate
+    def test_host_list(self):
+        data = self._get('/api/host')
+        self.assertStatus(200)
+
+        self.assertEqual(len(data), 1)
+        data = data[0]
+        self.assertIn('services', data)
+        self.assertIn('hostname', data)
+        self.assertIn('ceph_version', data)
+        self.assertIsNotNone(data['hostname'])
+        self.assertIsNotNone(data['ceph_version'])
+        self.assertGreaterEqual(len(data['services']), 1)
+        for service in data['services']:
+            self.assertIn('type', service)
+            self.assertIn('id', service)
+            self.assertIsNotNone(service['type'])
+            self.assertIsNotNone(service['id'])
diff --git a/src/pybind/mgr/dashboard_v2/tests/test_module.py b/src/pybind/mgr/dashboard_v2/tests/test_module.py
deleted file mode 100644 (file)
index 3edc3a5..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-
-import errno
-
-from .helper import ControllerTestCase
-from ..controllers.auth import Auth
-
-
-class ModuleTest(ControllerTestCase):
-
-    def test_ping(self):
-        self._get("/api/ping")
-        self.assertStatus("401 Unauthorized")
-
-    def test_set_login_credentials_cmd(self):
-        ret, _, _ = self._mgr_module.handle_command({
-            'prefix': 'dashboard set-login-credentials',
-            'username': 'admin',
-            'password': 'admin'
-        })
-        self.assertEqual(ret, 0)
-        user = self._mgr_module.get_localized_config('username')
-        passwd = self._mgr_module.get_localized_config('password')
-        self.assertEqual(user, 'admin')
-        self.assertEqual(passwd, Auth.password_hash('admin', passwd))
-
-    def test_cmd_not_found(self):
-        ret, _, _ = self._mgr_module.handle_command({
-            'prefix': 'dashboard non-command'
-        })
-        self.assertEqual(ret, -errno.EINVAL)
diff --git a/src/pybind/mgr/dashboard_v2/tests/test_ping.py b/src/pybind/mgr/dashboard_v2/tests/test_ping.py
deleted file mode 100644 (file)
index c382b02..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-# pylint: disable=W0212
-
-from __future__ import absolute_import
-
-from .helper import ControllerTestCase
-from ..controllers.ping import Ping
-
-
-class PingTest(ControllerTestCase):
-    @classmethod
-    def setup_test(cls):
-        Ping._cp_config['tools.authenticate.on'] = False
-
-    def test_ping(self):
-        self.getPage("/api/ping")
-        self.assertStatus('200 OK')
-
-    def test_echo(self):
-        self._post("/api/echo2", {'msg': 'Hello World'})
-        self.assertStatus('201 Created')
-        self.assertJsonBody({'echo': 'Hello World'})
-
-    def test_echo_args(self):
-        self._post("/api/echo1", {'msg': 'Hello World'})
-        self.assertStatus('201 Created')
-        self.assertJsonBody({'echo': 'Hello World'})
index aac4798c74e08b640bbbc6304abcbe97b118d0e7..cc84de37572da8dc03267eea31cf8538ca7e05c0 100644 (file)
@@ -1,65 +1,30 @@
-from mock import Mock
-import cherrypy
-from ..controllers.rgw import RgwDaemon
-from .helper import ControllerTestCase
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
 
-mocked_servers = [{
-    'ceph_version': 'ceph version 13.0.0-5083-g8d1965af24 ' +
-                    '(8d1965af241a5a5487e1b2e3684c676c47392be9) ' +
-                    'mimic (dev)',
-    'hostname': 'ceph-dev',
-    'services': [{'id': 'a', 'type': 'mds'},
-                 {'id': 'b', 'type': 'mds'},
-                 {'id': 'c', 'type': 'mds'},
-                 {'id': 'a', 'type': 'mon'},
-                 {'id': 'b', 'type': 'mon'},
-                 {'id': 'c', 'type': 'mon'},
-                 {'id': '0', 'type': 'osd'},
-                 {'id': '1', 'type': 'osd'},
-                 {'id': '2', 'type': 'osd'},
-                 {'id': 'rgw', 'type': 'rgw'}]}]
-
-mocked_metadata = {
-    'arch': 'x86_64',
-    'ceph_version': 'ceph version 13.0.0-5083-g8d1965af24 ' +
-                    '(8d1965af241a5a5487e1b2e3684c676c47392be9) mimic (dev)',
-    'cpu': 'Intel(R) Core(TM)2 Quad  CPU   Q9550  @ 2.83GHz',
-    'distro': 'opensuse',
-    'distro_description': 'openSUSE Tumbleweed',
-    'distro_version': '20180202',
-    'frontend_config#0': 'civetweb port=8000',
-    'frontend_type#0': 'civetweb',
-    'hostname': 'ceph-dev',
-    'kernel_description': '#135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018',
-    'kernel_version': '4.4.0-112-generic',
-    'mem_swap_kb': '6287356',
-    'mem_total_kb': '8173856',
-    'num_handles': '1',
-    'os': 'Linux',
-    'pid': '979',
-    'zone_id': '850fbec4-9238-481d-9044-c8048f4d582e',
-    'zone_name': 'default',
-    'zonegroup_id': '47e10f8e-5801-4d4d-802c-fc32a252c0ba',
-    'zonegroup_name': 'default'}
+from .helper import ControllerTestCase, authenticate
 
 
 class RgwControllerTest(ControllerTestCase):
 
-    @classmethod
-    def setup_test(cls):
-        mgr_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 = {'current_sync': []}
-        mgr_mock.url_prefix = ''
+    @authenticate
+    def test_rgw_daemon_list(self):
+        data = self._get('/api/rgw/daemon')
+        self.assertStatus(200)
 
-        cherrypy.tree.mount(RgwDaemon(mgr_mock), "/api/test/rgw")
+        self.assertEqual(len(data), 1)
+        data = data[0]
+        self.assertIn('id', data)
+        self.assertIn('version', data)
+        self.assertIn('server_hostname', data)
 
-    def test_list(self):
-        self._get('/api/test/rgw')
+    @authenticate
+    def test_rgw_daemon_get(self):
+        data = self._get('/api/rgw/daemon')
         self.assertStatus(200)
-        self.assertJsonBody([{
-            'id': 'rgw',
-            'version': mocked_servers[0]['ceph_version'],
-            'server_hostname': mocked_servers[0]['hostname']
-        }])
+        data = self._get('/api/rgw/daemon/{}'.format(data[0]['id']))
+        self.assertStatus(200)
+
+        self.assertIn('rgw_metadata', data)
+        self.assertIn('rgw_id', data)
+        self.assertIn('rgw_status', data)
+        self.assertTrue(data['rgw_metadata'])
index d108c58fa0d37aa5b47c668e6179e15a0aaa2468..8a868b95611c38f880ba8ca50cb328d17f8bc576 100644 (file)
@@ -1,12 +1,13 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import
 
+import json
+
 import cherrypy
 from cherrypy.lib.sessions import RamSession
 from cherrypy.test import helper
 from mock import patch
 
-from .helper import RequestHelper
 from ..tools import RESTController, detail_route
 
 
@@ -51,7 +52,39 @@ class Root(object):
     fooargs = FooArgs()
 
 
-class RESTControllerTest(helper.CPWebCase, RequestHelper):
+class RESTControllerTest(helper.CPWebCase):
+    def _request(self, url, method, data=None):
+        if not data:
+            b = None
+            h = None
+        else:
+            b = json.dumps(data)
+            h = [('Content-Type', 'application/json'),
+                 ('Content-Length', str(len(b)))]
+        self.getPage(url, method=method, body=b, headers=h)
+
+    def _get(self, url):
+        self._request(url, 'GET')
+
+    def _post(self, url, data=None):
+        self._request(url, 'POST', data)
+
+    def _delete(self, url, data=None):
+        self._request(url, 'DELETE', data)
+
+    def _put(self, url, data=None):
+        self._request(url, 'PUT', data)
+
+    def assertJsonBody(self, data, msg=None):
+        """Fail if value != self.body."""
+        body_str = self.body.decode('utf-8') if isinstance(self.body, bytes) else self.body
+        json_body = json.loads(body_str)
+        if data != json_body:
+            if msg is None:
+                msg = 'expected body:\n%r\n\nactual body:\n%r' % (
+                    data, json_body)
+            self._handlewebError(msg)
+
     @classmethod
     def setup_server(cls):
         cherrypy.tree.mount(Root())
index 6fcfcdacef732d25f72bb9e2435fef0544c15ae6..3d1ad27e68c144fea2b28ccd5fafbd9d81fb24aa 100644 (file)
@@ -1,5 +1,5 @@
 [tox]
-envlist = cov-init,py27,py3,cov-report,lint
+envlist = cov-init,ceph-cluster-start,py27,py3,ceph-cluster-stop,cov-report,lint
 skipsdist = true
 
 [testenv]
@@ -10,6 +10,8 @@ setenv=
     COVERAGE_FILE= .coverage.{envname}
     PYTHONPATH = {toxinidir}/../../../../build/lib/cython_modules/lib.3:{toxinidir}/../../../../build/lib/cython_modules/lib.2
     LD_LIBRARY_PATH = {toxinidir}/../../../../build/lib
+    PATH = {toxinidir}/../../../../build/bin:$PATH
+    DASHBOARD_V2_PORT=9865
 commands=
     {envbindir}/py.test --cov=. --cov-report= --junitxml=junit.{envname}.xml tests/
 
@@ -20,6 +22,41 @@ deps = coverage
 commands =
     coverage erase
 
+[testenv:ceph-cluster-start]
+deps=coverage
+changedir={toxinidir}/../../../../build
+whitelist_externals=
+    bash
+    cp
+    sleep
+setenv=
+    COVERAGE_ENABLED=true
+    COVERAGE_FILE=.coverage.mgr
+    RGW=1
+commands =
+    bash ../src/vstart.sh -n
+    sleep 10
+    python ./bin/ceph config-key set mgr/dashboard_v2/x/server_port 9865
+    python ./bin/ceph mgr module enable dashboard_v2
+    cp ceph.conf {toxinidir}/
+    sleep 20
+
+[testenv:ceph-cluster-stop]
+deps=
+changedir={toxinidir}/../../../../build
+whitelist_externals=
+    bash
+    rm
+    mv
+    killall
+    sleep
+commands =
+    killall ceph-mgr
+    sleep 5
+    bash ../src/stop.sh
+    rm {toxinidir}/ceph.conf
+    mv {toxinidir}/../../../../build/.coverage.mgr {toxinidir}/
+
 [testenv:cov-report]
 setenv =
     COVERAGE_FILE = .coverage
@@ -35,5 +72,5 @@ setenv =
     LD_LIBRARY_PATH = {toxinidir}/../../../../build/lib
 deps=-r{toxinidir}/requirements.txt
 commands=
-    pylint --rcfile=.pylintrc --jobs=5 . module.py tools.py controllers tests cephmock
-    pycodestyle --max-line-length=100 --exclude=python2.7,.tox,venv,frontend .
+    pylint --rcfile=.pylintrc --jobs=5 . module.py tools.py controllers tests
+    pycodestyle --max-line-length=100 --exclude=python2.7,.tox,venv,frontend --ignore=E402,E121,E123,E126,E226,E24,E704,W503 .