async def get_metadata(dbcon: DBConnection, object_type: str, object_id: int) -> Dict[str, str]:
async def add_metadata(dbcon: DBConnection, object_type: str, object_id: int, metadict: Dict[str, str]):
    async def _run(cur: Cursor) -> None:
async def update_metadata(dbcon: DBConnection, object_type: str, object_id: int, metadict: Dict[str, str]):
    async def _run(cur: Cursor) -> None:
async def delete_metadata(dbcon: DBConnection, object_type: str, object_id: int,
                          keys: Optional[Iterable[str]] = None):
    async def _run(cur: Cursor) -> None:
async def get_metadata_for_object(
        dbcon: DBConnection, object_type: str, object_id: int) -> Iterable[object_models.ObjectMetadata]:
async def get_metadata_for_object_type(
        dbcon: DBConnection, object_type: str) -> Iterable[object_models.ObjectMetadata]:
async def get_metadata_for_object_metadata(
        dbcon: DBConnection, metadata_key: str, metadata_value: str,
        object_type: str, object_table: str) -> Iterable[object_models.ObjectMetadata]: async def get_bindata(dbcon: DBConnection, object_type: str, object_id: int, key: str) -> bytes:
async def set_bindata(dbcon: DBConnection, object_type: str, object_id: int, key: str, value: bytes):
async def delete_bindata(dbcon: DBConnection, object_type: str, object_id: int, key: str): def get_section(section: Optional[str]) -> Dict[str, float]:
def set(var: str, value: float, section: Optional[str] = None) -> None:
def inc(var: str, section: Optional[str] = None) -> None:
def dec(var: str, section: Optional[str] = None) -> None:
def get_stats() -> Dict[str, float]: def parse_bool(string: Union[str, bool]) -> bool:
def get_display_time(seconds: float, granularity: int = 2) -> str: async def create_contact(dbcon: DBConnection, name: Optional[str], email: Optional[str],
                         phone: Optional[str], active: bool) -> str:
async def update_contact(dbcon: DBConnection, contact_id: int, data: Dict[str, str]) -> None:
    async def _run(cur: Cursor) -> None:
async def delete_contact(dbcon: DBConnection, contact_id: int) -> None:
async def create_contact_group(dbcon: DBConnection, name: str, active: bool) -> str:
async def update_contact_group(dbcon: DBConnection, contact_group_id: int, data: Dict[str, str]) -> None:
    async def _run(cur: Cursor) -> None:
async def delete_contact_group(dbcon: DBConnection, contact_group_id: int) -> None:
async def get_all_contacts_for_active_monitor(dbcon: DBConnection, monitor_id: int) -> Iterable[object_models.Contact]:
async def _active_monitor_contacts(dbcon: DBConnection, monitor_id: int) -> Set[object_models.Contact]:
async def _active_monitor_contact_groups(dbcon: DBConnection, monitor_id: int) -> Set[object_models.Contact]:
async def _active_monitor_monitor_group_contacts(dbcon: DBConnection, monitor_id: int) -> Set[object_models.Contact]:
async def _active_monitor_monitor_group_contact_groups(
        dbcon: DBConnection, monitor_id: int) -> Set[object_models.Contact]:
async def get_contact_dict_for_active_monitor(dbcon: DBConnection, monitor_id: int) -> Dict[str, set]:
async def add_contact_to_active_monitor(dbcon: DBConnection, contact_id: int, monitor_id: int) -> None:
async def delete_contact_from_active_monitor(dbcon: DBConnection, contact_id: int, monitor_id: int) -> None:
async def set_active_monitor_contacts(dbcon: DBConnection,
                                      contact_ids: Iterable[int], monitor_id: int):
    async def _run(cur: Cursor) -> None:
async def get_contacts_for_active_monitor(dbcon: DBConnection, monitor_id: int) -> Iterable[object_models.Contact]:
async def add_contact_group_to_active_monitor(dbcon: DBConnection, contact_group_id: int, monitor_id: int) -> None:
async def delete_contact_group_from_active_monitor(dbcon: DBConnection, contact_group_id: int, monitor_id: int) -> None:
async def set_active_monitor_contact_groups(dbcon: DBConnection,
                                            contact_group_ids: Iterable[int], monitor_id: int) -> None:
    async def _run(cur: Cursor) -> None:
async def get_contact_groups_for_active_monitor(
        dbcon: DBConnection, monitor_id: int) -> Iterable[object_models.ContactGroup]:
async def get_all_contacts(dbcon: DBConnection) -> Iterable[object_models.Contact]:
async def get_contact(dbcon: DBConnection, id: int) -> Any:  # Use any because optional returns suck.
async def get_contacts_for_metadata(
        dbcon: DBConnection, meta_key: str, meta_value: str) -> Iterable[object_models.Contact]:
async def add_contact_to_contact_group(dbcon: DBConnection, contact_group_id: int, contact_id: int) -> None:
async def delete_contact_from_contact_group(dbcon: DBConnection, contact_group_id: int, contact_id: int) -> None:
async def set_contact_group_contacts(dbcon: DBConnection,
                                     contact_group_id: int, contact_ids: Iterable[int]) -> None:
    async def _run(cur: Cursor) -> None:
async def get_contacts_for_contact_group(dbcon: DBConnection, contact_group_id: int) -> Iterable[object_models.Contact]:
async def get_all_contact_groups(dbcon: DBConnection) -> Iterable[object_models.ContactGroup]:
async def get_contact_group(dbcon: DBConnection, id: int) -> Any:  # Use any because optional returns suck.
async def get_contact_groups_for_metadata(
        dbcon: DBConnection, meta_key: str, meta_value: str) -> Iterable[object_models.ContactGroup]: async def create_monitor_group(dbcon: DBConnection, parent_id: Optional[int], name: str):
async def update_monitor_group(dbcon: DBConnection, monitor_group_id: int, data: Dict[str, Any]):
    async def _run(cur: Cursor) -> None:
async def delete_monitor_group(dbcon: DBConnection, monitor_group_id: int) -> None:
    async def _run(cur: Cursor) -> None:
async def add_active_monitor_to_monitor_group(dbcon: DBConnection, monitor_group_id: int, monitor_id: int) -> None:
async def delete_active_monitor_from_monitor_group(dbcon: DBConnection, monitor_group_id: int, monitor_id: int) -> None:
async def add_contact_to_monitor_group(dbcon: DBConnection, monitor_group_id: int, contact_id: int) -> None:
async def delete_contact_from_monitor_group(dbcon: DBConnection, monitor_group_id: int, contact_id: int) -> None:
async def add_contact_group_to_monitor_group(dbcon: DBConnection, monitor_group_id: int, contact_group_id: int) -> None:
async def delete_contact_group_from_monitor_group(
        dbcon: DBConnection, monitor_group_id: int, contact_group_id: int) -> None:
