]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/alerts/module.py
import quincy beta 17.1.0
[ceph.git] / ceph / src / pybind / mgr / alerts / module.py
index e0b6cbcaa425ce1933f5927c03652e34a1901ddc..296bcf9381da714a686478cc2c52979ea2563129 100644 (file)
@@ -3,87 +3,71 @@
 A simple cluster health alerting module.
 """
 
-from mgr_module import MgrModule, HandleCommandResult
+from mgr_module import CLIReadCommand, HandleCommandResult, MgrModule, Option
 from threading import Event
-import errno
+from typing import Any, Optional, Dict, List, TYPE_CHECKING, Union
 import json
 import smtplib
 
-class Alerts(MgrModule):
-    COMMANDS = [
-        {
-            "cmd": "alerts send",
-            "desc": "(re)send alerts immediately",
-            "perm": "r"
-        },
-    ]
 
+class Alerts(MgrModule):
     MODULE_OPTIONS = [
-        {
-            'name': 'interval',
-            'type': 'secs',
-            'default': 60,
-            'desc': 'How frequently to reexamine health status',
-            'runtime': True,
-        },
+        Option(
+            name='interval',
+            type='secs',
+            default=60,
+            desc='How frequently to reexamine health status',
+            runtime=True),
         # smtp
-        {
-            'name': 'smtp_host',
-            'default': '',
-            'desc': 'SMTP server',
-            'runtime': True,
-        },
-        {
-            'name': 'smtp_destination',
-            'default': '',
-            'desc': 'Email address to send alerts to',
-            'runtime': True,
-        },
-        {
-            'name': 'smtp_port',
-            'type': 'int',
-            'default': 465,
-            'desc': 'SMTP port',
-            'runtime': True,
-        },
-        {
-            'name': 'smtp_ssl',
-            'type': 'bool',
-            'default': True,
-            'desc': 'Use SSL to connect to SMTP server',
-            'runtime': True,
-        },
-        {
-            'name': 'smtp_user',
-            'default': '',
-            'desc': 'User to authenticate as',
-            'runtime': True,
-        },
-        {
-            'name': 'smtp_password',
-            'default': '',
-            'desc': 'Password to authenticate with',
-            'runtime': True,
-        },
-        {
-            'name': 'smtp_sender',
-            'default': '',
-            'desc': 'SMTP envelope sender',
-            'runtime': True,
-        },
-        {
-            'name': 'smtp_from_name',
-            'default': 'Ceph',
-            'desc': 'Email From: name',
-            'runtime': True,
-        },
+        Option(
+            name='smtp_host',
+            default='',
+            desc='SMTP server',
+            runtime=True),
+        Option(
+            name='smtp_destination',
+            default='',
+            desc='Email address to send alerts to',
+            runtime=True),
+        Option(
+            name='smtp_port',
+            type='int',
+            default=465,
+            desc='SMTP port',
+            runtime=True),
+        Option(
+            name='smtp_ssl',
+            type='bool',
+            default=True,
+            desc='Use SSL to connect to SMTP server',
+            runtime=True),
+        Option(
+            name='smtp_user',
+            default='',
+            desc='User to authenticate as',
+            runtime=True),
+        Option(
+            name='smtp_password',
+            default='',
+            desc='Password to authenticate with',
+            runtime=True),
+        Option(
+            name='smtp_sender',
+            default='',
+            desc='SMTP envelope sender',
+            runtime=True),
+        Option(
+            name='smtp_from_name',
+            default='Ceph',
+            desc='Email From: name',
+            runtime=True)
     ]
 
     # These are "native" Ceph options that this module cares about.
-    NATIVE_OPTIONS = [
+    NATIVE_OPTIONS: List[str] = [
     ]
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
         super(Alerts, self).__init__(*args, **kwargs)
 
         # set up some members to enable the serve() method and shutdown()
@@ -95,8 +79,18 @@ class Alerts(MgrModule):
 
         self.log.info("Init")
 
-
-    def config_notify(self):
+        if TYPE_CHECKING:
+            self.interval = 60
+            self.smtp_host = ''
+            self.smtp_destination = ''
+            self.smtp_port = 0
+            self.smtp_ssl = True
+            self.smtp_user = ''
+            self.smtp_password = ''
+            self.smtp_sender = ''
+            self.smtp_from_name = ''
+
+    def config_notify(self) -> None:
         """
         This method is called whenever one of our config options is changed.
         """
@@ -116,28 +110,25 @@ class Alerts(MgrModule):
                     self.get_ceph_option(opt))
             self.log.debug(' native option %s = %s', opt, getattr(self, opt))
 
-    def handle_command(self, inbuf, cmd):
-        ret = 0
-        out = ''
-        err = ''
-        if cmd['prefix'] == 'alerts send':
-            status = json.loads(self.get('health')['json'])
-            self._send_alert(status, {})
-        return HandleCommandResult(
-            retval=ret,   # exit code
-            stdout=out,   # stdout
-            stderr=err)
+    @CLIReadCommand('alerts send')
+    def send(self) -> HandleCommandResult:
+        """
+        (re)send alerts immediately
+        """
+        status = json.loads(self.get('health')['json'])
+        self._send_alert(status, {})
+        return HandleCommandResult()
 
-    def _diff(self, last, new):
-        d = {}
+    def _diff(self, last: Dict[str, Any], new: Dict[str, Any]) -> Dict[str, Any]:
+        d: Dict[str, Any] = {}
         for code, alert in new.get('checks', {}).items():
             self.log.debug('new code %s alert %s' % (code, alert))
             if code not in last.get('checks', {}):
                 if 'new' not in d:
                     d['new'] = {}
                 d['new'][code] = alert
-            elif alert['summary'].get('count', 0) > \
-                 last['checks'][code]['summary'].get('count', 0):
+            elif (alert['summary'].get('count', 0)
+                  > last['checks'][code]['summary'].get('count', 0)):
                 if 'updated' not in d:
                     d['updated'] = {}
                 d['updated'][code] = alert
@@ -149,7 +140,7 @@ class Alerts(MgrModule):
                 d['cleared'][code] = alert
         return d
 
-    def _send_alert(self, status, diff):
+    def _send_alert(self, status: Dict[str, Any], diff: Dict[str, Any]) -> None:
         checks = {}
         if self.smtp_host:
             r = self._send_alert_smtp(status, diff)
@@ -160,13 +151,13 @@ class Alerts(MgrModule):
             self.log.warning('Alert is not sent because smtp_host is not configured')
         self.set_health_checks(checks)
 
-    def serve(self):
+    def serve(self) -> None:
         """
         This method is called by the mgr when the module starts and can be
         used for any background activity.
         """
         self.log.info("Starting")
-        last_status = {}
+        last_status: Dict[str, Any] = {}
         while self.run:
             # Do some useful background work here.
             new_status = json.loads(self.get('health')['json'])
@@ -180,11 +171,11 @@ class Alerts(MgrModule):
                     self._send_alert(new_status, diff)
                 last_status = new_status
 
-            self.log.debug('Sleeping for %d seconds', self.interval)
-            ret = self.event.wait(self.interval)
+            self.log.debug('Sleeping for %s seconds', self.interval)
+            self.event.wait(self.interval or 60)
             self.event.clear()
 
-    def shutdown(self):
+    def shutdown(self) -> None:
         """
         This method is called by the mgr when the module needs to shut
         down (i.e., when the serve() function needs to exit).
