]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Fix mypy issues and enable it by default 33454/head
authorVolker Theile <vtheile@suse.com>
Mon, 24 Feb 2020 12:03:13 +0000 (13:03 +0100)
committerVolker Theile <vtheile@suse.com>
Tue, 25 Feb 2020 10:27:03 +0000 (11:27 +0100)
The decorator @no_type_check is used:
* To prevent a mypy error like 'error: INTERNAL ERROR -- Please try using mypy master on Github:'
* '#type: ignore' does not work, e.g. in broken lines

Fixes: https://tracker.ceph.com/issues/44269
Signed-off-by: Volker Theile <vtheile@suse.com>
31 files changed:
src/pybind/mgr/dashboard/__init__.py
src/pybind/mgr/dashboard/awsauth.py
src/pybind/mgr/dashboard/cherrypy_backports.py
src/pybind/mgr/dashboard/controllers/__init__.py
src/pybind/mgr/dashboard/controllers/cephfs.py
src/pybind/mgr/dashboard/controllers/iscsi.py
src/pybind/mgr/dashboard/controllers/orchestrator.py
src/pybind/mgr/dashboard/controllers/osd.py
src/pybind/mgr/dashboard/controllers/rbd_mirroring.py
src/pybind/mgr/dashboard/controllers/rgw.py
src/pybind/mgr/dashboard/module.py
src/pybind/mgr/dashboard/plugins/debug.py
src/pybind/mgr/dashboard/plugins/feature_toggles.py
src/pybind/mgr/dashboard/plugins/interfaces.py
src/pybind/mgr/dashboard/plugins/pluggy.py
src/pybind/mgr/dashboard/plugins/plugin.py
src/pybind/mgr/dashboard/plugins/ttl_cache.py
src/pybind/mgr/dashboard/rest_client.py
src/pybind/mgr/dashboard/services/access_control.py
src/pybind/mgr/dashboard/services/auth.py
src/pybind/mgr/dashboard/services/ceph_service.py
src/pybind/mgr/dashboard/services/exception.py
src/pybind/mgr/dashboard/services/iscsi_client.py
src/pybind/mgr/dashboard/services/iscsi_config.py
src/pybind/mgr/dashboard/services/orchestrator.py
src/pybind/mgr/dashboard/services/rbd.py
src/pybind/mgr/dashboard/services/rgw_client.py
src/pybind/mgr/dashboard/services/sso.py
src/pybind/mgr/dashboard/services/tcmu_service.py
src/pybind/mgr/dashboard/tools.py
src/pybind/mgr/tox.ini

index bdcdcc3685ce40b31833fa560f4e8f7ea5de147c..78879986398d8f1f4060077f337b801bcb72df78 100644 (file)
@@ -32,7 +32,7 @@ else:
     os.environ['PATH'] = '{}:{}'.format(os.path.abspath('../../../../build/bin'),
                                         os.environ['PATH'])
 
-    from tests import mock
+    from tests import mock  # type: ignore
 
     mgr = mock.Mock()
     mgr.get_frontend_path.side_effect = lambda: os.path.abspath("./frontend/dist")
index fdf87de61377d94141de6395ef919f49560ade7d..5cbb6f297d755b1a8151d6a16c89cc032f6574c1 100644 (file)
@@ -90,7 +90,7 @@ class S3Auth(AuthBase):
             key = self.secret_key.encode('utf-8')
             msg = canonical_string.encode('utf-8')
         else:
-            key = self.secret_key
+            key = self.secret_key  # type: ignore
             msg = canonical_string
         h = hmac.new(key, msg, digestmod=sha)
         return encodestring(h.digest()).strip()
index ba5ef2e1a9121cbdcd2012369cf4ec52d5791ca0..d11b541fab1a178c7b6b3bb0479795e31436e1b1 100644 (file)
@@ -174,7 +174,7 @@ def patch_request_unique_id(v):
                 It's evaluated lazily on render.
                 """
                 try:
-                    self._uuid4
+                    self._uuid4  # type: ignore
                 except AttributeError:
                     # evaluate on first access
                     self._uuid4 = uuid.uuid4()
index e539008eb5288f3e9fe2e4d7da5528fc2c47198c..3bd4cba8d738a56b47e38ec03b670fadfad649d2 100644 (file)
@@ -24,6 +24,11 @@ from ..exceptions import ScopeNotValid, PermissionNotValid
 from ..services.auth import AuthManager, JwtManager
 from ..plugins import PLUGIN_MANAGER
 
+try:
+    from typing import Any, List, Optional
+except ImportError:
+    pass  # For typing only
+
 
 def EndpointDoc(description="", group="", parameters=None, responses=None):  # noqa: N802
     if not isinstance(description, str):
@@ -86,14 +91,14 @@ def EndpointDoc(description="", group="", parameters=None, responses=None):  # n
         return splitted
 
     def _split_list(data, nested):
-        splitted = []
+        splitted = []  # type: List[Any]
         for item in data:
             splitted.extend(_split_parameters(item, nested))
         return splitted
 
     # nested = True means parameters are inside a dict or array
     def _split_parameters(data, nested=False):
-        param_list = []
+        param_list = []  # type: List[Any]
         if isinstance(data, dict):
             param_list.extend(_split_dict(data, nested))
         elif isinstance(data, (list, tuple)):
@@ -286,7 +291,7 @@ def load_controllers():
     return controllers
 
 
-ENDPOINT_MAP = collections.defaultdict(list)
+ENDPOINT_MAP = collections.defaultdict(list)  # type: dict
 
 
 def generate_controller_routes(endpoint, mapper, base_url):
@@ -589,11 +594,11 @@ class BaseController(object):
     def __init__(self):
         logger = logging.getLogger('controller')
         logger.info('Initializing controller: %s -> %s',
-                    self.__class__.__name__, self._cp_path_)
+                    self.__class__.__name__, self._cp_path_)  # type: ignore
         super(BaseController, self).__init__()
 
     def _has_permissions(self, permissions, scope=None):
-        if not self._cp_config['tools.authenticate.on']:
+        if not self._cp_config['tools.authenticate.on']:  # type: ignore
             raise Exception("Cannot verify permission in non secured "
                             "controllers")
 
@@ -612,7 +617,7 @@ class BaseController(object):
     def get_path_param_names(cls, path_extension=None):
         if path_extension is None:
             path_extension = ""
-        full_path = cls._cp_path_[1:] + path_extension
+        full_path = cls._cp_path_[1:] + path_extension  # type: ignore
         path_params = []
         for step in full_path.split('/'):
             param = None
@@ -628,7 +633,7 @@ class BaseController(object):
 
     @classmethod
     def get_path(cls):
-        return cls._cp_path_
+        return cls._cp_path_  # type: ignore
 
     @classmethod
     def endpoints(cls):
@@ -728,7 +733,7 @@ class RESTController(BaseController):
     # to specify a composite id (two parameters) use '/'. e.g., "param1/param2".
     # If subclasses don't override this property we try to infer the structure
     # of the resource ID.
-    RESOURCE_ID = None
+    RESOURCE_ID = None  # type: Optional[str]
 
     _permission_map = {
         'GET': Permission.READ,
@@ -777,7 +782,7 @@ class RESTController(BaseController):
             permission = None
 
             if func.__name__ in cls._method_mapping:
-                meth = cls._method_mapping[func.__name__]
+                meth = cls._method_mapping[func.__name__]  # type: dict
 
                 if meth['resource']:
                     if not res_id_params:
index f4c024d619e9bf9ab2a298e2a4891aec716c187e..d482ee6de37cf8b81f256f848fb9958c0d8f331c 100644 (file)
@@ -74,7 +74,7 @@ class CephFS(RESTController):
                 "mds_mem.ino"
             ]
 
-        result = {}
+        result = {}  # type: dict
         mds_names = self._get_mds_names(fs_id)
 
         for mds_name in mds_names:
@@ -129,7 +129,7 @@ class CephFS(RESTController):
 
     # pylint: disable=too-many-statements,too-many-branches
     def fs_status(self, fs_id):
-        mds_versions = defaultdict(list)
+        mds_versions = defaultdict(list)  # type: dict
 
         fsmap = mgr.get("fs_map")
         filesystem = None
index 843b0fac0b3a8fbdefc56799502bcb182e642d3a..af5049300001dfa35c4aa780af8b1614bc2de8af 100644 (file)
@@ -22,6 +22,11 @@ from ..services.tcmu_service import TcmuService
 from ..exceptions import DashboardException
 from ..tools import str_to_bool, TaskManager
 
+try:
+    from typing import Any, Dict, List, no_type_check
+except ImportError:
+    no_type_check = object()  # Just for type checking
+
 
 @UiApiController('/iscsi', Scope.ISCSI)
 class IscsiUi(BaseController):
@@ -31,6 +36,7 @@ class IscsiUi(BaseController):
 
     @Endpoint()
     @ReadPermission
+    @no_type_check
     def status(self):
         status = {'available': False}
         try:
@@ -559,7 +565,7 @@ class IscsiTarget(RESTController):
                                                      code='disk_control_invalid_max',
                                                      component='iscsi')
 
-        initiators = []
+        initiators = []  # type: List[Any]
         for group in groups:
             initiators = initiators + group['members']
         if len(initiators) != len(set(initiators)):
@@ -895,7 +901,8 @@ class IscsiTarget(RESTController):
 
     @staticmethod
     def _get_portals_by_host(portals):
-        portals_by_host = {}
+        # type: (List[dict]) -> Dict[str, List[str]]
+        portals_by_host = {}  # type: Dict[str, List[str]]
         for portal in portals:
             host = portal['host']
             ip = portal['ip']
index 6e3f11d7a5c01dd618ec6352c29a15bd59ba4827..642a2f6bab2121b5981150481544802ef67f8bd3 100644 (file)
@@ -88,7 +88,8 @@ class Orchestrator(RESTController):
         """
         Identify a device by switching on the device light for N seconds.
         :param hostname: The hostname of the device to process.