async def get_all_monitor_groups(dbcon: DBConnection) -> Iterable[object_models.MonitorGroup]:
async def get_monitor_group(dbcon: DBConnection, id: int) -> Any:  # Use any because optional returns suck.
async def get_contacts_for_monitor_group(dbcon: DBConnection, id: int) -> Iterable[object_models.Contact]:
async def get_contact_groups_for_monitor_group(dbcon: DBConnection, id: int) -> Iterable[object_models.ContactGroup]:
async def get_active_monitors_for_monitor_group(dbcon: DBConnection, id: int) -> Iterable[object_models.ActiveMonitor]:
async def get_monitor_groups_for_metadata(
        dbcon: DBConnection, meta_key: str, meta_value: str) -> Iterable[object_models.MonitorGroup]:
async def get_active_monitor_metadata_for_monitor_group(
        dbcon: DBConnection, id: int) -> Iterable[object_models.ObjectMetadata]: def insert_values(object: Any) -> Tuple:
def list_asdict(in_list: Iterable[Any]) -> List[Any]:
def insert_filter(attribute: Any, value: Any) -> bool:
class Contact:
class ContactGroup:
class ActiveMonitor:
class ActiveMonitorArg:
class ActiveMonitorAlert:
class ActiveMonitorDef:
class ActiveMonitorDefArg:
class ObjectMetadata:
class ObjectBindata:
class MonitorGroup: class WebAPIError(Exception):
class InvalidData(WebAPIError):
class PermissionDenied(WebAPIError):
class NotFound(WebAPIError): class ActiveMonitor(log.LoggingMixin):
    monitor_type = 'active'

    def __init__(self, id: int, args: Dict[str, str], monitor_def: ActiveMonitorDef, state: str, state_ts: float,
                 msg: str, alert_id: Union[int, None], checks_enabled: bool,
                 alerts_enabled: bool, manager: ActiveMonitorManager) -> None:
        self.id = id
        self.args = args
        self.monitor_def = monitor_def
        self.state = state
        self.manager = manager
        self.last_check_state = None  # type: Optional[str]
        self.consecutive_checks = 0
        self.last_check = time.time()
        self.msg = msg
        self.alert_id = alert_id
        self.state_ts = state_ts
        if not self.state_ts:
            self.state_ts = time.time()
        self.monitoring = False
        self.deleted = False
        self.checks_enabled = checks_enabled
        self.alerts_enabled = alerts_enabled
        self._pending_reset = False
        self.scheduled_job = None  # type: Optional[asyncio.Handle]
        self.scheduled_job_ts = 0.0
        event.running('CREATE_ACTIVE_MONITOR', monitor=self)
        stats.inc('num_monitors', 'ACT_MON')

    def __str__(self) -> str:
        return '<ActiveMonitor(%s/%s/%s)>' % (self.id, self.state, self.last_check_state)

    def get_description(self) -> str:
        """Get a description for this monitor.

        The description is created from a template in the monitor defintion.
        """
        ret = self.monitor_def.tmpl_cache.get(self, 'description')
        if not ret:
            ret = self.monitor_def.tmpl_cache.set(
                self, 'description', self.monitor_def.expand_monitor_description(self.args))
        return ret

    def get_expanded_args(self) -> List[str]:
        """Get the expanded service check arguments for this monitor.

        The arguments are created from a template in the monitor definition
        """
        ret = self.monitor_def.tmpl_cache.get(self, 'args')
        if not ret:
            ret = self.monitor_def.tmpl_cache.set(
                self, 'args', self.monitor_def.expand_monitor_args(self.args))
        return ret

    async def run(self) -> bool:
        if self.deleted or self.monitoring:
            return False
        self.monitoring = True
        self.last_check = time.time()
        try:
            await self._run()
        except:
            self.monitoring = False
            raise
        self.monitoring = False
        return True

    async def _run(self) -> None:
        if self._pending_reset:
            await self.reset_monitor()
        if not self.checks_enabled:
            self.log_debug('skipping monitor check, disabled')
            self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL)
            return
        expanded_args = self.get_expanded_args()
        self.log_debug('monitoring: %s %s' % (self.monitor_def.cmdline_filename, expanded_args))
        event.running('RUN_ACTIVE_MONITOR', monitor=self)
        # noinspection PyUnusedLocal
        msg = ''  # type: Union[str, bytes]
        try:
            _msg = await nagios.run_plugin(self.monitor_def.cmdline_filename, expanded_args, 30)
            msg, perf = _msg
            check_state = 'UP'
        except nagios.MonitorFailedError as e:
            msg = e.args[0]
            self.log_debug('monitoring failed: %s' % msg)
            check_state = 'DOWN'
        except nagios.NagiosError as e:
            self.log_debug('monitoring unknown error: %s' % (str(e)))
            check_state = 'UNKNOWN'
            msg = str(e)
        msg = msg[:199]  # Set a reasonable max length for stored monitor messages.
        if type(msg) == bytes:
            msg = cast(bytes, msg)
            msg = msg.decode('utf-8', errors='ignore')
        msg = cast(str, msg)
        self.msg = msg
        self.update_consecutive_checks(check_state)
        await self.handle_check_result(check_state, msg)
        self.log_debug('monitoring complete')
        if self.deleted:
            await self._purge()

    async def handle_check_result(self, check_state: str, msg: str) -> None:
        if check_state == 'UP' and self.state == 'UP':
            # Introduce a slight variation in monitoring intervals when
            # everything is going ok for a monitor. This will help spread
            # the service check times out.
            self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL + random.randint(-5, 5))
            stats.inc('checks_up', 'ACT_MON')
        elif check_state == 'UP' and self.state != 'UP':
            self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL)
            await self.state_change('UP', msg)
            stats.inc('checks_up', 'ACT_MON')
        elif check_state == 'DOWN' and self.state == 'DOWN':
            self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL)
            stats.inc('checks_down', 'ACT_MON')
        elif check_state == 'DOWN' and self.state == 'UNKNOWN':
            await self.state_change('DOWN', msg)
            self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL)
            stats.inc('checks_down', 'ACT_MON')
        elif check_state == 'DOWN' and self.state != 'DOWN':
            if self.consecutive_checks >= DOWN_THRESHOLD:
                await self.state_change('DOWN', msg)
                self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL)
            else:
                self.manager.schedule_monitor(self, 30)
            stats.inc('checks_down', 'ACT_MON')
        elif check_state == 'UNKNOWN' and self.state == 'UNKNOWN':
            self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL)
            stats.inc('checks_unknown', 'ACT_MON')
        elif check_state == 'UNKNOWN' and self.state != 'UNKNOWN':
            if self.consecutive_checks >= UNKNOWN_THRESHOLD:
                await self.state_change('UNKNOWN', msg)
                self.manager.schedule_monitor(self, DEFAULT_MONITOR_INTERVAL)
            else:
                self.manager.schedule_monitor(self, 120)
            stats.inc('checks_unknown', 'ACT_MON')
        event.running('ACTIVE_MONITOR_CHECK_RESULT', monitor=self, check_state=check_state, msg=msg)

    async def _set_monitor_checks_disabled(self) -> None:
        self.state = 'UNKNOWN'
        self.state_ts = int(time.time())
        self.msg = ''

    async def state_change(self, new_state: str, msg: str) -> None:
        event.running('ACTIVE_MONITOR_STATE_CHANGE', monitor=self, new_state=new_state)
        prev_state = self.state
        prev_state_ts = self.state_ts
        self.state = new_state
        self.state_ts = int(time.time())
        self.msg = msg
        self.log_msg('changed state to %s - %s' % (new_state, msg))
        if new_state == 'DOWN':
            await self.set_down()
            await self.notify_state_change(prev_state, prev_state_ts)
        elif new_state == 'UP':
            await self.set_up()
            if prev_state == 'DOWN':
                await self.notify_state_change(prev_state, prev_state_ts)
        else:
            await self.set_unknown()

    async def notify_state_change(self, prev_state: str, prev_state_ts: float) -> None:
        if not self.alerts_enabled:
            self.log_debug('skipping alert notifications, disabled')
            return
        contacts = await contact.get_contact_dict_for_active_monitor(
            self.manager.dbcon, self.id)
        metadata = await self.get_metadata()
        tmpl_data = {}  # type: Dict[str, Any]
        for key, value in metadata.items():
            tmpl_data['meta_%s' % key] = value
        if prev_state_ts and self.state_ts - prev_state_ts:
            tmpl_data['state_elapsed'] = utils.get_display_time(self.state_ts - prev_state_ts)
        tmpl_data['state'] = self.state
        tmpl_data['prev_state'] = prev_state
        tmpl_data['type'] = 'active_monitor'
        tmpl_data['id'] = self.id
        tmpl_data['monitor_description'] = self.get_description()
        tmpl_data['msg'] = self.msg
        # Don't wait for notifications to be sent, it may or may not take a
        # while and we don't want to pause the monitoring to wait for it.
        asyncio.ensure_future(
            self.manager.notification_manager.send_notification(contacts, tmpl_data))

    def update_consecutive_checks(self, state: str) -> None:
        """Update the counter for consecutive checks with the same result."""
        if state == self.last_check_state:
            self.consecutive_checks += 1
        else:
            self.consecutive_checks = 0
        self.last_check_state = state

    async def set_up(self) -> None:
        """Set a monitor up (in the database)."""

        async def _run(cur: sql.Cursor) -> None:
            if self.alert_id:
                await self.txn_close_alert(cur)
            await self.txn_save_state(cur)

        await self.manager.dbcon.transact(_run)

    async def set_down(self) -> None:
        """Set a monitor down (in the database)."""

        async def _run(cur: sql.Cursor) -> None:
            await self.txn_create_alert(cur)
            await self.txn_save_state(cur)

        await self.manager.dbcon.transact(_run)

    async def set_unknown(self) -> None:
        """Set a monitor in unknown state (in the database)."""

        async def _run(cur: sql.Cursor) -> None:
            await self.txn_save_state(cur)

        await self.manager.dbcon.transact(_run)

    async def txn_create_alert(self, cur: sql.Cursor) -> None:
        q = """insert into active_monitor_alerts (monitor_id, start_ts, end_ts, alert_msg) values (%s, %s, %s, %s)"""
        q_args = (self.id, self.state_ts, 0, self.msg)
        await cur.execute(q, q_args)
        self.alert_id = cur.lastrowid

    async def txn_close_alert(self, cur: sql.Cursor) -> None:
        q = """update active_monitor_alerts set end_ts=%s where id=%s"""
        q_args = (self.state_ts, self.alert_id)
        await cur.execute(q, q_args)
        self.alert_id = None

    async def txn_save_state(self, cur: sql.Cursor) -> None:
        q = """update active_monitors set state=%s, state_ts=%s, msg=%s, alert_id=%s where id=%s"""
        q_args = (self.state, self.state_ts, self.msg, self.alert_id, self.id)
        await cur.execute(q, q_args)

    async def delete(self) -> None:
        """Delete an existing monitor.

        If the monitor is not running it will be removed immediately.
        If the monitor is running it will be remove when the run is complete.
        """
        if self.deleted:
            return
        self.log_msg('deleting monitor')
        event.running('DELETE_ACTIVE_MONITOR', monitor=self)
        self.deleted = True
        if self.id in self.manager.monitors:
            del self.manager.monitors[self.id]
        if self.monitoring:
            q = """update active_monitors set deleted=true where id=%s"""
            q_args = (int(time.time()), self.id,)
            await self.manager.dbcon.operation(q, q_args)
        else:
            await self._purge()

    async def _purge(self) -> None:
        """Remove a monitor from the database."""
        self.log_msg('purging deleted monitor')
        stats.dec('num_monitors', 'ACT_MON')
        self.monitor_def.tmpl_cache.flush_monitor(self)
        await remove_monitor_from_db(self.manager.dbcon, self.id)

    async def update_args(self, args: Dict[str, str]) -> None:
        async def _run(cur: sql.Cursor) -> None:
            q = """delete from active_monitor_args where monitor_id=%s"""
            q_args = (self.id,)  # type: Tuple
            await cur.execute(q, q_args)
            q = """insert into active_monitor_args (monitor_id, name, value) values (%s, %s, %s)"""
            for name, value in args.items():
                q_args = (self.id, name, value)
                await cur.execute(q, q_args)

        self.log_msg('updating monitor arguments')
        self.monitor_def.validate_monitor_args(args)
        self.args = args
        self.monitor_def.tmpl_cache.flush_monitor(self)
        await self.manager.dbcon.transact(_run)

    async def set_checks_enabled_status(self, checks_enabled: bool) -> None:
        if self.checks_enabled == checks_enabled:
            return
        self.log_debug('settings monitor checks to %s' % checks_enabled)
        self.checks_enabled = checks_enabled
        if not checks_enabled:
            await self.reset_monitor()
        q = """update active_monitors set checks_enabled=%s where id=%s"""
        q_args = (checks_enabled, self.id)
        self.schedule_immediately()
        await self.manager.dbcon.operation(q, q_args)

    async def set_alerts_enabled_status(self, alerts_enabled: bool) -> None:
        if self.alerts_enabled == alerts_enabled:
            return
        self.log_debug('settings monitor alerts to %s' % alerts_enabled)
        self.alerts_enabled = alerts_enabled
        q = """update active_monitors set alerts_enabled=%s where id=%s"""
        q_args = (alerts_enabled, self.id)
        await self.manager.dbcon.operation(q, q_args)

    def schedule_immediately(self) -> None:
        """Schedule a check for this monitor ASAP."""
        if not self.monitoring and not self.deleted:
            self.log_msg('Forcing immediate check by request')
            self.manager.schedule_monitor(self, 5)

    async def get_metadata(self) -> Dict[str, str]:
        ret = await get_metadata(self.manager.dbcon, 'active_monitor', self.id)
        return ret

    async def reset_monitor(self) -> None:
        """Reset a monitor to its initial state.

        This is currently only used when disabling checks for a monitor
        but might be useful for other purposes in the future.
        """
        if self.monitoring:
            self._pending_reset = True
            return
        self._pending_reset = False
        self.state = 'UNKNOWN'
        self.state_ts = int(time.time())
        self.msg = ''
        self.consecutive_checks = 0

        async def _run(cur: sql.Cursor) -> None:
            if self.alert_id:
                await self.txn_close_alert(cur)
            await self.txn_save_state(cur)

        await self.manager.dbcon.transact(_run) class ActiveMonitorDef(log.LoggingMixin):
    def __init__(self, id: int, name: str, active: bool, cmdline_filename: str, cmdline_args_tmpl: str,
                 description_tmpl: str, arg_spec: List[object_models.ActiveMonitorDefArg],
                 manager: ActiveMonitorManager) -> None:
        self.id = id
        self.name = name
        self.active = active
        self.cmdline_filename = cmdline_filename
        self.cmdline_args_tmpl = cmdline_args_tmpl
        self.description_tmpl = description_tmpl
        self.arg_spec = arg_spec
        self.manager = manager
        self.jinja_cmdline_args = jinja2.Template(cmdline_args_tmpl)
        self.jinja_description_tmpl = jinja2.Template(description_tmpl)
        self.tmpl_cache = MonitorTemplateCache()

    def __str__(self) -> str:
        return '<ActiveMonitorDef(%s/%s)>' % (self.id, self.cmdline_filename)

    def get_arg_with_name(self, name: str) -> Optional[object_models.ActiveMonitorDefArg]:
        match = None
        for arg in self.arg_spec:
            if arg.name == name:
                match = arg
                break
        return match

    def expand_monitor_args(self, monitor_args: Dict[str, str]) -> List[str]:
        """Expand the monitors command line arguments.

        The monitor command line arguments are based on monitor def
        cmdline_args_tmpl template.
        """
        args = {a.name: a.default_value for a in self.arg_spec}
        args.update(monitor_args)
        expanded = self.jinja_cmdline_args.render(**args)
        ret = shlex.split(expanded)  # Supports "" splitting etc.
        return ret

    def expand_monitor_description(self, monitor_args: Dict[str, str]) -> str:
        """Return a monitor description based on this monitor def.

        This uses the monitor def description_tmpl to create a useful
        monitor description based on the monitors (commandline) arguments
        and the monitor defs default values.

        This is used when sending monitor notifications.
        """
        args = {a.name: a.default_value for a in self.arg_spec}
        args.update(monitor_args)
        description = self.jinja_description_tmpl.render(**args)
        return description

    def validate_monitor_args(self, monitor_args: Dict[str, str], permit_missing: bool = False) -> bool:
        if not permit_missing:
            for arg in self.arg_spec:
                if arg.required and arg.name not in monitor_args:
                    raise errors.InvalidArguments('missing argument %s' % arg.name)
        arg_name_set = {a.name for a in self.arg_spec}
        for key, value in monitor_args.items():
            if key not in arg_name_set:
                raise errors.InvalidArguments('invalid argument %s' % key)
        return True

    async def delete(self) -> None:
        for _ in self.iter_monitors():
            raise errors.IrisettError('can\'t remove active monitor def that is in use')
        del self.manager.monitor_defs[self.id]
        self.tmpl_cache.flush_all()
        await remove_monitor_def_from_db(self.manager.dbcon, self.id)

    async def update(self, update_params: Dict[str, Any]) -> None:
        async def _run(cur: sql.Cursor) -> None:
            for param in ['name', 'description', 'active', 'cmdline_filename', 'cmdline_args_tmpl', 'description_tmpl']:
                if param in update_params:
                    q = """update active_monitor_defs set %s=%%s where id=%%s""" % param
                    q_args = (update_params[param], self.id)
                    await cur.execute(q, q_args)

        self.log_msg('updating monitor def')
        if 'name' in update_params:
            self.name = update_params['name']
        if 'active' in update_params:
            self.active = update_params['active']
        if 'cmdline_filename' in update_params:
            self.cmdline_filename = update_params['cmdline_filename']
        if 'cmdline_args_tmpl' in update_params:
            self.cmdline_args_tmpl = update_params['cmdline_args_tmpl']
            self.jinja_cmdline_args = jinja2.Template(self.cmdline_args_tmpl)
        if 'description_tmpl' in update_params:
            self.description_tmpl = update_params['description_tmpl']
            self.jinja_description_tmpl = jinja2.Template(self.description_tmpl)
        self.tmpl_cache.flush_all()
        await self.manager.dbcon.transact(_run)

    def iter_monitors(self) -> Iterator['ActiveMonitor']:
        """List all monitors that use this monitor def."""
        for monitor in self.manager.monitors.values():
            if monitor.monitor_def.id == self.id:
                yield monitor

    async def set_arg(self, new_arg: object_models.ActiveMonitorDefArg) -> None:
        existing_arg = self.get_arg_with_name(new_arg.name)
        if existing_arg:
            existing_arg.name = new_arg.name
            existing_arg.required = new_arg.required
            existing_arg.default_value = new_arg.default_value
            await update_monitor_def_arg_in_db(self.manager.dbcon, existing_arg)
        else:
            new_arg.id = await add_monitor_def_arg_to_db(self.manager.dbcon, new_arg)
            self.arg_spec.append(new_arg)
        self.tmpl_cache.flush_all()

    async def delete_arg(self, name: str) -> None:
        arg = self.get_arg_with_name(name)
        if arg:
            self.arg_spec.remove(arg)
            self.tmpl_cache.flush_all()
            await delete_monitor_def_arg_from_db(self.manager.dbcon, arg.id)

    async def get_notify_data(self) -> Dict[str, str]:
        q = """select name, description from active_monitor_defs where id=%s"""
        q_args = (self.id,)
        res = await self.manager.dbcon.fetch_all(q, q_args)
        name, description = res[0]
        ret = {
            'name': name,
            'description': description,
        }
        return ret async def create_active_monitor(manager: ActiveMonitorManager, args: Dict[str, str],
                                monitor_def: ActiveMonitorDef) -> ActiveMonitor:
    async def _run(cur: sql.Cursor) -> None:
        q = """insert into active_monitors (def_id, state, state_ts, msg) values (%s, %s, %s, %s)"""
        q_args = (monitor_def.id, 'UNKNOWN', 0, '')  # type: Tuple
        await cur.execute(q, q_args)
        _monitor_id = cur.lastrowid
        q = """insert into active_monitor_args (monitor_id, name, value) values (%s, %s, %s)"""
        for name, value in args.items():
            q_args = (_monitor_id, name, value)
            await cur.execute(q, q_args)
        return _monitor_id

    monitor_def.validate_monitor_args(args)
    monitor_id = await manager.dbcon.transact(_run)
    monitor = ActiveMonitor(monitor_id, args, monitor_def, 'UNKNOWN', state_ts=0, msg='', alert_id=None,
                            checks_enabled=True, alerts_enabled=True, manager=manager)
    log.msg('Created active monitor %s' % monitor)
    manager.add_monitor(monitor)
    return monitor async def create_active_monitor_def(manager: ActiveMonitorManager, name: str, description: str,
                                    active: bool, cmdline_filename: str, cmdline_args_tmpl: str,
                                    description_tmpl: str) -> ActiveMonitorDef:
    q = """insert into active_monitor_defs
        (name, description, active, cmdline_filename, cmdline_args_tmpl, description_tmpl)
        values (%s, %s, %s, %s, %s, %s)"""
    q_args = (name, description, active, cmdline_filename, cmdline_args_tmpl, description_tmpl)
    monitor_def_id = await manager.dbcon.operation(q, q_args)
    monitor_def = ActiveMonitorDef(monitor_def_id, name, active, cmdline_filename,
                                   cmdline_args_tmpl, description_tmpl, [], manager)
    log.msg('Created active monitor def %s' % monitor_def)
    manager.monitor_defs[monitor_def.id] = monitor_def
    return monitor_def def get_monitor_def_by_name(manager: ActiveMonitorManager, name: str) -> Optional[ActiveMonitorDef]:
    """Get a monitor definition based on its name."""
    ret = None
    for monitor_def in manager.monitor_defs.values():
        if monitor_def.name == name:
            ret = monitor_def
            break
    return ret async def get_all_active_monitor_defs(dbcon: DBConnection) -> Iterable[object_models.ActiveMonitorDef]:
async def get_active_monitor_def(dbcon: DBConnection, id: int) -> Optional[object_models.ActiveMonitorDef]:
async def get_all_active_monitor_def_args(dbcon: DBConnection) -> Iterable[object_models.ActiveMonitorDefArg]:
async def get_active_monitor_def_args_for_def(
        dbcon: DBConnection, def_id: int) -> Iterable[object_models.ActiveMonitorDefArg]:
async def get_all_active_monitors(dbcon: DBConnection) -> Iterable[object_models.ActiveMonitor]:
async def get_all_active_monitor_args(dbcon: DBConnection) -> Iterable[object_models.ActiveMonitorArg]:
async def get_active_monitors_for_metadata(dbcon: DBConnection, meta_key: str, meta_value: str): class DBConnection:
    """A sql connection manager."""
    def __init__(self, host: str, user: str, passwd: str, dbname: str, loop: asyncio.AbstractEventLoop=None) -> None:
        self.loop = loop or asyncio.get_event_loop()
        self.host = host
        self.user = user
        self.passwd = passwd
        self.dbname = dbname
        self.pool = None  # type: Any
        stats.set('queries', 0, 'SQL')
        stats.set('transactions', 0, 'SQL')

    async def initialize(self, *, only_init_tables: bool=False):
        """Initialize the DBConnection.

        Creates a connection pool using aiomysql and initializes the database
        if necessary.
        """
        self.pool = await aiomysql.create_pool(
            host=self.host, user=self.user, password=self.passwd,
            loop=self.loop)
        db_exists = await self._check_db_exists()
        if not db_exists:
            await self._create_db()
            db_initialized = False
        else:
            db_initialized = await self._check_db_initialized()
        # We close the pool and create a new one because aiomysql doesn't
        # provide an easy way to change the active database for an entire
        # pool, just individual connections.
        self.pool.terminate()
        self.pool = await aiomysql.create_pool(
            host=self.host, user=self.user, password=self.passwd,
            db=self.dbname, loop=self.loop)
        if not db_initialized:
            await self._init_db(only_init_tables)
        await self._upgrade_db()
        log.msg('Database initialized')

    async def close(self) -> None:
        self.pool.terminate()
        await self.pool.wait_closed()

    async def _create_db(self) -> None:
        # Yes yes, this risks sql injection, but the dbname is from the
        # irisett config file, so if you want to sql inject yourself,
        # go ahead.
        log.msg('Creating missing database %s' % self.dbname)
        q = """CREATE DATABASE %s""" % self.dbname
        await self.operation(q)

    async def _init_db(self, only_init_tables: bool) -> None:
        log.msg('Initializing empty database')
        commands = sql_data.SQL_ALL
        if only_init_tables:
            commands = sql_data.SQL_BARE
        for command in commands:
            await self.operation(command)

    async def _check_db_exists(self) -> bool:
        """Check if the database exists."""
        q = """SELECT SCHEMA_NAME
            FROM INFORMATION_SCHEMA.SCHEMATA
            WHERE SCHEMA_NAME = %s"""
        res = await self.fetch_single(q, (self.dbname,))
        if not res:
            return False
        return True

    async def _check_db_initialized(self) -> bool:
        """Check if the database has been initialized."""
        q = """SELECT count(*)
        FROM information_schema.TABLES
        WHERE (TABLE_SCHEMA = %s) AND (TABLE_NAME = %s)"""
        res = await self.fetch_single(q, (self.dbname, 'version'))
        if res == 0:
            return False
        return True

    async def _upgrade_db(self) -> None:
        """Upgrade to a newer database version if required.

        Loops through the commands in sql_data.SQL_UPGRADES and runs them.
        """
        cur_version = await self._get_db_version()
        for n in range(cur_version + 1, sql_data.CUR_VERSION + 1):
            log.msg('Upgrading database to version %d' % n)
            if n in sql_data.SQL_UPGRADES:
                for command in sql_data.SQL_UPGRADES[n]:
                    await self.operation(command)
        if cur_version != sql_data.CUR_VERSION:
            await self._set_db_version(sql_data.CUR_VERSION)

    async def _get_db_version(self) -> int:
        q = """select version from version limit 1"""
        str_version = await self.fetch_single(q)
        version = int(str_version)
        return version

    async def _set_db_version(self, version: int):
        q = """update version set version=%s"""
        q_args = (version,)
        await self.operation(q, q_args)

    async def fetch_all(self, query: str, args: Optional[Iterable]=None) -> List:
        """Run a query and fetch all returned rows."""
        stats.inc('queries', 'SQL')
        async with self.pool.acquire() as conn:
            async with conn.cursor() as cur:
                await cur.execute(query, args)
                ret = await cur.fetchall()
        return ret

    async def fetch_row(self, query: str, args: Optional[Iterable]=None) -> List:
        """Run a query and fetch a single returned row."""
        stats.inc('queries', 'SQL')
        async with self.pool.acquire() as conn:
            async with conn.cursor() as cur:
                await cur.execute(query, args)
                ret = await cur.fetchone()
        return ret

    async def fetch_single(self, query: str, args: Optional[Iterable]=None) -> Any:
        """Run a query and fetch a single returned value from a single row."""
        res = await self.fetch_row(query, args)
        ret = None
        if res and len(res) == 1:
            ret = res[0]
        return ret

    async def count_rows(self, query: str, args: Optional[Iterable]=None) -> float:
        """Count the number of returned rows for a query.

        This is not equivalent to select count(*), this will actually fetch
        all the rows then count them and is thus much slower.
        """
        res = await self.fetch_all(query, args)
        return len(res)

    async def operation(self, query: str, args: Optional[Iterable]=None) -> Any:
        """Run a sql operation (query).

        Ie. insert, update etc. not select.
        Returns the row id of the created row if any.
        """
        stats.inc('queries', 'SQL')
        async with self.pool.acquire() as conn:
            async with conn.cursor() as cur:
                await cur.execute(query, args)
                ret = cur.lastrowid
                await conn.commit()
        return ret

    async def transact(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
        """Create a db cursor and hand it to a callback.

        This can be used to simulate transactions.
        commit will be called when the callback returns. If an exception is
        raised in the callback a rollback is performed.
        """
        stats.inc('transactions', 'SQL')
        async with self.pool.acquire() as conn:
            async with conn.cursor() as cur:
                try:
                    ret = await func(cur, *args, **kwargs)
                except:
                    await conn.rollback()
                    raise
                else:
                    await conn.commit()
        return ret async def create_contact(dbcon: DBConnection, name: Optional[str], email: Optional[str],
                         phone: Optional[str], active: bool) -> str:
    """Add a contact to the database."""
    q = """insert into contacts (name, email, phone, active) values (%s, %s, %s, %s)"""
    q_args = (name, email, phone, active)
    contact_id = await dbcon.operation(q, q_args)
    return contact_id async def update_contact(dbcon: DBConnection, contact_id: int, data: Dict[str, str]) -> None:
    """Update a contacts information in the database.

    Data is a dict with name/email/phone/active values that
    will be updated.
    """

    async def _run(cur: Cursor) -> None:
        for key, value in data.items():
            if key not in ['name', 'email', 'phone', 'active']:
                raise errors.IrisettError('invalid contact key %s' % key)
            q = """update contacts set %s=%%s where id=%%s""" % key
            q_args = (value, contact_id)
            await cur.execute(q, q_args)

    if not await contact_exists(dbcon, contact_id):
        raise errors.InvalidArguments('contact does not exist')
    await dbcon.transact(_run) async def delete_contact(dbcon: DBConnection, contact_id: int) -> None:
    """Remove a contact from the database."""
    if not await contact_exists(dbcon, contact_id):
        raise errors.InvalidArguments('contact does not exist')
    q = """delete from contacts where id=%s"""
    await dbcon.operation(q, (contact_id,)) async def add_contact_to_active_monitor(dbcon: DBConnection, contact_id: int, monitor_id: int) -> None:
    """Connect a contact and an active monitor."""
    if not await active_monitor_exists(dbcon, monitor_id):
        raise errors.InvalidArguments('monitor does not exist')
    if not await contact_exists(dbcon, contact_id):
        raise errors.InvalidArguments('contact does not exist')
    q = """replace into active_monitor_contacts (active_monitor_id, contact_id) values (%s, %s)"""
    q_args = (monitor_id, contact_id)
    await dbcon.operation(q, q_args) async def delete_contact_from_active_monitor(dbcon: DBConnection, contact_id: int, monitor_id: int) -> None:
    """Disconnect a contact and an active monitor."""
    q = """delete from active_monitor_contacts where active_monitor_id=%s and contact_id=%s"""
    q_args = (monitor_id, contact_id)
    await dbcon.operation(q, q_args) async def get_contacts_for_active_monitor(dbcon: DBConnection, monitor_id: int) -> Iterable[object_models.Contact]:
    """Get contacts for an active monitor.

    Return a list of dicts, one dict describing each contacts information.
    """
    q = """select
        contacts.id, contacts.name, contacts.email, contacts.phone, contacts.active
        from active_monitor_contacts, contacts
        where active_monitor_contacts.active_monitor_id = %s
        and active_monitor_contacts.contact_id = contacts.id"""
    contacts = [object_models.Contact(*row) for row in await dbcon.fetch_all(q, (monitor_id,))]
    return contacts async def set_active_monitor_contacts(dbcon: DBConnection,
                                      contact_ids: Iterable[int], monitor_id: int):
    """(Re-)set contacts for an active monitor.

    Delete existing contacts for an active monitor and set the given new
    contacts.
    """

    async def _run(cur: Cursor) -> None:
        q = """delete from active_monitor_contacts where active_monitor_id=%s"""
        await cur.execute(q, (monitor_id,))
        for contact_id in contact_ids:
            q = """insert into active_monitor_contacts (active_monitor_id, contact_id) values (%s, %s)"""
            q_args = (monitor_id, contact_id)
            await cur.execute(q, q_args)

    if not await active_monitor_exists(dbcon, monitor_id):
        raise errors.InvalidArguments('monitor does not exist')
    await dbcon.transact(_run) async def get_all_contacts_for_active_monitor(dbcon: DBConnection, monitor_id: int) -> Iterable[object_models.Contact]:
    """Get a list of all contacts for an active monitor.

    This includes directly attached contacts, contacts from contact groups,
    monitor groups etc.
    """
    contacts = set()
    contacts.update(await _active_monitor_contacts(dbcon, monitor_id))
    contacts.update(await _active_monitor_contact_groups(dbcon, monitor_id))
    contacts.update(await _active_monitor_monitor_group_contacts(dbcon, monitor_id))
    contacts.update(await _active_monitor_monitor_group_contact_groups(dbcon, monitor_id))
    return list(contacts) async def create_contact_group(dbcon: DBConnection, name: str, active: bool) -> str:
    """Add a contact group to the database."""
    q = """insert into contact_groups (name, active) values (%s, %s)"""
    q_args = (name, active)
    contact_group_id = await dbcon.operation(q, q_args)
    return contact_group_id async def update_contact_group(dbcon: DBConnection, contact_group_id: int, data: Dict[str, str]) -> None:
    """Update a contact groups information in the database.

    Data is a dict with name/active values that will be updated.
    """

    async def _run(cur: Cursor) -> None:
        for key, value in data.items():
            if key not in ['name', 'active']:
                raise errors.IrisettError('invalid contact key %s' % key)
            q = """update contact_groups set %s=%%s where id=%%s""" % key
            q_args = (value, contact_group_id)
            await cur.execute(q, q_args)

    if not await contact_group_exists(dbcon, contact_group_id):
        raise errors.InvalidArguments('contact group does not exist')
    await dbcon.transact(_run) async def delete_contact_group(dbcon: DBConnection, contact_group_id: int) -> None:
    """Remove a contact group from the database."""
    if not await contact_group_exists(dbcon, contact_group_id):
        raise errors.InvalidArguments('contact group does not exist')
    q = """delete from contact_groups where id=%s"""
    await dbcon.operation(q, (contact_group_id,)) async def add_contact_to_contact_group(dbcon: DBConnection, contact_group_id: int, contact_id: int) -> None:
    """Connect a contact and a contact group."""
    if not await contact_group_exists(dbcon, contact_group_id):
        raise errors.InvalidArguments('contact group does not exist')
    if not await contact_exists(dbcon, contact_id):
        raise errors.InvalidArguments('contact does not exist')
    q = """replace into contact_group_contacts (contact_group_id, contact_id) values (%s, %s)"""
    q_args = (contact_group_id, contact_id)
    await dbcon.operation(q, q_args) async def delete_contact_from_contact_group(dbcon: DBConnection, contact_group_id: int, contact_id: int) -> None:
    """Disconnect a contact and a contact_group."""
    q = """delete from contact_group_contacts where contact_group_id=%s and contact_id=%s"""
    q_args = (contact_group_id, contact_id)
    await dbcon.operation(q, q_args) async def set_contact_group_contacts(dbcon: DBConnection,
                                     contact_group_id: int, contact_ids: Iterable[int]) -> None:
    """(Re-)set contacts for a contact group.

    Delete existing contacts for a contact group and set the given new
    contacts.
    """

    async def _run(cur: Cursor) -> None:
        q = """delete from contact_group_contacts where contact_group_id=%s"""
        await cur.execute(q, (contact_group_id,))
        for contact_id in contact_ids:
            q = """insert into contact_group_contacts (contact_group_id, contact_id) values (%s, %s)"""
            q_args = (contact_group_id, contact_id)
            await cur.execute(q, q_args)

    if not await contact_group_exists(dbcon, contact_group_id):
        raise errors.InvalidArguments('contact group does not exist')
    await dbcon.transact(_run) async def get_contacts_for_contact_group(dbcon: DBConnection, contact_group_id: int) -> Iterable[object_models.Contact]:
    """Get contacts for a contact group."""
    q = """select
        contacts.id, contacts.name, contacts.email, contacts.phone, contacts.active
        from contact_group_contacts, contacts
        where contact_group_contacts.contact_group_id = %s
        and contact_group_contacts.contact_id = contacts.id"""
    return [object_models.Contact(*row) for row in await dbcon.fetch_all(q, (contact_group_id,))] async def get_contact_groups_for_active_monitor(
        dbcon: DBConnection, monitor_id: int) -> Iterable[object_models.ContactGroup]:
    """Get contact groups for an active monitor."""
    q = """select
        contact_groups.id, contact_groups.name, contact_groups.active
        from active_monitor_contact_groups, contact_groups
        where active_monitor_contact_groups.active_monitor_id = %s
        and active_monitor_contact_groups.contact_group_id = contact_groups.id"""
    return [object_models.ContactGroup(*row) for row in await dbcon.fetch_all(q, (monitor_id,))] async def add_contact_group_to_active_monitor(dbcon: DBConnection, contact_group_id: int, monitor_id: int) -> None:
    """Connect a contact group and an active monitor."""
    if not await active_monitor_exists(dbcon, monitor_id):
        raise errors.InvalidArguments('monitor does not exist')
    if not await contact_group_exists(dbcon, contact_group_id):
        raise errors.InvalidArguments('contact does not exist')
    q = """replace into active_monitor_contact_groups (active_monitor_id, contact_group_id) values (%s, %s)"""
    q_args = (monitor_id, contact_group_id)
    await dbcon.operation(q, q_args) async def delete_contact_group_from_active_monitor(dbcon: DBConnection, contact_group_id: int, monitor_id: int) -> None:
    """Disconnect a contact group and an active monitor."""
    q = """delete from active_monitor_contact_groups where active_monitor_id=%s and contact_group_id=%s"""
    q_args = (monitor_id, contact_group_id)
    await dbcon.operation(q, q_args) async def set_active_monitor_contact_groups(dbcon: DBConnection,
                                            contact_group_ids: Iterable[int], monitor_id: int) -> None:
    """(Re-)set contact_groups for an active monitor.

    Delete existing contact groups for an active monitor and set the given new
    contact groups.
    """

    async def _run(cur: Cursor) -> None:
        q = """delete from active_monitor_contact_groups where active_monitor_id=%s"""
        await cur.execute(q, (monitor_id,))
        for contact_group_id in contact_group_ids:
            q = """insert into active_monitor_contact_groups (active_monitor_id, contact_group_id) values (%s, %s)"""
            q_args = (monitor_id, contact_group_id)
            await cur.execute(q, q_args)

    if not await active_monitor_exists(dbcon, monitor_id):
        raise errors.InvalidArguments('monitor does not exist')
    await dbcon.transact(_run) def require_int(value: Optional[Union[SupportsInt, str, bytes]], allow_none: bool=False) -> Any:
    """Make sure a value is an int.

    Used when dealing with http input data.
    """
    if value is None and allow_none:
        return value
    value = cast(Union[SupportsInt, str, bytes], value)
    try:
        value = int(value)
    except (ValueError, TypeError):
        raise InvalidData('value was %s(%s), expected list' % (type(value), value))
    return value def require_str(value: Any, convert: bool=False, allow_none: bool=False) -> Any:
    """Make sure a value is a str.

    Used when dealing with http input data.
    """
    if value is None and allow_none:
        return value
    if type(value) != str:
        if not convert:
            raise InvalidData('value  was %s(%s), expected str' % (type(value), value))
        value = str(value)
    return value def require_bool(value: Optional[Union[bool, str, int]], convert: bool=False, allow_none: bool=False) -> Any:
    """Make sure a value is a boolean.

    Used when dealing with http input data.
    """
    if value is None and allow_none:
        return value
    if type(value) != bool:
        if not convert:
            raise InvalidData()
        if value in [None, 0, '0', 'false', 'False']:
            value = False
        elif value in [1, '1', 'true', 'True']:
            value = True
        else:
            raise InvalidData('value was %s(%s), expected bool' % (type(value), value))
    return cast(bool, value) def require_dict(value: Optional[Dict[Any, Any]], key_type: Any=None, value_type: Any=None,
                 allow_none: bool=False) -> Any:
    """Make sure a value is a Dict[key_type, value_type].

    Used when dealing with http input data.
    """
    if value is None and allow_none:
        return value
    if type(value) != dict:
        raise InvalidData('value was %s(%s), expected dict' % (type(value), value))
    value = cast(Dict, value)
    if key_type or value_type:
        for k, v in value.items():
            if key_type and type(k) != key_type:
                raise InvalidData('dict key was %s(%s), expected %s' % (type(k), k, key_type))
            if value_type and type(v) != value_type:
                raise InvalidData('dict value was %s(%s), expected %s' % (type(v), v, key_type))
    return value def require_list(value: Optional[List[Any]], item_type: Any=None, allow_none: bool=False) -> Any:
    """Make sure a value is a List[item_type].

    Used when dealing with http input data.
    """
    if value is None and allow_none:
        return value
    if type(value) != list:
        raise InvalidData('value was %s, expected list' % type(value))
    value = cast(List, value)
    if item_type:
        for item in value:
            if type(item) != item_type:
                raise InvalidData('list item was %s, expected %s' % (type(item), item_type))
    return value
from typing import Any, Dict, List, Iterable, Optional, cast, Tuple
from aiohttp import web
from irisett import (
    metadata,
    bindata,
    stats,
    utils,
    contact,
    monitor_group,
    object_models,
)
from irisett.webapi import (
    errors,
)
from irisett.monitor.active import (
    ActiveMonitor,
    ActiveMonitorDef,
    create_active_monitor,
    create_active_monitor_def,
    get_monitor_def_by_name,
)
from irisett.monitor import active_sql
from irisett.sql import DBConnection
from irisett.contact import (
    create_contact,
    update_contact,
    delete_contact,
    add_contact_to_active_monitor,
    delete_contact_from_active_monitor,
    get_contacts_for_active_monitor,
    set_active_monitor_contacts,
    get_all_contacts_for_active_monitor,
    create_contact_group,
    update_contact_group,
    delete_contact_group,
    add_contact_to_contact_group,
    delete_contact_from_contact_group,
    set_contact_group_contacts,
    get_contacts_for_contact_group,
    get_contact_groups_for_active_monitor,
    add_contact_group_to_active_monitor,
    delete_contact_group_from_active_monitor,
    set_active_monitor_contact_groups,
)
from irisett.webapi.require import (
    require_int,
    require_str,
    require_bool,
    require_dict,
    require_list,
)
import time
                    from active_monitor_alerts
                    where end_ts=0
                    order by start_ts desc"""
            else:
                q = """select
                    id, monitor_id, start_ts, end_ts, alert_msg
                    from active_monitor_alerts
                    order by start_ts desc"""
            ret = await self._get_alerts(q, ())
        return web.json_response(ret)

    async def _get_alerts(self, q: str, q_args: Iterable[Any]) -> List[Dict[str, Any]]:
        rows = await self.request.app['dbcon'].fetch_all(q, q_args)
        ret = []
        for id, monitor_id, start_ts, end_ts, alert_msg in rows:
            alert = {
                'id': id,
                'monitor_id': monitor_id,
                'start_ts': start_ts,
                'end_ts': end_ts,
                'alert_msg': alert_msg,
                'monitor_description': '',
            }
            monitor = self.request.app['active_monitor_manager'].monitors.get(monitor_id, None)  # type: ActiveMonitor
            if monitor:
                alert['monitor_description'] = monitor.get_description()
            ret.append(alert)
        return ret


class ActiveMonitorContactView(web.View):
    async def get(self) -> web.Response:
        monitor_id = cast(int, require_int(get_request_param(self.request, 'monitor_id')))
        if 'include_all' in self.request.rel_url.query:
            contacts = await get_all_contacts_for_active_monitor(self.request.app['dbcon'], monitor_id)
        else:
            contacts = object_models.asdict(
                await get_contacts_for_active_monitor(self.request.app['dbcon'], monitor_id)
            )
        ret = object_models.list_asdict(contacts)
        return web.json_response(ret)

    async def post(self) -> web.Response:
        request_data = await self.request.json()
        await add_contact_to_active_monitor(
            self.request.app['dbcon'],
            cast(int, require_int(request_data.get('contact_id'))),
            cast(int, require_int(request_data.get('monitor_id'))))
        return web.json_response(True)

    async def delete(self) -> web.Response:
        request_data = await self.request.json()
        await delete_contact_from_active_monitor(
            self.request.app['dbcon'],
            cast(int, require_int(request_data.get('contact_id'))),
            cast(int, require_int(request_data.get('monitor_id'))))
        return web.json_response(True)

    async def put(self) -> web.Response:
        request_data = await self.request.json()
        await set_active_monitor_contacts(
            self.request.app['dbcon'],
            cast(List[int], require_list(request_data.get('contact_ids'), int)),
            cast(int, require_int(request_data.get('monitor_id'))))
        return web.json_response(True)


class ActiveMonitorContactGroupView(web.View):
    async def get(self) -> web.Response:
        monitor_id = cast(int, require_int(get_request_param(self.request, 'monitor_id')))
        ret = await get_contact_groups_for_active_monitor(self.request.app['dbcon'], monitor_id)
        return web.json_response(object_models.list_asdict(ret))

    async def post(self) -> web.Response:
        request_data = await self.request.json()
        await add_contact_group_to_active_monitor(
            self.request.app['dbcon'],
            cast(int, require_int(request_data.get('contact_group_id'))),
            cast(int, require_int(request_data.get('monitor_id'))))
        return web.json_response(True)

    async def delete(self) -> web.Response:
        request_data = await self.request.json()
        await delete_contact_group_from_active_monitor(
            self.request.app['dbcon'],
            cast(int, require_int(request_data.get('contact_group_id'))),
            cast(int, require_int(request_data.get('monitor_id'))))
        return web.json_response(True)

    async def put(self) -> web.Response:
        request_data = await self.request.json()
        await set_active_monitor_contact_groups(
            self.request.app['dbcon'],
            cast(List[int], require_list(request_data.get('contact_group_ids'), int)),
            cast(int, require_int(request_data.get('monitor_id'))))
        return web.json_response(True)


class ActiveMonitorDefView(web.View):
    async def get(self) -> web.Response:
        dbcon = self.request.app['dbcon']
        if 'id' in self.request.rel_url.query:
            monitor_def_id = require_int(get_request_param(self.request, 'id'))
            monitor_def_item = await active_sql.get_active_monitor_def(dbcon, monitor_def_id)
            monitor_def_list = []  # type: Iterable[object_models.ActiveMonitorDef]
            if monitor_def_item:
                monitor_def_list = [monitor_def_item]
            metadata_list = await metadata.get_metadata_for_object(dbcon, 'active_monitor_def', monitor_def_id)
            arg_list = await active_sql.get_active_monitor_def_args_for_def(dbcon, monitor_def_id)
        else:
            monitor_def_list = await active_sql.get_all_active_monitor_defs(dbcon)
            metadata_list = await metadata.get_metadata_for_object_type(dbcon, 'active_monitor_def')
            arg_list = await active_sql.get_all_active_monitor_def_args(dbcon)
        monitor_def_dict = {item.id: object_models.asdict(item) for item in monitor_def_list}
        for monitor_def in monitor_def_dict.values():
            monitor_def['metadata'] = {}
            monitor_def['arg_def'] = []
        for arg in arg_list:
            monitor_def = monitor_def_dict.get(arg.active_monitor_def_id)
            if monitor_def:

                monitor_def['arg_def'].append(object_models.asdict(arg))