@@ -194,7 +185,7 @@ class Alerts(MgrModule):
         self.event.set()
 
     # SMTP
-    def _smtp_format_alert(self, code, alert):
+    def _smtp_format_alert(self, code: str, alert: Dict[str, Any]) -> str:
         r = '[{sev}] {code}: {summary}\n'.format(
             code=code,
             sev=alert['severity'].split('_')[1],
@@ -204,7 +195,9 @@ class Alerts(MgrModule):
                 message=detail['message'])
         return r
 
-    def _send_alert_smtp(self, status, diff):
+    def _send_alert_smtp(self,
+                         status: Dict[str, Any],
+                         diff: Dict[str, Any]) -> Optional[Dict[str, Any]]:
         # message
         self.log.debug('_send_alert_smtp')
         message = ('From: {from_name} <{sender}>\n'
@@ -233,13 +226,14 @@ class Alerts(MgrModule):
         message += ('\n\n=== Full health status ===\n')
         for code, alert in status['checks'].items():
             message += self._smtp_format_alert(code, alert)
-            
+
         self.log.debug('message: %s' % message)
 
         # send
         try:
             if self.smtp_ssl:
-                server = smtplib.SMTP_SSL(self.smtp_host, self.smtp_port)
+                server: Union[smtplib.SMTP_SSL, smtplib.SMTP] = \
+                    smtplib.SMTP_SSL(self.smtp_host, self.smtp_port)
             else:
                 server = smtplib.SMTP(self.smtp_host, self.smtp_port)
             if self.smtp_password:
@@ -252,7 +246,7 @@ class Alerts(MgrModule):
                     'severity': 'warning',
                     'summary': 'unable to send alert email',
                     'count': 1,
-                    'detail': [ str(e) ]
+                    'detail': [str(e)]
                 }
             }
         self.log.debug('Sent email to %s' % self.smtp_destination)