-        :param device: The device identifier to process, e.g. ``ABC1234DEF567-1R1234_ABC8DE0Q``.
+        :param device: The device identifier to process, e.g. ``/dev/dm-0`` or
+          ``ABC1234DEF567-1R1234_ABC8DE0Q``.
         :param duration: The duration in seconds how long the LED should flash.
         """
         orch = OrchClient.instance()
index 064a48be0d8e81e13ec1b48e9b3c740ca6770e8d..373f66329adcfe024e7dc404234a09aeab1ccd96 100644 (file)
@@ -62,7 +62,7 @@ class Osd(RESTController):
 
     @staticmethod
     def get_osd_map(svc_id=None):
-        # type: (Union[int, None]) -> Dict[int, Union[Dict[str, Any], Any]]
+        # type: (Union[int, None]) -> Dict[int, Union[dict, Any]]
         def add_id(osd):
             osd['id'] = osd['osd']
             return osd
index 505787387a7c77de9258e96f02669151b6755f4f..497f8c3d72cb6b334606fb6da889f74e4ad0fdad 100644 (file)
@@ -22,6 +22,11 @@ from ..tools import ViewCache
 from ..services.exception import handle_rados_error, handle_rbd_error, \
     serialize_dashboard_exception
 
+try:
+    from typing import no_type_check
+except ImportError:
+    no_type_check = object()  # Just for type checking
+
 
 logger = logging.getLogger('controllers.rbd_mirror')
 
@@ -148,7 +153,7 @@ def get_daemons_and_pools():  # pylint: disable=R0915
 
         for daemon in daemons:
             for _, pool_data in daemon['status'].items():
-                stats = pool_stats.get(pool_data['name'], None)
+                stats = pool_stats.get(pool_data['name'], None)  # type: ignore
                 if stats is None:
                     continue
 
@@ -191,6 +196,7 @@ def get_daemons_and_pools():  # pylint: disable=R0915
 
 
 @ViewCache()
+@no_type_check
 def _get_pool_datum(pool_name):
     data = {}
     logger.debug("Constructing IOCtx %s", pool_name)
index 608093dea889261af250fadd2bbf06776e945c6e..4e3f8f2ac9099501340c652f58a7f5cbd08afd06 100644 (file)
@@ -15,6 +15,11 @@ from ..services.ceph_service import CephService
 from ..services.rgw_client import RgwClient
 from ..tools import json_str_to_object
 
+try:
+    from typing import List
+except ImportError:
+    pass  # Just for type checking
+
 
 logger = logging.getLogger('controllers.rgw')
 
@@ -44,7 +49,7 @@ class Rgw(BaseController):
                 raise RequestException(msg)
             status['available'] = True
         except (RequestException, LookupError) as ex:
-            status['message'] = str(ex)
+            status['message'] = str(ex)  # type: ignore
         return status
 
 
@@ -52,6 +57,7 @@ class Rgw(BaseController):
 class RgwDaemon(RESTController):
 
     def list(self):
+        # type: () -> List[dict]
         daemons = []
         for hostname, server in CephService.get_service_map('rgw').items():
             for service in server['services']:
@@ -69,6 +75,7 @@ class RgwDaemon(RESTController):
         return sorted(daemons, key=lambda k: k['id'])
 
     def get(self, svc_id):
+        # type: (str) -> dict
         daemon = {
             'rgw_metadata': [],
             'rgw_id': svc_id,
@@ -150,7 +157,7 @@ class RgwBucket(RgwRESTController):
 
     @staticmethod
     def strip_tenant_from_bucket_name(bucket_name):
-        # type (str) => str
+        # type (str) -> str
         """
         >>> RgwBucket.strip_tenant_from_bucket_name('tenant/bucket-name')
         'bucket-name'
@@ -161,7 +168,7 @@ class RgwBucket(RgwRESTController):
 
     @staticmethod
     def get_s3_bucket_name(bucket_name, tenant=None):
