def _create_proto_internal( cls, conn, ctx, name, monitored_entity_id, alerter, summary_sample_query, notification_channels, created_at_millis, updated_at_millis, last_evaluated_at_millis, # TODO: should we allow `status` and `violating samples` on create? ): msg = _AlertService.CreateAlertRequest(alert=_AlertService.Alert( name=name, monitored_entity_id=monitored_entity_id, created_at_millis=created_at_millis, updated_at_millis=updated_at_millis, last_evaluated_at_millis=last_evaluated_at_millis, notification_channels={ channel.id: True for channel in notification_channels }, sample_find_base=summary_sample_query._to_proto_request(), alerter_type=alerter._TYPE, ), ) field = getattr(msg.alert, alerter._get_alert_field()) field.CopyFrom(alerter._as_proto()) endpoint = "/api/v1/alerts/createAlert" response = conn.make_proto_request("POST", endpoint, body=msg) alert_msg = conn.must_proto_response(response, _AlertService.Alert) return alert_msg
def add_notification_channels(self, notification_channels): """ Add notification channels to this alert. Parameters ---------- notification_channels : list of :class:`~verta.monitoring.notification_channel.entities.NotificationChannel` Notification channels. Examples -------- .. code-block:: python from verta.monitoring.notification_channel import SlackNotificationChannel channels = Client().monitoring.notification_channels channel = notification_channels.create( "Slack alerts", SlackNotificationChannel("https://hooks.slack.com/services/.../.../......"), ) alert.add_notification_channels([channel]) """ for channel in notification_channels: self._validate_notification_channel(channel) alert_msg = _AlertService.Alert() self._fetch_with_no_cache() alert_msg.CopyFrom(self._msg) alert_msg.notification_channels.update( {channel.id: True for channel in notification_channels}) self._update(alert_msg)
def delete(self, channels): """ Delete the given notification channels in a single request. Parameters ---------- channels : list of :class:`NotificationChannel` Notification channels. Returns ------- bool ``True`` if the delete was successful. Raises ------ :class:`requests.HTTPError` If the delete failed. """ channel_ids = arg_handler.extract_ids(channels) msg = _AlertService.DeleteNotificationChannelRequest(ids=channel_ids) endpoint = "/api/v1/alerts/deleteNotificationChannel" response = self._conn.make_proto_request("DELETE", endpoint, body=msg) self._conn.must_response(response) return True
def list(self, workspace=None): """ Return accesible notification channels. Parameters ---------- workspace : str, optional Workspace from which to list notification channels. Defaults to the client's default workspace. Returns ------- list of :class:`NotificationChannel` Notification channels. """ msg = _AlertService.FindNotificationChannelRequest( page_number=1, page_limit=-1, workspace_name=workspace, ) endpoint = "/api/v1/alerts/findNotificationChannel" response = self._conn.make_proto_request("POST", endpoint, body=msg) channels = self._conn.must_proto_response(response, msg.Response).channels return [ NotificationChannel(self._conn, self._conf, channel) for channel in channels ]
def _create_proto_internal( cls, conn, ctx, name, channel, workspace, created_at_millis, updated_at_millis, ): msg = _AlertService.CreateNotificationChannelRequest( name=name, created_at_millis=created_at_millis, updated_at_millis=updated_at_millis, workspace_name=workspace, type=channel._TYPE, ) if msg.type == _AlertService.NotificationChannelTypeEnum.SLACK: msg.slack_webhook.CopyFrom(channel._as_proto()) else: raise ValueError( "unrecognized notification channel type enum value {}".format( msg.alert.alerter_type ) ) endpoint = "/api/v1/alerts/createNotificationChannel" response = conn.make_proto_request("POST", endpoint, body=msg) notification_channel_msg = conn.must_proto_response( response, _AlertService.NotificationChannel, ) return notification_channel_msg
def delete(self, alerts): """ Delete the given alerts in a single request. Parameters ---------- list of :class:`Alert` Alerts. Returns ------- bool ``True`` if the delete was successful. Raises ------ :class:`requests.HTTPError` If the delete failed. """ alert_ids = arg_handler.extract_ids(alerts) msg = _AlertService.DeleteAlertRequest(ids=alert_ids) endpoint = "/api/v1/alerts/deleteAlert" response = self._conn.make_proto_request("DELETE", endpoint, body=msg) self._conn.must_response(response) return True
def history(self): # TODO: implement lazy list and pagination msg = _AlertService.ListAlertHistoryRequest(id=self.id) endpoint = "/api/v1/alerts/listAlertHistory" response = self._conn.make_proto_request("POST", endpoint, body=msg) history = self._conn.must_proto_response(response, msg.Response).history return list(map(AlertHistoryItem, history))
def test_sample_find_base_preserves_aggregation_flags(self, summary): """alert.summary_sample_query fully represents sample_find_base. The client used to drop `store_aggregate` and `allow_aggregate_samples` because of how :class:`SummarySampleQuery` was written. """ # define sample_find_base sample_find_base = _SummaryService.FindSummarySampleRequest( filter=_SummaryService.FilterQuerySummarySample( find_summaries=summary.alerts._build_summary_query( )._to_proto_request(), ), page_number=1, page_limit=-1, # following two fields are not exposed through client store_aggregate=True, allow_aggregate_samples=True, ) # create alert directly via REST API alerter = FixedAlerter(comparison.GreaterThan(0.7)) msg = _AlertService.CreateAlertRequest(alert=_AlertService.Alert( name=_utils.generate_default_name(), monitored_entity_id=summary._monitored_entity_id, sample_find_base=sample_find_base, alerter_type=alerter._TYPE, alerter_fixed=alerter._as_proto(), ), ) endpoint = "/api/v1/alerts/createAlert" response = summary.alerts._conn.make_proto_request("POST", endpoint, body=msg) alert_id = summary.alerts._conn.must_proto_response( response, _AlertService.Alert, ).id # retrieve alert via client and verify that fields are preserved alert = summary.alerts.get(id=alert_id) retrieved_sample_find_base = alert.summary_sample_query._to_proto_request( ) assert retrieved_sample_find_base.store_aggregate assert retrieved_sample_find_base.allow_aggregate_samples # verify full equality, minus time field added by alert retrieved_sample_find_base.filter.ClearField("created_at_after_millis") assert retrieved_sample_find_base == sample_find_base
def test_create(self, comparison, threshold): alerter = FixedAlerter(comparison(threshold)) msg = _AlertService.AlertFixed( threshold=threshold, operator=comparison._OPERATOR, ) assert alerter._as_proto() == msg
def _update_last_evaluated_at(self, last_evaluated_at=None): if last_evaluated_at is None: last_evaluated_at = time_utils.now() millis = time_utils.epoch_millis(last_evaluated_at) alert_msg = _AlertService.Alert(last_evaluated_at_millis=millis, ) self._update(alert_msg)
def _update(self, alert_msg): alert_msg.id = self.id msg = _AlertService.UpdateAlertRequest(alert=alert_msg) endpoint = "/api/v1/alerts/updateAlert" response = self._conn.make_proto_request("POST", endpoint, body=msg) self._conn.must_response(response) self._clear_cache() return True
def test_from_proto(self, comparison, threshold): msg = _AlertService.AlertFixed( threshold=threshold, operator=comparison._OPERATOR, ) alerter = _Alerter._from_proto(msg) assert type(alerter) is FixedAlerter assert alerter._as_proto() == msg
def test_create(self, comparison, threshold, reference_sample_id): alerter = ReferenceAlerter(comparison(threshold), reference_sample_id) msg = _AlertService.AlertReference( threshold=threshold, reference_sample_id=reference_sample_id, operator=comparison._OPERATOR, ) assert alerter._as_proto() == msg
def _to_proto_request(self): msg = _AlertService.UpdateAlertStatusRequest( status=self._ALERT_STATUS, ) if self._sample_ids: msg.ok_sample_ids.extend(self._sample_ids) else: msg.clear_alerting_sample_ids = True return msg
def test_to_proto_request_no_sample_ids(self): status = Ok() proto_request = status._to_proto_request() assert proto_request == _AlertService.UpdateAlertStatusRequest( status=_AlertService.AlertStatusEnum.OK, alerting_sample_ids=[], ok_sample_ids=[], clear_alerting_sample_ids=True, )
def test_to_proto_request(self, sample_ids): status = Ok(sample_ids) proto_request = status._to_proto_request() assert proto_request == _AlertService.UpdateAlertStatusRequest( status=_AlertService.AlertStatusEnum.OK, alerting_sample_ids=[], ok_sample_ids=sample_ids, clear_alerting_sample_ids=False, )
def test_from_proto(self, lower_bound, upper_bound, alert_if_outside_range): msg = _AlertService.AlertRange( lower_bound=lower_bound, upper_bound=upper_bound, alert_if_outside_range=alert_if_outside_range, ) alerter = _Alerter._from_proto(msg) assert type(alerter) is RangeAlerter assert alerter._as_proto() == msg
def test_from_proto(self, comparison, threshold, reference_sample_id): msg = _AlertService.AlertReference( threshold=threshold, reference_sample_id=reference_sample_id, operator=comparison._OPERATOR, ) alerter = _Alerter._from_proto(msg) assert type(alerter) is ReferenceAlerter assert alerter._as_proto() == msg
def test_to_proto(self, lower_bound, upper_bound, alert_if_outside_range): alerter = RangeAlerter( lower_bound=lower_bound, upper_bound=upper_bound, alert_if_outside_range=alert_if_outside_range, ) msg = _AlertService.AlertRange( lower_bound=lower_bound, upper_bound=upper_bound, alert_if_outside_range=alert_if_outside_range, ) assert alerter._as_proto() == msg
def _get_proto_by_id(cls, conn, id): msg = _AlertService.FindAlertRequest( ids=[int(id)], page_number=1, page_limit=-1, ) endpoint = "/api/v1/alerts/findAlert" response = conn.make_proto_request("POST", endpoint, body=msg) alerts = conn.must_proto_response(response, msg.Response).alerts if len(alerts) > 1: warnings.warn( "unexpectedly found multiple alerts with the same name and" " monitored entity ID") return alerts[0]
def _get_proto_by_id(cls, conn, id): msg = _AlertService.FindNotificationChannelRequest( ids=[int(id)], page_number=1, page_limit=-1, ) endpoint = "/api/v1/alerts/findNotificationChannel" response = conn.make_proto_request("POST", endpoint, body=msg) channels = conn.must_proto_response(response, msg.Response).channels if len(channels) > 1: warnings.warn( "unexpectedly found multiple notification channels with ID" " {}".format(id) ) if channels: return channels[0] else: return None
def delete(self): """ Delete this alert. Returns ------- bool ``True`` if the delete was successful. Raises ------ :class:`requests.HTTPError` If the delete failed. """ msg = _AlertService.DeleteAlertRequest(ids=[self.id]) endpoint = "/api/v1/alerts/deleteAlert" response = self._conn.make_proto_request("DELETE", endpoint, body=msg) self._conn.must_response(response) return True
def _as_proto(self): return _AlertService.AlertFixed( threshold=self._comparison.value, operator=self._comparison._operator_as_proto(), )
def _as_proto(self): return _AlertService.AlertReference( threshold=self._comparison.value, reference_sample_id=self._reference_sample_id, operator=self._comparison._operator_as_proto(), )
def test_create(self, webhook_url): slack_channel = SlackNotificationChannel(webhook_url) assert slack_channel._url == webhook_url msg = _AlertService.NotificationChannelSlackWebhook(url=webhook_url, ) assert slack_channel._as_proto() == msg
def _to_proto_request(self): return _AlertService.UpdateAlertStatusRequest( status=self._ALERT_STATUS, alerting_sample_ids=self._sample_ids, )
def _as_proto(self): return _AlertService.AlertRange( lower_bound=self.lower_bound, upper_bound=self.upper_bound, alert_if_outside_range=self.alert_if_outside_range, )
def __init__(self, conn, conf): super(AlertsPaginatedIterable, self).__init__( conn, conf, _AlertService.FindAlertRequest(), )