From: Kefu Chai Date: Wed, 27 Jan 2021 04:08:11 +0000 (+0800) Subject: mgr/devicehealth: add more annotations X-Git-Tag: v16.2.0~152^2~10 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=09284e9b8ef523a19f6144fc61e4f1a97a423335;p=ceph.git mgr/devicehealth: add more annotations Signed-off-by: Kefu Chai (cherry picked from commit 48438cccedb383e19cab743dd0222d6ec6265588) --- diff --git a/src/mypy.ini b/src/mypy.ini index b49bf0c1d5d2..a217501b0a1d 100755 --- a/src/mypy.ini +++ b/src/mypy.ini @@ -31,6 +31,9 @@ disallow_untyped_defs = True [mypy-cephadm.*] disallow_untyped_defs = True +[mypy-devicehealth.*] +disallow_untyped_defs = True + [mypy-orchestrator.*] disallow_untyped_defs = True diff --git a/src/pybind/mgr/devicehealth/module.py b/src/pybind/mgr/devicehealth/module.py index 5009820b6150..052ccaf79bdf 100644 --- a/src/pybind/mgr/devicehealth/module.py +++ b/src/pybind/mgr/devicehealth/module.py @@ -9,7 +9,7 @@ import operator import rados from threading import Event from datetime import datetime, timedelta -from typing import Optional +from typing import cast, Any, Dict, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union TIME_FORMAT = '%Y%m%d-%H%M%S' @@ -85,7 +85,7 @@ class Module(MgrModule): ), ] - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super(Module, self).__init__(*args, **kwargs) # populate options (just until serve() runs) @@ -97,7 +97,19 @@ class Module(MgrModule): self.event = Event() self.has_device_pool = False - def is_valid_daemon_name(self, who): + # for mypy which does not run the code + if TYPE_CHECKING: + self.enable_monitoring = True + self.scrape_frequency = 0.0 + self.pool_name = '' + self.device_health_metrics = '' + self.retention_period = 0.0 + self.mark_out_threshold = 0.0 + self.warn_threshold = 0.0 + self.self_heal = True + self.sleep_interval = 0.0 + + def is_valid_daemon_name(self, who: str) -> bool: parts = who.split('.') if len(parts) != 2: return False @@ -108,7 +120,7 @@ class Module(MgrModule): @CLICommand('device query-daemon-health-metrics', perm='r') - def do_query_daemon_health_metrics(self, who: str): + def do_query_daemon_health_metrics(self, who: str) -> Tuple[int, str, str]: ''' Get device health metrics for a given daemon ''' @@ -124,7 +136,7 @@ class Module(MgrModule): @CLICommand('device scrape-daemon-health-metrics', perm='r') - def do_scrape_daemon_health_metrics(self, who: str): + def do_scrape_daemon_health_metrics(self, who: str) -> Tuple[int, str, str]: ''' Scrape and store device health metrics for a given daemon ''' @@ -135,7 +147,7 @@ class Module(MgrModule): @CLICommand('device scrape-daemon-health-metrics', perm='r') - def do_scrape_health_metrics(self, devid: Optional[str] = None): + def do_scrape_health_metrics(self, devid: Optional[str] = None) -> Tuple[int, str, str]: ''' Scrape and store device health metrics ''' @@ -146,7 +158,7 @@ class Module(MgrModule): @CLICommand('device get-health-metrics', perm='r') - def do_get_health_metrics(self, devid: str, sample: Optional[str] = None): + def do_get_health_metrics(self, devid: str, sample: Optional[str] = None) -> Tuple[int, str, str]: ''' Show stored device metrics for the device ''' @@ -154,7 +166,7 @@ class Module(MgrModule): @CLICommand('device check-health', perm='rw') - def do_check_health(self): + def do_check_health(self) -> Tuple[int, str, str]: ''' Check life expectancy of devices ''' @@ -162,7 +174,7 @@ class Module(MgrModule): @CLICommand('device monitoring on', perm='rw') - def do_monitoring_on(self): + def do_monitoring_on(self) -> Tuple[int, str, str]: ''' Enable device health monitoring ''' @@ -172,7 +184,7 @@ class Module(MgrModule): @CLICommand('device monitoring off', perm='rw') - def do_monitoring_off(self): + def do_monitoring_off(self) -> Tuple[int, str, str]: ''' Disable device health monitoring ''' @@ -182,13 +194,13 @@ class Module(MgrModule): @CLICommand('device predict-life-expectancy', perm='r') - def do_predict_life_expectancy(self, devid: str): + def do_predict_life_expectancy(self, devid: str) -> Tuple[int, str, str]: ''' Predict life expectancy with local predictor ''' return self.predict_lift_expectancy(devid) - def self_test(self): + def self_test(self) -> None: self.config_notify() osdmap = self.get('osd_map') osd_id = osdmap['osds'][0]['osd'] @@ -204,7 +216,7 @@ class Module(MgrModule): assert r == 0 assert before != after - def config_notify(self): + def config_notify(self) -> None: for opt in self.MODULE_OPTIONS: setattr(self, opt['name'], @@ -223,7 +235,7 @@ class Module(MgrModule): if osd["up"]: up += 1 - need = int(self.get_ceph_option("osd_pool_default_size")) + need = cast(int, self.get_ceph_option("osd_pool_default_size")) return up >= need def maybe_create_device_pool(self) -> bool: @@ -235,7 +247,7 @@ class Module(MgrModule): self.has_device_pool = True return True - def create_device_pool(self): + def create_device_pool(self) -> None: self.log.debug('create %s pool' % self.pool_name) # create pool result = CommandResult('') @@ -259,7 +271,7 @@ class Module(MgrModule): r, outb, outs = result.wait() assert r == 0 - def serve(self): + def serve(self) -> None: self.log.info("Starting") self.config_notify() @@ -306,7 +318,7 @@ class Module(MgrModule): ret = self.event.wait(sleep_interval) self.event.clear() - def shutdown(self): + def shutdown(self) -> None: self.log.info('Stopping') self.run = False self.event.set() @@ -318,7 +330,7 @@ class Module(MgrModule): ioctx = self.rados.open_ioctx(self.pool_name) return ioctx - def scrape_daemon(self, daemon_type, daemon_id): + def scrape_daemon(self, daemon_type: str, daemon_id: str) -> Tuple[int, str, str]: ioctx = self.open_connection() if not ioctx: return -errno.EAGAIN, "", "device_health_metrics pool not yet available" @@ -331,7 +343,7 @@ class Module(MgrModule): ioctx.close() return 0, "", "" - def scrape_all(self): + def scrape_all(self) -> Tuple[int, str, str]: osdmap = self.get("osd_map") assert osdmap is not None ioctx = self.open_connection() @@ -359,7 +371,7 @@ class Module(MgrModule): ioctx.close() return 0, "", "" - def scrape_device(self, devid): + def scrape_device(self, devid: str) -> Tuple[int, str, str]: r = self.get("device " + devid) if not r or 'device' not in r.keys(): return -errno.ENOENT, '', 'device ' + devid + ' not found' @@ -381,7 +393,10 @@ class Module(MgrModule): ioctx.close() return 0, "", "" - def do_scrape_daemon(self, daemon_type, daemon_id, devid=''): + def do_scrape_daemon(self, + daemon_type: str, + daemon_id: str, + devid: str = '') -> Optional[Dict[str, Any]]: """ :return: a dict, or None if the scrape failed. """ @@ -400,8 +415,9 @@ class Module(MgrModule): self.log.error( "Fail to parse JSON result from daemon {0}.{1} ({2})".format( daemon_type, daemon_id, outb)) + return None - def put_device_metrics(self, ioctx, devid, data): + def put_device_metrics(self, ioctx: rados.Ioctx, devid: str, data: Any) -> None: assert devid old_key = datetime.utcnow() - timedelta( seconds=self.retention_period) @@ -437,7 +453,9 @@ class Module(MgrModule): ioctx.remove_omap_keys(op, tuple(erase)) ioctx.operate_write_op(op, devid) - def _get_device_metrics(self, devid, sample=None, min_sample=None): + def _get_device_metrics(self, devid: str, + sample: Optional[str] = None, + min_sample: Optional[str] = None) -> Dict[str, Dict[str, Any]]: res = {} ioctx = self.open_connection(create_if_missing=False) if not ioctx: @@ -468,7 +486,7 @@ class Module(MgrModule): raise return res - def show_device_metrics(self, devid, sample): + def show_device_metrics(self, devid: str, sample: Optional[str]) -> Tuple[int, str, str]: # verify device exists r = self.get("device " + devid) if not r or 'device' not in r.keys(): @@ -477,14 +495,14 @@ class Module(MgrModule): res = self._get_device_metrics(devid, sample=sample) return 0, json.dumps(res, indent=4, sort_keys=True), '' - def check_health(self): + def check_health(self) -> Tuple[int, str, str]: self.log.info('Check health') config = self.get('config') min_in_ratio = float(config.get('mon_osd_min_in_ratio')) mark_out_threshold_td = timedelta(seconds=self.mark_out_threshold) warn_threshold_td = timedelta(seconds=self.warn_threshold) - checks = {} - health_warnings = { + checks: Dict[str, Dict[str, Union[int, str, Sequence[str]]]] = {} + health_warnings: Dict[str, List[str]] = { DEVICE_HEALTH: [], DEVICE_HEALTH_IN_USE: [], } @@ -586,21 +604,21 @@ class Module(MgrModule): self.set_health_checks(checks) return 0, "", "" - def is_osd_in(self, osdmap, osd_id): + def is_osd_in(self, osdmap: Dict[str, Any], osd_id: str) -> bool: for osd in osdmap['osds']: - if str(osd_id) == str(osd['osd']): + if osd_id == str(osd['osd']): return bool(osd['in']) return False - def get_osd_num_pgs(self, osd_id): + def get_osd_num_pgs(self, osd_id: str) -> int: stats = self.get('osd_stats') assert stats is not None for stat in stats['osd_stats']: - if str(osd_id) == str(stat['osd']): + if osd_id == str(stat['osd']): return stat['num_pgs'] return -1 - def mark_out_etc(self, osd_ids): + def mark_out_etc(self, osd_ids: List[str]) -> None: self.log.info('Marking out OSDs: %s' % osd_ids) result = CommandResult('') self.send_command(result, 'mon', '', json.dumps({ @@ -626,15 +644,15 @@ class Module(MgrModule): 'r: [%s], outb: [%s], outs: [%s]', osd_id, r, outb, outs) - def extract_smart_features(self, raw): + def extract_smart_features(self, raw: Any) -> Any: # FIXME: extract and normalize raw smartctl --json output and # generate a dict of the fields we care about. return raw - def predict_lift_expectancy(self, devid): + def predict_lift_expectancy(self, devid: str) -> Tuple[int, str, str]: plugin_name = '' model = self.get_ceph_option('device_failure_prediction_mode') - if model and model.lower() == 'local': + if cast(str, model).lower() == 'local': plugin_name = 'diskprediction_local' else: return -1, '', 'unable to enable any disk prediction model[local/cloud]' @@ -645,10 +663,10 @@ class Module(MgrModule): except: return -1, '', 'unable to invoke diskprediction local or remote plugin' - def predict_all_devices(self): + def predict_all_devices(self) -> Tuple[int, str, str]: plugin_name = '' model = self.get_ceph_option('device_failure_prediction_mode') - if model and model.lower() == 'local': + if cast(str, model).lower() == 'local': plugin_name = 'diskprediction_local' else: return -1, '', 'unable to enable any disk prediction model[local/cloud]' @@ -659,8 +677,8 @@ class Module(MgrModule): except: return -1, '', 'unable to invoke diskprediction local or remote plugin' - def get_recent_device_metrics(self, devid, min_sample): + def get_recent_device_metrics(self, devid: str, min_sample: str) -> Dict[str, Dict[str, Any]]: return self._get_device_metrics(devid, min_sample=min_sample) - def get_time_format(self): + def get_time_format(self) -> str: return TIME_FORMAT diff --git a/src/pybind/mgr/tox.ini b/src/pybind/mgr/tox.ini index 0dcd002873c1..6e96ec630488 100644 --- a/src/pybind/mgr/tox.ini +++ b/src/pybind/mgr/tox.ini @@ -62,6 +62,7 @@ commands = mypy --config-file=../../mypy.ini \ -m cephadm \ -m dashboard \ + -m devicehealth \ -m mds_autoscaler \ -m mgr_module \ -m mgr_util \