-        # type (str, str) => str
+        # type (str, str) -> str
         """
         >>> RgwBucket.get_s3_bucket_name('bucket-name', 'tenant')
         'tenant:bucket-name'
@@ -176,9 +183,11 @@ class RgwBucket(RgwRESTController):
         return bucket_name
 
     def list(self):
+        # type: () -> List[str]
         return self.proxy('GET', 'bucket')
 
     def get(self, bucket):
+        # type: (str) -> dict
         result = self.proxy('GET', 'bucket', {'bucket': bucket})
 
         result['versioning'] =\
@@ -241,10 +250,11 @@ class RgwUser(RgwRESTController):
         return user
 
     def list(self):
-        users = []
+        # type: () -> List[str]
+        users = []  # type: List[str]
         marker = None
         while True:
-            params = {}
+            params = {}  # type: dict
             if marker:
                 params['marker'] = marker
             result = self.proxy('GET', 'user?list', params)
@@ -259,15 +269,17 @@ class RgwUser(RgwRESTController):
         return users
 
     def get(self, uid):
+        # type: (str) -> dict
         result = self.proxy('GET', 'user', {'uid': uid})
         return self._append_uid(result)
 
     @Endpoint()
     @ReadPermission
     def get_emails(self):
+        # type: () -> List[str]
         emails = []
-        for uid in json.loads(self.list()):
-            user = json.loads(self.get(uid))
+        for uid in json.loads(self.list()):  # type: ignore
+            user = json.loads(self.get(uid))  # type: ignore
             if user["email"]:
                 emails.append(user["email"])
         return emails
index 1815e23f0f9da22292056db01175ea053a06ab70..d58674c8bb9074b290be31f6d3bff2ee940347e4 100644 (file)
@@ -104,20 +104,20 @@ class CherryPyConfig(object):
 
         :returns our URI
         """
-        server_addr = self.get_localized_module_option(
+        server_addr = self.get_localized_module_option(  # type: ignore
             'server_addr', get_default_addr())
-        ssl = self.get_localized_module_option('ssl', True)
+        ssl = self.get_localized_module_option('ssl', True)  # type: ignore
         if not ssl:
-            server_port = self.get_localized_module_option('server_port', 8080)
+            server_port = self.get_localized_module_option('server_port', 8080)  # type: ignore
         else:
-            server_port = self.get_localized_module_option('ssl_server_port', 8443)
+            server_port = self.get_localized_module_option('ssl_server_port', 8443)  # type: ignore
 
         if server_addr is None:
             raise ServerConfigException(
                 'no server_addr configured; '
                 'try "ceph config set mgr mgr/{}/{}/server_addr <ip>"'
-                .format(self.module_name, self.get_mgr_id()))
-        self.log.info('server: ssl=%s host=%s port=%d', 'yes' if ssl else 'no',
+                .format(self.module_name, self.get_mgr_id()))  # type: ignore
+        self.log.info('server: ssl=%s host=%s port=%d', 'yes' if ssl else 'no',  # type: ignore
                       server_addr, server_port)
 
         # Initialize custom handlers.
@@ -155,23 +155,23 @@ class CherryPyConfig(object):
 
         if ssl:
             # SSL initialization
-            cert = self.get_store("crt")
+            cert = self.get_store("crt")  # type: ignore
             if cert is not None:
                 self.cert_tmp = tempfile.NamedTemporaryFile()
                 self.cert_tmp.write(cert.encode('utf-8'))
                 self.cert_tmp.flush()  # cert_tmp must not be gc'ed
                 cert_fname = self.cert_tmp.name
             else:
-                cert_fname = self.get_localized_module_option('crt_file')
+                cert_fname = self.get_localized_module_option('crt_file')  # type: ignore
 
-            pkey = self.get_store("key")
+            pkey = self.get_store("key")  # type: ignore
             if pkey is not None:
                 self.pkey_tmp = tempfile.NamedTemporaryFile()
                 self.pkey_tmp.write(pkey.encode('utf-8'))
                 self.pkey_tmp.flush()  # pkey_tmp must not be gc'ed
                 pkey_fname = self.pkey_tmp.name
             else:
-                pkey_fname = self.get_localized_module_option('key_file')
+                pkey_fname = self.get_localized_module_option('key_file')  # type: ignore
 
             verify_tls_files(cert_fname, pkey_fname)
 
@@ -181,8 +181,8 @@ class CherryPyConfig(object):
 
         self.update_cherrypy_config(config)
 
-        self._url_prefix = prepare_url_prefix(self.get_module_option('url_prefix',
-                                                                     default=''))
+        self._url_prefix = prepare_url_prefix(self.get_module_option(  # type: ignore
+            'url_prefix', default=''))
 
         uri = "{0}://{1}:{2}{3}/".format(
             'https' if ssl else 'http',
@@ -204,13 +204,13 @@ class CherryPyConfig(object):
             try:
                 uri = self._configure()
             except ServerConfigException as e:
-                self.log.info("Config not ready to serve, waiting: {0}".format(
-                    e
-                ))
+                self.log.info(  # type: ignore
+                    "Config not ready to serve, waiting: {0}".format(e)
+                )
                 # Poll until a non-errored config is present
                 self._stopping.wait(5)
             else:
-                self.log.info("Configured CherryPy, starting engine...")
+                self.log.info("Configured CherryPy, starting engine...")  # type: ignore
                 return uri
 
 
@@ -267,7 +267,7 @@ class Module(MgrModule, CherryPyConfig):
         MODULE_OPTIONS.extend(options)
 
     __pool_stats = collections.defaultdict(lambda: collections.defaultdict(
-        lambda: collections.deque(maxlen=10)))
+        lambda: collections.deque(maxlen=10)))  # type: dict
 
     def __init__(self, *args, **kwargs):
         super(Module, self).__init__(*args, **kwargs)
index f85336f50082abfa1f2c4ee4737318f38f395f13..71e56abc6460b8d077be79778affdc8d4496fb1d 100644 (file)
@@ -8,6 +8,11 @@ from . import PLUGIN_MANAGER as PM
 from . import interfaces as I  # noqa: E741,N812
 from .plugin import SimplePlugin as SP
 
+try:
+    from typing import no_type_check
+except ImportError:
+    no_type_check = object()  # Just for type checking
+
 
 class Actions(Enum):
     ENABLE = 'enable'
@@ -28,6 +33,7 @@ class Debug(SP, I.CanCherrypy, I.ConfiguresCherryPy):
         )
     ]
 
+    @no_type_check
     def handler(self, action):
         ret = 0
         msg = ''
index 3227a05b1812a6b5472e152f5329f745a926cdf8..2373df2dea33e6230efd7a8307962769e466e953 100644 (file)
@@ -16,6 +16,11 @@ from ..controllers.iscsi import Iscsi, IscsiTarget
 from ..controllers.cephfs import CephFS
 from ..controllers.rgw import Rgw, RgwDaemon, RgwBucket, RgwUser
 
+try:
+    from typing import no_type_check, Set
+except ImportError:
+    no_type_check = object()  # Just for type checking
+
 
 class Features(Enum):
     RBD = 'rbd'
@@ -25,7 +30,7 @@ class Features(Enum):
     RGW = 'rgw'
 
 
-PREDISABLED_FEATURES = set()
+PREDISABLED_FEATURES = set()  # type: Set[str]
 
 
 Feature2Controller = {
@@ -59,7 +64,7 @@ class FeatureToggles(I.CanMgr, I.Setupable, I.HasOptions,
         self.Controller2Feature = {
             controller: feature
             for feature, controllers in Feature2Controller.items()
-            for controller in controllers}
+            for controller in controllers}  # type: ignore
 
     @PM.add_hook
     def get_options(self):
@@ -102,6 +107,7 @@ class FeatureToggles(I.CanMgr, I.Setupable, I.HasOptions,
             return ret, '\n'.join(msg), ''
         return {'handle_command': cmd}
 
+    @no_type_check  # https://github.com/python/mypy/issues/7806
     def _get_feature_from_request(self, request):
         try:
             return self.Controller2Feature[
@@ -110,6 +116,7 @@ class FeatureToggles(I.CanMgr, I.Setupable, I.HasOptions,
             return None
 
     @ttl_cache(ttl=CACHE_TTL, maxsize=CACHE_MAX_SIZE)
+    @no_type_check  # https://github.com/python/mypy/issues/7806
     def _is_feature_enabled(self, feature):
         return self.mgr.get_module_option(self.OPTION_FMT.format(feature.value))
 
index fdf6fdc58d65ffc62350673655502e9d0b5498a6..f9169a0d89a6ea4f6fe17d17e0a1c58e58dc1582 100644 (file)
@@ -6,7 +6,7 @@ from . import PLUGIN_MANAGER as PM, Interface, Mixin  # pylint: disable=cyclic-i
 
 class CanMgr(Mixin):
     from .. import mgr
-    mgr = mgr
+    mgr = mgr  # type: ignore
 
 
 class CanCherrypy(Mixin):
index 844103883fdea2f8df7cf0e000715644e81b4b7b..0a1765f1f7f541cbdcb75c4368471ced3357dc71 100644 (file)
@@ -40,6 +40,10 @@ distros and releases currently targeted for Ceph Nautilus:
 TODO: Once this becomes available in the above distros, this file should be
 REMOVED, and the fully featured python-pluggy should be used instead.
 """
+try:
+    from typing import DefaultDict
+except ImportError:
+    pass  # For typing only
 
 
 class HookspecMarker(object):
@@ -75,7 +79,7 @@ class _HookRelay(object):
     """
     def __init__(self):
         from collections import defaultdict
-        self._registry = defaultdict(list)
+        self._registry = defaultdict(list)  # type: DefaultDict[str, list]
 
     def __getattr__(self, hook_name):
         return lambda *args, **kwargs: [
index 17225810f0f8664eec779b230bae92ca5dfb2ece..ad8342a20d7db0ecb1a3a55bb6ed127b17e2c17e 100644 (file)
@@ -3,6 +3,11 @@ from mgr_module import Option, Command
 from . import PLUGIN_MANAGER as PM
 from . import interfaces as I  # noqa: E741,N812
 
+try:
+    from typing import no_type_check
+except ImportError:
+    no_type_check = object()  # Just for type checking
+
 
 class SimplePlugin(I.CanMgr, I.HasOptions, I.HasCommands):
     """
@@ -17,17 +22,20 @@ class SimplePlugin(I.CanMgr, I.HasOptions, I.HasCommands):
 
     @PM.add_hook
     def get_options(self):
-        return self.OPTIONS
+        return self.OPTIONS  # type: ignore
 
     @PM.final
+    @no_type_check  # https://github.com/python/mypy/issues/7806
     def get_option(self, option):
         return self.mgr.get_module_option(option)
 
     @PM.final
+    @no_type_check  # https://github.com/python/mypy/issues/7806
     def set_option(self, option, value):
         self.mgr.set_module_option(option, value)
 
     @PM.add_hook
+    @no_type_check  # https://github.com/python/mypy/issues/7806
     def register_commands(self):
         for cmd in self.COMMANDS:
             cmd.register(instance=self)
index 4677e20dfbf151ba56d9093be5e79ce8c4e67cdf..4f4f244d2d77a54d93b1c90de321f5949b5e14b7 100644 (file)
@@ -10,13 +10,18 @@ from collections import OrderedDict
 from threading import RLock
 from time import time
 
+try:
+    from typing import Tuple
+except ImportError:
+    pass  # For typing only
+
 
 def ttl_cache(ttl, maxsize=128, typed=False):
     if typed is not False:
         raise NotImplementedError("typed caching not supported")
 
     def decorating_function(function):
-        cache = OrderedDict()
+        cache = OrderedDict()  # type: OrderedDict[object, Tuple[bool, float]]
         stats = [0, 0, 0]
         rlock = RLock()
         setattr(function, 'cache_info', lambda:
index 83d7fc6322f2e743681408ffaecb9183e5cb6d0f..22a36d0e3072c83d0b1ab49fefa4687ea19669bc 100644 (file)
@@ -24,7 +24,12 @@ from .tools import build_url
 try:
     from requests.packages.urllib3.exceptions import SSLError
 except ImportError:
-    from urllib3.exceptions import SSLError
+    from urllib3.exceptions import SSLError  # type: ignore
+
+try:
+    from typing import List
+except ImportError:
+    pass  # Just for type checking
 
 
 logger = logging.getLogger('rest_client')
@@ -180,13 +185,13 @@ class _ResponseValidator(object):
                 level_next = path[path_sep + 1:].strip()
             else:
                 path_sep = len(path)
-                level_next = None
+                level_next = None  # type: ignore
             key = path[:path_sep].strip()
 
             if key == '*':
                 continue
             elif key == '':  # check all keys
-                for k in resp.keys():
+                for k in resp.keys():  # type: ignore
                     _ResponseValidator._validate_key(k, level_next, resp)
             else:
                 _ResponseValidator._validate_key(key, level_next, resp)
@@ -249,6 +254,7 @@ class _ResponseValidator(object):
 
     @staticmethod
     def _parse_level_paths(level):
+        # type: (str) -> List[str]
         level = level.strip()
         if level[0] == '(':
             level = level[1:]
@@ -446,7 +452,7 @@ class RestClient(object):
                         match = re.match(r'.*: \[Errno (-?\d+)\] (.+)',
                                          ex.args[0].reason.args[0])
                     except AttributeError:
-                        match = False
+                        match = None
                     if match:
                         errno = match.group(1)
                         strerror = match.group(2)
index 6b45b5f9394f4ea1effc5565058bd9a3632a8e39..17d9f71976d692dd3f27f0543cfd797bb2854ed5 100644 (file)
@@ -383,7 +383,8 @@ class User(object):
         return False
 
     def permissions_dict(self):
-        perms = {}
+        # type: () -> dict
+        perms = {}  # type: dict
         for role in self.roles:
             for scope, perms_list in role.scopes_permissions.items():
                 if scope in perms:
@@ -553,12 +554,12 @@ class AccessControlDB(object):
             db.check_and_update_db()
             return db
 
-        db = json.loads(json_db)
+        dict_db = json.loads(json_db)
         roles = {rn: Role.from_dict(r)
-                 for rn, r in db.get('roles', {}).items()}
+                 for rn, r in dict_db.get('roles', {}).items()}
         users = {un: User.from_dict(u, dict(roles, **SYSTEM_ROLES))
-                 for un, u in db.get('users', {}).items()}
-        return cls(db['version'], users, roles)
+                 for un, u in dict_db.get('users', {}).items()}
+        return cls(dict_db['version'], users, roles)
 
 
 def load_access_control_db():
index afddfd99aa58851487d70072cb7836bbab3fb09a..d829362e63d3c9e2312dd54b6d8021fc1f23cac2 100644 (file)
@@ -31,7 +31,7 @@ class JwtManager(object):
 
     @classmethod
     def init(cls):
-        cls.logger = logging.getLogger('jwt')
+        cls.logger = logging.getLogger('jwt')  # type: ignore
         # generate a new secret if it does not exist
         secret = mgr.get_store('jwt_secret')
         if secret is None:
@@ -53,13 +53,13 @@ class JwtManager(object):
             'iat': now,
             'username': username
         }
-        return jwt.encode(payload, cls._secret, algorithm=cls.JWT_ALGORITHM)
+        return jwt.encode(payload, cls._secret, algorithm=cls.JWT_ALGORITHM)  # type: ignore
 
     @classmethod
     def decode_token(cls, token):
         if not cls._secret:
             cls.init()
-        return jwt.decode(token, cls._secret, algorithms=cls.JWT_ALGORITHM)
+        return jwt.decode(token, cls._secret, algorithms=cls.JWT_ALGORITHM)  # type: ignore
 
     @classmethod
     def get_token_from_header(cls):
@@ -90,16 +90,20 @@ class JwtManager(object):
                 user = AuthManager.get_user(dtoken['username'])
                 if user.last_update <= dtoken['iat']:
                     return user
-                cls.logger.debug("user info changed after token was issued, iat=%s last_update=%s",
-                                 dtoken['iat'], user.last_update)
+                cls.logger.debug(  # type: ignore
+                    "user info changed after token was issued, iat=%s last_update=%s",
+                    dtoken['iat'], user.last_update
+                )
             else:
-                cls.logger.debug('Token is black-listed')
-        except jwt.exceptions.ExpiredSignatureError:
-            cls.logger.debug("Token has expired")
-        except jwt.exceptions.InvalidTokenError:
-            cls.logger.debug("Failed to decode token")
+                cls.logger.debug('Token is black-listed')  # type: ignore
+        except jwt.ExpiredSignatureError:
+            cls.logger.debug("Token has expired")  # type: ignore
+        except jwt.InvalidTokenError:
+            cls.logger.debug("Failed to decode token")  # type: ignore
         except UserDoesNotExist:
-            cls.logger.debug("Invalid token: user %s does not exist", dtoken['username'])
+            cls.logger.debug(  # type: ignore
+                "Invalid token: user %s does not exist", dtoken['username']
+            )
         return None
 
     @classmethod
@@ -140,15 +144,15 @@ class AuthManager(object):
 
     @classmethod
     def get_user(cls, username):
-        return cls.AUTH_PROVIDER.get_user(username)
+        return cls.AUTH_PROVIDER.get_user(username)  # type: ignore
 
     @classmethod
     def authenticate(cls, username, password):
-        return cls.AUTH_PROVIDER.authenticate(username, password)
+        return cls.AUTH_PROVIDER.authenticate(username, password)  # type: ignore
 
     @classmethod
     def authorize(cls, username, scope, permissions):
-        return cls.AUTH_PROVIDER.authorize(username, scope, permissions)
+        return cls.AUTH_PROVIDER.authorize(username, scope, permissions)  # type: ignore
 
 
 class AuthManagerTool(cherrypy.Tool):
index 506c2f98dfed44c7a25c10c96ba50034b1fb3a65..6c83dcc75570f8492e2b48195f5dbefe47fb6415 100644 (file)
@@ -11,7 +11,7 @@ from mgr_util import get_time_series_rates, get_most_recent_rate
 from .. import mgr
 
 try:
-    from typing import Dict, Any  # pylint: disable=unused-import
+    from typing import Dict  # pylint: disable=unused-import
 except ImportError:
     pass  # For typing only
 
@@ -40,7 +40,7 @@ class CephService(object):
 
     @classmethod
     def get_service_map(cls, service_name):
-        service_map = {}  # type: Dict[str, Dict[str, Any]]
+        service_map = {}  # type: Dict[str, dict]
         for server in mgr.list_servers():
             for service in server['services']:
                 if service['type'] == service_name:
@@ -192,7 +192,7 @@ class CephService(object):
     def get_smart_data_by_host(hostname):
         # type: (str) -> dict
         devices = CephService.get_devices_by_host(hostname)
-        smart_data = {}
+        smart_data = {}  # type: dict
         if devices:
             for device in devices:
                 if device['devid'] not in smart_data:
index 4a1f2a105d1355289daa119394aec1ea455e1321..63249eb8ccf60283bb3f99830fd1dd7e2dd913e8 100644 (file)
@@ -83,7 +83,7 @@ def serialize_dashboard_exception(e, include_http_status=False, task=None):
     if include_http_status:
         out['status'] = getattr(e, 'status', 500)
     if task:
-        out['task'] = dict(name=task.name, metadata=task.metadata)
+        out['task'] = dict(name=task.name, metadata=task.metadata)  # type: ignore
     return out
 
 
index f41639e7a09f36a2c19023ddf50d4c6431b4ae1d..b82a51a3d4a68f62719bd0e156a67fbc1487fd0d 100644 (file)
@@ -22,7 +22,7 @@ logger = logging.getLogger('iscsi_client')
 
 class IscsiClient(RestClient):
     _CLIENT_NAME = 'iscsi'
-    _instances = {}
+    _instances = {}  # type: dict
 
     service_url = None
     gateway_name = None
index fa1ec5a21d1741d5ce45579eb307ffa80da669a3..3c0698c574fa165c579ce06187df9fede59c287c 100644 (file)
@@ -78,7 +78,7 @@ class IscsiGatewaysConfig(object):
 
     @staticmethod
     def _load_config_from_orchestrator():
-        config = {'gateways': {}}
+        config = {'gateways': {}}  # type: dict
         try:
             instances = OrchClient.instance().services.list("iscsi")
             for instance in instances:
index 355ab9f652e573633f3e727d1bd71678ea20476a..b2f282bbc0cb3c2bdf586a85b2ab86a0ff909fe5 100644 (file)
@@ -8,7 +8,6 @@ from orchestrator import OrchestratorClientMixin, raise_if_exception, Orchestrat
 from .. import mgr
 from ..tools import wraps
 
-
 logger = logging.getLogger('orchestrator')
 
 
@@ -16,7 +15,7 @@ logger = logging.getLogger('orchestrator')
 class OrchestratorAPI(OrchestratorClientMixin):
     def __init__(self):
         super(OrchestratorAPI, self).__init__()
-        self.set_mgr(mgr)
+        self.set_mgr(mgr)  # type: ignore
 
     def status(self):
         try:
@@ -24,8 +23,9 @@ class OrchestratorAPI(OrchestratorClientMixin):
             logger.info("is orchestrator available: %s, %s", status, desc)
             return dict(available=status, description=desc)
         except (RuntimeError, OrchestratorError, ImportError):
-            return dict(available=False,
-                        description='Orchestrator is unavailable for unknown reason')
+            return dict(
+                available=False,
+                description='Orchestrator is unavailable for unknown reason')
 
     def orchestrator_wait(self, completions):
         return self._orchestrator_wait(completions)
@@ -38,6 +38,7 @@ def wait_api_result(method):
         self.api.orchestrator_wait([completion])
         raise_if_exception(completion)
         return completion.result
+
     return inner
 
 
@@ -47,7 +48,6 @@ class ResourceManager(object):
 
 
 class HostManger(ResourceManager):
-
     @wait_api_result
     def list(self):
         return self.api.get_hosts()
@@ -66,7 +66,6 @@ class HostManger(ResourceManager):
 
 
 class InventoryManager(ResourceManager):
-
     @wait_api_result
     def list(self, hosts=None, refresh=False):
         host_filter = InventoryFilter(hosts=hosts) if hosts else None
@@ -74,7 +73,6 @@ class InventoryManager(ResourceManager):
 
 
 class ServiceManager(ResourceManager):
-
     @wait_api_result
     def list(self, service_type=None, service_id=None, host_name=None):
         return self.api.list_daemons(service_type, service_id, host_name)
@@ -83,16 +81,17 @@ class ServiceManager(ResourceManager):
         if not isinstance(service_ids, list):
             service_ids = [service_ids]
 
-        completion_list = [self.api.service_action('reload', service_type,
-                                                   service_name, service_id)
-                           for service_name, service_id in service_ids]
+        completion_list = [
+            self.api.service_action('reload', service_type, service_name,
+                                    service_id)
+            for service_name, service_id in service_ids
+        ]
         self.api.orchestrator_wait(completion_list)
         for c in completion_list:
             raise_if_exception(c)
 
 
 class OsdManager(ResourceManager):
-
     @wait_api_result
     def create(self, drive_group):
         return self.api.create_osds([drive_group])
@@ -125,4 +124,5 @@ class OrchClient(object):
     @wait_api_result
     def blink_device_light(self, hostname, device, ident_fault, on):
         # type: (str, str, str, bool) -> Completion
-        return self.api.blink_device_light(ident_fault, on, [DeviceLightLoc(hostname, device)])
+        return self.api.blink_device_light(
+            ident_fault, on, [DeviceLightLoc(hostname, device, device)])
index 1f708eef3ecd75de66683a1cbd6a297574adc7d6..e1670b696814ba3b467443d1d6d3b5190409841b 100644 (file)
@@ -9,6 +9,11 @@ from .. import mgr
 from ..tools import ViewCache
 from .ceph_service import CephService
 
+try:
+    from typing import List
+except ImportError:
+    pass  # For typing only
+
 
 RBD_FEATURES_NAME_MAPPING = {
     rbd.RBD_FEATURE_LAYERING: "layering",
@@ -81,7 +86,7 @@ class RbdConfiguration(object):
 
     def __init__(self, pool_name='', namespace='', image_name='', pool_ioctx=None,
                  image_ioctx=None):
-        # type: (str, str, object, object) -> None
+        # type: (str, str, str, object, object) -> None
         assert bool(pool_name) != bool(pool_ioctx)  # xor
         self._pool_name = pool_name
         self._namespace = namespace if namespace is not None else ''
@@ -95,7 +100,7 @@ class RbdConfiguration(object):
         return option if option.startswith('conf_') else 'conf_' + option
 
     def list(self):
-        # type: () -> [dict]
+        # type: () -> List[dict]
         def _list(ioctx):
             if self._image_name:  # image config
                 with rbd.Image(ioctx, self._image_name) as image:
@@ -131,23 +136,23 @@ class RbdConfiguration(object):
         pool_ioctx = self._pool_ioctx
         if self._pool_name:  # open ioctx
             pool_ioctx = mgr.rados.open_ioctx(self._pool_name)
-            pool_ioctx.__enter__()
-            pool_ioctx.set_namespace(self._namespace)
+            pool_ioctx.__enter__()  # type: ignore
+            pool_ioctx.set_namespace(self._namespace)  # type: ignore
 
         image_ioctx = self._image_ioctx
         if self._image_name:
             image_ioctx = rbd.Image(pool_ioctx, self._image_name)
-            image_ioctx.__enter__()
+            image_ioctx.__enter__()  # type: ignore
 
         if image_ioctx:
-            image_ioctx.metadata_set(option_name, option_value)
+            image_ioctx.metadata_set(option_name, option_value)  # type: ignore
         else:
             self._rbd.pool_metadata_set(pool_ioctx, option_name, option_value)
 
         if self._image_name:  # Name provided, so we opened it and now have to close it
-            image_ioctx.__exit__(None, None, None)
+            image_ioctx.__exit__(None, None, None)  # type: ignore
         if self._pool_name:
-            pool_ioctx.__exit__(None, None, None)
+            pool_ioctx.__exit__(None, None, None)  # type: ignore
 
     def remove(self, option_name):
         """
index 5ec4269ee1916895b64266c7045ee627bbf5e01f..daf2bb48ed364cee145dee81eb458bf7326e3584 100644 (file)
@@ -14,7 +14,7 @@ from ..tools import build_url, dict_contains_path, json_str_to_object, partial_d
 from .. import mgr
 
 try:
-    from typing import Any, Dict, List  # pylint: disable=unused-import
+    from typing import Dict, List, Optional  # pylint: disable=unused-import
 except ImportError:
     pass  # For typing only
 
@@ -179,7 +179,7 @@ def _parse_frontend_config(config):
                         return port, ssl
                 if option_name in ['endpoint', 'ssl_endpoint']:
                     match = re.search(r'([\d.]+|\[.+\])(:(\d+))?',
-                                      match.group(2))
+                                      match.group(2))  # type: ignore
                     if match:
                         port = int(match.group(3)) if \
                             match.group(2) is not None else 443 if \
@@ -187,7 +187,7 @@ def _parse_frontend_config(config):
                             80
                         ssl = option_name == 'ssl_endpoint'
                         return port, ssl
-        if match.group(1) == 'civetweb':
+        if match.group(1) == 'civetweb':  # type: ignore
             match = re.search(r'port=(.*:)?(\d+)(s)?', config)
             if match:
                 port = int(match.group(2))
@@ -202,7 +202,7 @@ class RgwClient(RestClient):
     _host = None
     _port = None
     _ssl = None
-    _user_instances = {}
+    _user_instances = {}  # type: Dict[str, RgwClient]
     _rgw_settings_snapshot = None
 
     @staticmethod
@@ -238,10 +238,10 @@ class RgwClient(RestClient):
         # Append the instance to the internal map.
         RgwClient._user_instances[RgwClient._SYSTEM_USERID] = instance
 
-    def _get_daemon_zone_info(self):  # type: () -> Dict[str, Any]
+    def _get_daemon_zone_info(self):  # type: () -> dict
         return json_str_to_object(self.proxy('GET', 'config?type=zone', None, None))
 
-    def _get_daemon_zonegroup_map(self):  # type: () -> List[Dict[str, Any]]
+    def _get_daemon_zonegroup_map(self):  # type: () -> List[dict]
         zonegroups = json_str_to_object(
             self.proxy('GET', 'config?type=zonegroup-map', None, None)
         )
@@ -264,6 +264,7 @@ class RgwClient(RestClient):
 
     @staticmethod
     def instance(userid):
+        # type: (Optional[str]) -> RgwClient
         # Discard all cached instances if any rgw setting has changed
         if RgwClient._rgw_settings_snapshot != RgwClient._rgw_settings():
             RgwClient._rgw_settings_snapshot = RgwClient._rgw_settings()
@@ -284,11 +285,11 @@ class RgwClient(RestClient):
                         userid))
 
             # Create an instance and append it to the internal map.
-            RgwClient._user_instances[userid] = RgwClient(userid,
+            RgwClient._user_instances[userid] = RgwClient(userid,  # type: ignore
                                                           keys['access_key'],
                                                           keys['secret_key'])
 
-        return RgwClient._user_instances[userid]
+        return RgwClient._user_instances[userid]  # type: ignore
 
     @staticmethod
     def admin_instance():
@@ -328,7 +329,7 @@ class RgwClient(RestClient):
         super(RgwClient, self).__init__(host, port, 'RGW', ssl, s3auth, ssl_verify=ssl_verify)
 
         # If user ID is not set, then try to get it via the RGW Admin Ops API.
-        self.userid = userid if userid else self._get_user_id(self.admin_path)
+        self.userid = userid if userid else self._get_user_id(self.admin_path)  # type: str
 
         logger.info("Created new connection for user: %s", self.userid)
 
@@ -457,7 +458,7 @@ class RgwClient(RestClient):
 
         return request(data=data)
 
-    def get_placement_targets(self):  # type: () -> Dict[str, Any]
+    def get_placement_targets(self):  # type: () -> dict
         zone = self._get_daemon_zone_info()
         # A zone without realm id can only belong to default zonegroup.
         zonegroup_name = 'default'
index 5735d85178b57f15cbd128b3e3c059fdb2dcd072..ca9a221abcbcbc4658dd97c016a8db160c68f54b 100644 (file)
@@ -15,10 +15,10 @@ from six.moves.urllib import parse
 from .. import mgr
 from ..tools import prepare_url_prefix
 
+
 if six.PY2:
     FileNotFoundError = IOError  # pylint: disable=redefined-builtin
 
-
 logger = logging.getLogger('sso')
 
 try:
@@ -91,8 +91,9 @@ class SsoDB(object):
             db.check_and_update_db()
             return db
 
-        db = json.loads(json_db)
-        return cls(db['version'], db.get('protocol'), Saml2.from_dict(db.get('saml2')))
+        dict_db = json.loads(json_db)  # type: dict
+        return cls(dict_db['version'], dict_db.get('protocol'),
+                   Saml2.from_dict(dict_db.get('saml2')))
 
 
 def load_sso_db():
index 1f3e4d8d78b4967836cf140ac912b7d9379c05c6..183cb29bb37a155093311b5c46a19de3367ec155 100644 (file)
@@ -3,6 +3,12 @@ from mgr_util import get_most_recent_rate
 from dashboard.services.ceph_service import CephService
 from .. import mgr
 
+try:
+    from typing import Dict
+except ImportError:
+    pass  # Just for type checking
+
+
 SERVICE_TYPE = 'tcmu-runner'
 
 
@@ -10,8 +16,8 @@ class TcmuService(object):
     # pylint: disable=too-many-nested-blocks
     @staticmethod
     def get_iscsi_info():
-        daemons = {}
-        images = {}
+        daemons = {}  # type: Dict[str, dict]
+        images = {}  # type: Dict[str, dict]
         for service in CephService.get_service_list(SERVICE_TYPE):
             metadata = service['metadata']
             if metadata is None:
index 1d7a46d86b5946f3983b7278a24297139ee0b093..ce730862d22015381e7548c1057377adf5e65773 100644 (file)
@@ -29,7 +29,8 @@ from .settings import Settings
 from .services.auth import JwtManager
 
 try:
-    from typing import Any, AnyStr, Dict, List  # noqa pylint: disable=unused-import
+    from typing import Any, AnyStr, Callable, DefaultDict, Deque,\
+        Dict, List, Set, Tuple, Union  # noqa pylint: disable=unused-import
 except ImportError:
     pass  # For typing only
 
@@ -251,7 +252,7 @@ class ViewCache(object):
                 rvc = ViewCache.RemoteViewCache(self.timeout)
                 self.cache_by_args[args] = rvc
             return rvc.run(fn, args, kwargs)
-        wrapper.reset = self.reset
+        wrapper.reset = self.reset  # type: ignore
         return wrapper
 
     def reset(self):
@@ -261,10 +262,10 @@ class ViewCache(object):
 
 class NotificationQueue(threading.Thread):
     _ALL_TYPES_ = '__ALL__'
-    _listeners = collections.defaultdict(set)
+    _listeners = collections.defaultdict(set)  # type: DefaultDict[str, Set[Tuple[int, Callable]]]
     _lock = threading.Lock()
     _cond = threading.Condition()
-    _queue = collections.deque()
+    _queue = collections.deque()  # type: Deque[Tuple[str, Any]]
     _running = False
     _instance = None
 
@@ -279,8 +280,8 @@ class NotificationQueue(threading.Thread):
                 return
             cls._running = True
             cls._instance = NotificationQueue()
-        cls.logger = logging.getLogger('notification_queue')
-        cls.logger.debug("starting notification queue")
+        cls.logger = logging.getLogger('notification_queue')  # type: ignore
+        cls.logger.debug("starting notification queue")  # type: ignore
         cls._instance.start()
 
     @classmethod
@@ -294,9 +295,9 @@ class NotificationQueue(threading.Thread):
             cls._running = False
         with cls._cond:
             cls._cond.notify()
-        cls.logger.debug("waiting for notification queue to finish")
+        cls.logger.debug("waiting for notification queue to finish")  # type: ignore
         instance.join()
-        cls.logger.debug("notification queue stopped")
+        cls.logger.debug("notification queue stopped")  # type: ignore
 
     @classmethod
     def _registered_handler(cls, func, n_types):
@@ -327,11 +328,14 @@ class NotificationQueue(threading.Thread):
             for ev_type in n_types:
                 if not cls._registered_handler(func, ev_type):
                     cls._listeners[ev_type].add((priority, func))
-                    cls.logger.debug("function %s was registered for events of"
-                                     " type %s", func, ev_type)
+                    cls.logger.debug(  # type: ignore
+                        "function %s was registered for events of type %s",
+                        func, ev_type
+                    )
 
     @classmethod
     def deregister(cls, func, n_types=None):
+        # type: (Callable, Union[str, list, None]) -> None
         """Removes the listener function from this notification queue
 
         If the second parameter `n_types` is omitted, the function is removed
@@ -358,11 +362,14 @@ class NotificationQueue(threading.Thread):
                         break
                 if to_remove:
                     listeners.discard(to_remove)
-                    cls.logger.debug("function %s was deregistered for events "
-                                     "of type %s", func, ev_type)
+                    cls.logger.debug(  # type: ignore
+                        "function %s was deregistered for events of type %s",
+                        func, ev_type
+                    )
 
     @classmethod
     def new_notification(cls, notify_type, notify_value):
+        # type: (str, Any) -> None
         with cls._cond:
             cls._queue.append((notify_type, notify_value))
             cls._cond.notify()
@@ -379,10 +386,10 @@ class NotificationQueue(threading.Thread):
                 listener[1](notify_value)
 
     def run(self):
-        self.logger.debug("notification queue started")
+        self.logger.debug("notification queue started")  # type: ignore
         while self._running:
             private_buffer = []
-            self.logger.debug("processing queue: %s", len(self._queue))
+            self.logger.debug("processing queue: %s", len(self._queue))  # type: ignore
             try:
                 while True:
                     private_buffer.append(self._queue.popleft())
@@ -393,10 +400,10 @@ class NotificationQueue(threading.Thread):
                 while self._running and not self._queue:
                     self._cond.wait()
         # flush remaining events
-        self.logger.debug("flush remaining events: %s", len(self._queue))
+        self.logger.debug("flush remaining events: %s", len(self._queue))  # type: ignore
         self._notify_listeners(self._queue)
         self._queue.clear()
-        self.logger.debug("notification queue finished")
+        self.logger.debug("notification queue finished")  # type: ignore
 
 
 # pylint: disable=too-many-arguments, protected-access
@@ -407,20 +414,20 @@ class TaskManager(object):
     VALUE_DONE = "done"
     VALUE_EXECUTING = "executing"
 
-    _executing_tasks = set()
-    _finished_tasks = []
+    _executing_tasks = set()  # type: Set[Task]
+    _finished_tasks = []  # type: List[Task]
     _lock = threading.Lock()
 
     _task_local_data = threading.local()
 
     @classmethod
     def init(cls):
-        cls.logger = logging.getLogger('taskmgr')
+        cls.logger = logging.getLogger('taskmgr')  # type: ignore
         NotificationQueue.register(cls._handle_finished_task, 'cd_task_finished')
 
     @classmethod
     def _handle_finished_task(cls, task):
-        cls.logger.info("finished %s", task)
+        cls.logger.info("finished %s", task)  # type: ignore
         with cls._lock:
             cls._executing_tasks.remove(task)
             cls._finished_tasks.append(task)
@@ -438,13 +445,13 @@ class TaskManager(object):
                     exception_handler)
         with cls._lock:
             if task in cls._executing_tasks:
-                cls.logger.debug("task already executing: %s", task)
+                cls.logger.debug("task already executing: %s", task)  # type: ignore
                 for t in cls._executing_tasks:
                     if t == task:
                         return t
-            cls.logger.debug("created %s", task)
+            cls.logger.debug("created %s", task)  # type: ignore
             cls._executing_tasks.add(task)
-        cls.logger.info("running %s", task)
+        cls.logger.info("running %s", task)  # type: ignore
         task._run()
         return task
 
@@ -522,7 +529,7 @@ class TaskExecutor(object):
     def start(self):
         self.logger.debug("executing task %s", self.task)
         try:
-            self.task.fn(*self.task.fn_args, **self.task.fn_kwargs)
+            self.task.fn(*self.task.fn_args, **self.task.fn_kwargs)  # type: ignore
         except Exception as ex:
             self.logger.exception("Error while calling %s", self.task)
             self.finish(None, ex)
@@ -532,7 +539,7 @@ class TaskExecutor(object):
             self.logger.debug("successfully finished task: %s", self.task)
         else:
             self.logger.debug("task finished with exception: %s", self.task)
-        self.task._complete(ret_value, exception)
+        self.task._complete(ret_value, exception)  # type: ignore
 
 
 # pylint: disable=protected-access
@@ -549,7 +556,7 @@ class ThreadedExecutor(TaskExecutor):
         TaskManager._task_local_data.task = self.task
         try:
             self.logger.debug("executing task %s", self.task)
-            val = self.task.fn(*self.task.fn_args, **self.task.fn_kwargs)
+            val = self.task.fn(*self.task.fn_args, **self.task.fn_kwargs)  # type: ignore
         except Exception as ex:
             self.logger.exception("Error while calling %s", self.task)
             self.finish(None, ex)
@@ -614,7 +621,7 @@ class Task(object):
             self.end_time = now
             self.ret_value = ret_value
             self.exception = exception
-            self.duration = now - self.begin_time
+            self.duration = now - self.begin_time  # type: ignore
             if not self.exception:
                 self.set_progress(100, True)
         NotificationQueue.new_notification('cd_task_finished', self)
@@ -648,7 +655,7 @@ class Task(object):
             raise Exception("Progress delta value must be a positive integer")
         if not in_lock:
             self.lock.acquire()
-        prog = self.progress + delta
+        prog = self.progress + delta  # type: ignore
         self.progress = prog if prog <= 100 else 100
         if not in_lock:
             self.lock.release()
@@ -796,7 +803,7 @@ def json_str_to_object(value):  # type: (AnyStr) -> Any
 
     try:
         # json.loads accepts binary input from version >=3.6
-        value = value.decode('utf-8')
+        value = value.decode('utf-8')  # type: ignore
     except AttributeError:
         pass
 
@@ -821,7 +828,7 @@ def get_request_body_params(request):
     :return: A dictionary containing the parameters.
     :rtype: dict
     """
-    params = {}
+    params = {}  # type: dict
     if request.method not in request.methods_with_bodies:
         return params
 
index 23b0573220b4a4a866b734254d2c1c49139a3707..4e365b441c4c0f9d1d30736de8a7cd20d06633e3 100644 (file)
@@ -15,6 +15,7 @@ deps =
 commands = mypy --config-file=../../mypy.ini \
            cephadm/module.py \
            mgr_module.py \
+           dashboard/module.py \
            mgr_util.py \
            orchestrator/__init__.py \
            progress/module.py \