From 82f80ec2b52cf545c7043fd492772343a9ead94d Mon Sep 17 00:00:00 2001 From: Ganesh Date: Mon, 12 Apr 2021 21:47:09 -0700 Subject: [PATCH 01/10] Added supporting methods for composite alerts --- argusclient/client.py | 168 +++++++++++++++++++++++++++++++-------- tests/test_data.py | 60 ++++++++++++++ tests/test_service.py | 180 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 376 insertions(+), 32 deletions(-) diff --git a/argusclient/client.py b/argusclient/client.py index 92e94a9..6d361ad 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -18,10 +18,11 @@ import os import logging import collections + try: import http.client as httplib # Python 3 except ImportError: - import httplib # Python 2 + import httplib # Python 2 from functools import wraps from .model import Namespace, Metric, Annotation, Dashboard, Alert, Trigger, Notification, JsonEncoder, JsonDecoder, \ @@ -32,24 +33,28 @@ REQ_PARAMS = "req_params" REQ_BODY = "req_body" + class ArgusException(Exception): """ An exception type that is thrown for Argus service errors. """ pass + class ArgusAuthException(ArgusException): """ An exception type that is thrown for Argus authentication errors. """ pass + class ArgusObjectNotFoundException(ArgusException): """ An exception type that is thrown for Argus object not found errors. """ pass + class BaseQuery(object): def __init__(self, baseExpr, *tailParams, **kwargs): self.baseExpr = baseExpr @@ -80,9 +85,12 @@ class MetricQuery(BaseQuery): >>> print str(mquery) -1d:-0d:test.scope:test.metric{test.tag=test.value}:sum:test.namespace """ - def __init__(self, scope, metric, aggregator, tags=None, namespace=None, downsampler=None, stTimeSpec=None, enTimeSpec=None): + + def __init__(self, scope, metric, aggregator, tags=None, namespace=None, downsampler=None, stTimeSpec=None, + enTimeSpec=None): # NOTE: Namespace no longer goes into the metric expression, so we pass it down as a tail parameter. - super(MetricQuery, self).__init__(str(Metric(scope, metric, tags=tags)), aggregator, downsampler, namespace, stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) + super(MetricQuery, self).__init__(str(Metric(scope, metric, tags=tags)), aggregator, downsampler, namespace, + stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) class AnnotationQuery(BaseQuery): @@ -94,8 +102,10 @@ class AnnotationQuery(BaseQuery): >>> print str(mquery) -1d:-0d:test.scope:test.metric{test.tag=test.value}:test.source """ + def __init__(self, scope, metric, source, tags=None, stTimeSpec=None, enTimeSpec=None): - super(AnnotationQuery, self).__init__(str(Annotation(source, scope, metric, None, None, None, tags=tags)), stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) + super(AnnotationQuery, self).__init__(str(Annotation(source, scope, metric, None, None, None, tags=tags)), + stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) class BaseCollectionServiceClient(object): @@ -121,7 +131,8 @@ def add(self, data): :return: :class:`argusclient.model.AddListResult` object with a summary of the operation. """ if not data: raise ValueError("need a value for data parameter") - if not isinstance(data, list) or not isinstance(data[0], self.obj_type): raise TypeError("data should be a list of %s objects" % self.obj_type) + if not isinstance(data, list) or not isinstance(data[0], self.obj_type): raise TypeError( + "data should be a list of %s objects" % self.obj_type) return self.argus._request("post", self.coll_path, dataObj=data) @@ -131,6 +142,7 @@ class MetricCollectionServiceClient(BaseCollectionServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.metrics` attribute. """ + def __init__(self, argus): super(MetricCollectionServiceClient, self).__init__(MetricQuery, Metric, argus, "metrics", "collection/metrics") @@ -141,8 +153,10 @@ class AnnotationCollectionServiceClient(BaseCollectionServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.annotations` attribute. """ + def __init__(self, argus): - super(AnnotationCollectionServiceClient, self).__init__(AnnotationQuery, Annotation, argus, "annotations", "collection/annotations") + super(AnnotationCollectionServiceClient, self).__init__(AnnotationQuery, Annotation, argus, "annotations", + "collection/annotations") class BaseModelServiceClient(object): @@ -163,9 +177,11 @@ def _init_all(self, coll=None): if not self._retrieved_all: self._coll = dict((obj.argus_id, self._fill(obj)) for obj in (coll or self.argus._request(self.get_all_req_opts.get(REQ_METHOD, "get"), - self.get_all_req_opts.get(REQ_PATH, None), - params=self.get_all_req_opts.get(REQ_PARAMS, None), - dataObj=self.get_all_req_opts.get(REQ_BODY, None)))) + self.get_all_req_opts.get(REQ_PATH, None), + params=self.get_all_req_opts.get(REQ_PARAMS, + None), + dataObj=self.get_all_req_opts.get(REQ_BODY, + None)))) self._retrieved_all = True def _fill(self, obj): @@ -246,6 +262,7 @@ class UsersServiceClient(BaseModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.users` attribute. """ + def __init__(self, argus): super(UsersServiceClient, self).__init__(argus) self._coll_by_name = {} @@ -276,6 +293,7 @@ class NamespacesServiceClient(BaseModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.namespaces` attribute. """ + def __init__(self, argus): super(NamespacesServiceClient, self).__init__(argus, get_all_req_opts={REQ_PATH: "namespace"}) @@ -288,7 +306,8 @@ def update(self, id, namespace): if not id: raise ValueError("Need to specify an id to update namespace") id = int(id) if not namespace.argus_id: raise ValueError("Namespace needs an id to update") - if id != namespace.argus_id: raise ValueError("Namespace id: %s doesn't match the id: %s that you are updating" % (namespace.id, id)) + if id != namespace.argus_id: raise ValueError( + "Namespace id: %s doesn't match the id: %s that you are updating" % (namespace.id, id)) self._coll[id] = self.argus._request("put", "namespace/%s" % id, dataObj=namespace) return self._coll[id] @@ -342,7 +361,8 @@ def update(self, id, obj): if not isinstance(obj, self.objType): raise TypeError("Need an object of type: %s" % self.objType) if not obj.argus_id: raise ValueError("Object needs an id to update") # Ensure that user doesn't accidentally copy another item. - if id != obj.argus_id: raise ValueError("Object id: %s doesn't match the id: %s that you are updating" % (obj.id, id)) + if id != obj.argus_id: raise ValueError( + "Object id: %s doesn't match the id: %s that you are updating" % (obj.id, id)) self._coll[id] = self.argus._request("put", self.id_path % id, dataObj=obj) return self._coll[id] @@ -363,6 +383,7 @@ class DashboardsServiceClient(BaseUpdatableModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.dashboards` attribute. """ + def __init__(self, argus, get_all_req_opts=None): """ :param get_all_req_opts: See BaseModelServiceClient.__init__() for description. @@ -393,7 +414,8 @@ def get_user_dashboard(self, ownerName, dashboardName, shared=True): """ assert dashboardName, "Expected a dashboard name" assert ownerName, "Expected a owner name" - dashboards = self.argus._request("get", "dashboards", params=dict(dashboardName=dashboardName, owner=ownerName, shared=shared)) + dashboards = self.argus._request("get", "dashboards", + params=dict(dashboardName=dashboardName, owner=ownerName, shared=shared)) if not dashboards: return None else: @@ -407,7 +429,9 @@ def get_user_dashboards(self, ownerName=None, shared=True, limit=None, version=N :return: a list of :class:`argusclient.model.Dashboard` objects with all fields populated. """ - return self.argus._request("get", "dashboards", params=dict(owner=ownerName, shared=shared, limit=limit, version=version)) + return self.argus._request("get", "dashboards", + params=dict(owner=ownerName, shared=shared, limit=limit, version=version)) + class PermissionsServiceClient(BaseUpdatableModelServiceClient): """ @@ -415,6 +439,7 @@ class PermissionsServiceClient(BaseUpdatableModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.permissions` attribute. """ + def __init__(self, argus, get_all_req_opts=None): """ :param get_all_req_opts: See BaseModelServiceClient.__init__() for description. @@ -431,9 +456,9 @@ def _init_all(self, coll=None): raise TypeError("Unsupported operation on: %s" % type(self)) if not self._retrieved_all: resp = convert(self.argus._request(self.get_all_req_opts.get(REQ_METHOD, "get"), - self.get_all_req_opts.get(REQ_PATH, None), - params=self.get_all_req_opts.get(REQ_PARAMS, None), - dataObj=self.get_all_req_opts.get(REQ_BODY, None))) + self.get_all_req_opts.get(REQ_PATH, None), + params=self.get_all_req_opts.get(REQ_PARAMS, None), + dataObj=self.get_all_req_opts.get(REQ_BODY, None))) for id, perms in resp.items(): self._coll[id] = perms self._retrieved_all = True @@ -446,6 +471,7 @@ def get_permissions_for_entities(self, entityIds): """ return convert(self.argus._request("post", "permission/entityIds", dataObj=entityIds)) + def convert(input): if isinstance(input, Mapping): return {convert(key): convert(value) for key, value in input.iteritems()} @@ -479,6 +505,7 @@ class AlertsServiceClient(BaseUpdatableModelServiceClient): Interfaces with the Argus alert notifications endpoint. """ + def __init__(self, argus, get_all_req_opts=None): """ :param get_all_req_opts: See BaseModelServiceClient.__init__() for description. @@ -542,7 +569,8 @@ def get_notification_trigger(self, alertid, notificationid, triggerid): if not notificationid: raise ValueError("Need to specify a notificationid") if not triggerid: raise ValueError("Need to specify a triggerid") # TODO: Update self._coll - return self.argus._request("get", "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) + return self.argus._request("get", + "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) def add_notification_trigger(self, alertid, notificationid, triggerid): """ @@ -554,7 +582,8 @@ def add_notification_trigger(self, alertid, notificationid, triggerid): if not notificationid: raise ValueError("Need to specify a notificationid") if not triggerid: raise ValueError("Need to specify a triggerid") # TODO: Update self._coll - return self.argus._request("post", "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) + return self.argus._request("post", + "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) def delete_notification_trigger(self, alertid, notificationid, triggerid): """ @@ -574,7 +603,8 @@ def get_user_alert(self, ownerName, alertName, shared=True): """ assert alertName, "Expected an alert name" assert ownerName, "Expected a owner name" - alerts = self.argus._request("get", "alerts/meta", params=dict(alertname=alertName, ownername=ownerName, shared=shared)) + alerts = self.argus._request("get", "alerts/meta", + params=dict(alertname=alertName, ownername=ownerName, shared=shared)) if not alerts: return None else: @@ -589,8 +619,75 @@ def get_alerts_allinfo(self, ownerName=None, alertNameContains=None, shared=Fals :return: the list of :class:`argusclient.model.Alert` objects, with all fields populated, including triggers and notifications """ - return self.argus._request("get", "alerts/allinfo", params=dict(ownername=ownerName, alertNameContains=alertNameContains, shared=shared, limit=limit)) + return self.argus._request("get", "alerts/allinfo", + params=dict(ownername=ownerName, alertNameContains=alertNameContains, shared=shared, + limit=limit)) + + ''' + Functions for supporting composite alerts + ''' + + def get_composite_alert_children_info(self, alert_id): + if not alert_id: raise ValueError("Need to specify alert id") + uri_path = "alerts/{}/children/info".format(alert_id) + return self.argus._request("get", uri_path) + + def create_composite_alert(self, payload): + if not payload: raise ValueError("Need to specify an payload") + alert_obj = self.argus._request("post", "alerts", dataObj=payload) + self._coll[alert_obj.id] = alert_obj + return alert_obj + + def add_child_alert_to_composite_alert(self, alert_id, payload): + if not alert_id: raise ValueError("Need to specify composite alert id") + if not payload: raise ValueError("Need to specify payload") + uri_path = "alerts/{}/children".format(alert_id) + alert_obj = self.argus._request("post", uri_path, dataObj=payload) + self._coll[alert_obj.id] = alert_obj + return alert_obj + + def update_composite_alert(self, alert_id, payload): + if not alert_id: raise ValueError("Need to specify composite alert id") + if not payload: raise ValueError("Need to specify payload") + uri_path = "alerts/{}".format(alert_id) + return self.argus._request("put", uri_path, dataObj=payload) + + def delete_child_alert_from_composite_alert(self, comp_alert_id, child_alert_id): + """ + Delete a child alert from a composite alert + """ + if not comp_alert_id: raise ValueError("Need to specify composite alertid") + if not child_alert_id: raise ValueError("Need to specify a child alertid") + uri_path = "alerts/{}/children/{}".format(comp_alert_id, child_alert_id) + if id in self._coll: + del self._coll[child_alert_id] + return self.argus._request("delete", uri_path) + + def add_trigger_to_child_alert(self, alert_id, trigger): + if not alert_id: raise ValueError("Need to specify alertid") + if not isinstance(trigger, Trigger): raise TypeError("Need a Trigger object, got: %s" % type(trigger)) + uri_path = "alerts/{}/triggers".format(alert_id) + return self.argus._request("post", uri_path, dataObj=trigger) + + def del_trigger_from_child_alert(self, alert_id, trigger_id): + if not alert_id: raise ValueError("Need to specify alert id") + if not trigger_id: raise ValueError("Need to specify trigger id") + uri_path = "alerts/{}/triggers/{}".format(alert_id, trigger_id) + return self.argus._request("delete", uri_path) + + def add_notification(self, alert, notification): + if not isinstance(alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(alert)) + if not isinstance(notification, Notification): raise TypeError( + "Need a Notification object, got: %s" % type(notification)) + uri_path = "alerts/{}/notifications".format(alert.id) + notification_obj = self.argus._request("post", uri_path, dataObj=notification) + return notification_obj + def del_notification(self, alert, notification_id): + if not isinstance(alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(alert)) + if not isinstance(notification_id, int): raise TypeError("Need a Notifications id, got: %s" % type(alert)) + uri_path = "alerts/{}/notifications/{}".format(alert.id, notification_id) + return self.argus._request("delete", uri_path) class AlertTriggersServiceClient(BaseUpdatableModelServiceClient): """ @@ -622,7 +719,8 @@ def add(self, trigger): try: return next(t for t in triggers if t.name == trigger.name) except StopIteration: - raise ArgusException("This is unexpected... trigger: %s not found after successfully adding it" % trigger.name) + raise ArgusException( + "This is unexpected... trigger: %s not found after successfully adding it" % trigger.name) def delete(self, id): super(AlertTriggersServiceClient, self).delete(id) @@ -635,11 +733,14 @@ class AlertNotificationsServiceClient(BaseUpdatableModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.AlertsServiceClient.notifications` attribute. """ + def __init__(self, argus, alert): assert alert, "Expected an alert at this point" assert alert.id, "Alert expected to have an id at this point" - super(AlertNotificationsServiceClient, self).__init__(Notification, argus, id_path="alerts/%s/notifications/%%s" % alert.id, - get_all_req_opts={REQ_PATH: "alerts/%s/notifications" % alert.id}) + super(AlertNotificationsServiceClient, self).__init__(Notification, argus, + id_path="alerts/%s/notifications/%%s" % alert.id, + get_all_req_opts={ + REQ_PATH: "alerts/%s/notifications" % alert.id}) self.alert = alert if alert.notifications: self._init_all(alert.notifications) @@ -650,7 +751,8 @@ def add(self, notification): :return: the added :class:`argusclient.model.Notification` with all fields populated. """ - if not isinstance(notification, Notification): raise TypeError("Need a Notification object, got: %s" % type(notification)) + if not isinstance(notification, Notification): raise TypeError( + "Need a Notification object, got: %s" % type(notification)) if notification.argus_id: raise ValueError("A new Notification can't have an id") notifications = self.argus._request("post", "alerts/%s/notifications" % self.alert.id, dataObj=notification) self._init_all(notifications) @@ -658,7 +760,8 @@ def add(self, notification): try: return next(n for n in notifications if n.name == notification.name) except StopIteration: - raise ArgusException("This is unexpected... notification: %s not found after successfully adding it" % notification.name) + raise ArgusException( + "This is unexpected... notification: %s not found after successfully adding it" % notification.name) def delete(self, id): super(AlertNotificationsServiceClient, self).delete(id) @@ -689,8 +792,8 @@ def with_auth_token(*args, **kwargs): if not argus.accessToken and argus.refreshToken: try: res = argus._request_no_auth("post", - "v2/auth/token/refresh", - dataObj=dict(refreshToken=argus.refreshToken)) + "v2/auth/token/refresh", + dataObj=dict(refreshToken=argus.refreshToken)) argus.accessToken = res["accessToken"] except ArgusAuthException: if argus.password: @@ -700,8 +803,8 @@ def with_auth_token(*args, **kwargs): if not argus.accessToken and argus.password: argus.refreshToken = None res = argus._request_no_auth("post", - "v2/auth/login", - dataObj=dict(username=argus.user, password=argus.password)) + "v2/auth/login", + dataObj=dict(username=argus.user, password=argus.password)) argus.refreshToken, argus.accessToken = res["refreshToken"], res["accessToken"] try: @@ -818,7 +921,7 @@ def logout(self): Logs out of the Argus service and destroys the session. """ # The new V2 auth doesn't support a logout, so just clear the tokens. - #self._request("get", "auth/logout") + # self._request("get", "auth/logout") self.refreshToken = self.accessToken = None @retry_auth @@ -846,11 +949,12 @@ def _request_no_auth(self, method, path, params=None, dataObj=None, encCls=JsonE url = os.path.join(self.endpoint, path) req_method = getattr(self.conn, method) data = dataObj and json.dumps(dataObj, cls=encCls) or None - logging.debug("%s request with params: %s data length %s on: %s", method.upper(), params, data and len(data) or 0, url) # Mainly for the sake of data length + logging.debug("%s request with params: %s data length %s on: %s", method.upper(), params, + data and len(data) or 0, url) # Mainly for the sake of data length # Argus seems to recognized "Accept" header for "application/json" and "application/ms-excel", but the former is the default. headers = {"Content-Type": "application/json"} if self.accessToken: - headers["Authorization"] = "Bearer "+self.accessToken + headers["Authorization"] = "Bearer " + self.accessToken resp = req_method(url, data=data, params=params, headers=headers, timeout=self.timeout) diff --git a/tests/test_data.py b/tests/test_data.py index ba0b3e7..eb67748 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -39,6 +39,12 @@ permissionNames = ["VIEW", "EDIT", "DELETE"] permissionGroupId = '24231-52321-43523-64353-23111' +compAlertID = 6000 +childAlertID_1 = 6003 +childAlertID_2 = 6009 +triggerID_1 = 6005 +compAlert_notificationID = 6007 + metric_D = { "scope": scope, "metric": metric, @@ -296,4 +302,58 @@ "triggers": [trigger_D, trigger_2_D], "notifications": [notification_D, notification_2_D, notification_3_D], "ownerName": userName +} + +compalert_D = { + 'id': compAlertID, + 'alertType': 'COMPOSITE', + 'name': 'CompAlertTest', + 'triggerIds': [], + 'enabled': False, + 'cronEntry': '*/2 * * * *', + 'notificationsIds': [], + 'expression': {'expression': {'operator': 'AND', 'join': [], 'terms': []}, 'termDefinitions': []}, + 'childAlertsIds': [] + } + +childAlert_1 = { + 'id': childAlertID_1, + 'triggersIds': [], + 'alertType': 'COMPOSITE_CHILD', + 'name': 'CompAlertTest-ChildAlert-1', + 'cronEntry': '* * * * *', + 'notificationsIds': [], + 'enabled': False, + 'expression': '-1h:-0m:tracer.api.XRD.NONE.none:requestsReceivedLastMin:avg:1m-avg' +} + +childAlert_2 = { + 'id': childAlertID_2, + 'triggersIds': [], + 'alertType': 'COMPOSITE_CHILD', + 'name': 'CompAlertTest-ChildAlert-2', + 'cronEntry': '* * * * *', + 'notificationsIds': [], + 'enabled': False, + 'expression': '-1h:-0m:tracer.api.XRD.NONE.none:avgQueryTime:avg:1m-avg' +} + +childAlert_trigger_1 = { + 'id': triggerID_1, + 'threshold': 1.0, + 'type': 'GREATER_THAN', + 'inertia': 0L, + 'name': 'CompAlertTest/trigger1' +} + +compAlert_notification = { + 'id': compAlert_notificationID, + 'severityLevel': 5, + 'name': 'Email1', + 'subscriptions': ['jdoe@example.com'], + 'notifierName': 'com.salesforce.dva.argus.service.alert.notifier.EmailNotifier', + 'metricsToAnnotate': [], + 'cooldownPeriod': 0L, + 'sractionable': False, + 'customText': 'None' } \ No newline at end of file diff --git a/tests/test_service.py b/tests/test_service.py index 2220b17..0fb0b44 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -830,3 +830,183 @@ def testGetAlertWithMultipleTriggers(self): self.assertEquals(len(alert.triggers), 2) self.assertEquals(alert.triggers[100].argus_id, 100) self.assertEquals(alert.triggers[101].argus_id, 101) + + + +class TestCompAlert(TestServiceBase): + + @mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) + def testAddCompAlert(self, mockPost): + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + res = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(res, Alert)) + self.assertTrue(hasattr(res, "id")) + self.assertEqual(res.expression['expression']['operator'], 'AND') + + + @mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) + def testAddChildAlert(self, mockPost): + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)): + child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, Alert.from_dict(childAlert_1)) + self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') + self.assertTrue(isinstance(child_alert, Alert)) + + @mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) + def testAddTriggerToChildAlert(self, mockPost): + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)) as mock_add_childalert: + child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, + Alert.from_dict(childAlert_1)) + self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') + self.assertTrue(isinstance(child_alert, Alert)) + call_args = tuple(mock_add_childalert.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/children".format(comp_alert.id)) + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_trigger_1), 200)) as mock_trigger_post: + trigger = self.argus.alerts.add_trigger_to_child_alert(child_alert.id, Trigger.from_dict(childAlert_trigger_1)) + self.assertTrue(isinstance(trigger, Trigger)) + call_args = tuple(mock_trigger_post.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/triggers".format(child_alert.id)) + self.assertIn((uri_path,), call_args) + + def testAddNotification(self): + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_add_comp_alert: + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + call_args = mock_add_comp_alert.call_args + uri_path = os.path.join(endpoint, "alerts") + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compAlert_notification), 200)) as mock_notification: + notification = self.argus.alerts.add_notification(comp_alert, Notification.from_dict(compAlert_notification)) + self.assertTrue(isinstance(notification, Notification)) + call_args = mock_notification.call_args + uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) + self.assertIn((uri_path,), call_args) + + + def testDeleteChildAlert(self): + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_alert: + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + call_args = mock_alert.call_args + uri_path = os.path.join(endpoint, "alerts") + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)): + child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, Alert.from_dict(childAlert_1)) + self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') + self.assertTrue(isinstance(child_alert, Alert)) + + with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: + self.argus.alerts.delete_child_alert_from_composite_alert(comp_alert.id, child_alert.id) + call_args = mock_delete.call_args + uri_path = os.path.join(endpoint, "alerts/{}/children/{}".format(comp_alert.id, child_alert.id)) + self.assertIn((uri_path,), call_args) + + + def testDeleteTriggerFromChildAlert(self): + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_alert: + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)) as mock_add_childalert: + child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, + Alert.from_dict(childAlert_1)) + self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') + self.assertTrue(isinstance(child_alert, Alert)) + call_args = tuple(mock_add_childalert.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/children".format(comp_alert.id)) + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_trigger_1), 200)) as mock_trigger_post: + trigger = self.argus.alerts.add_trigger_to_child_alert(child_alert.id, Trigger.from_dict(childAlert_trigger_1)) + self.assertTrue(isinstance(trigger, Trigger)) + call_args = tuple(mock_trigger_post.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/triggers".format(child_alert.id)) + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: + self.argus.alerts.del_trigger_from_child_alert(child_alert.id, trigger.id) + call_args = tuple(mock_trigger_post.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/triggers".format(child_alert.id)) + self.assertIn((uri_path,), call_args) + + def testDeleteNotification(self): + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_add_comp_alert: + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + call_args = mock_add_comp_alert.call_args + uri_path = os.path.join(endpoint, "alerts") + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compAlert_notification), 200)) as mock_notification: + notification = self.argus.alerts.add_notification(comp_alert, Notification.from_dict(compAlert_notification)) + self.assertTrue(isinstance(notification, Notification)) + call_args = mock_notification.call_args + uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: + self.argus.alerts.del_notification(comp_alert, notification.id) + call_args = tuple(mock_delete.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/notifications/{}".format(comp_alert.id, notification.id)) + self.assertIn((uri_path,), call_args) + + def testGetCompAlertChildrenInfo(self): + with mock.patch('requests.Session.get', return_value=MockResponse(json.dumps([childAlert_1, childAlert_2]), 200)) as mock_get: + res = self.argus.alerts.get_composite_alert_children_info(compAlertID) + if res: + for obj in res: + self.assertTrue(isinstance(obj, Alert)) + call_args = tuple(mock_get.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/children/info".format(compAlertID)) + self.assertIn((uri_path,), call_args) + + def testUpdateCompAlert(self): + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_add_comp_alert: + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + call_args = mock_add_comp_alert.call_args + uri_path = os.path.join(endpoint, "alerts") + self.assertIn((uri_path,), call_args) + + with mock.patch('requests.Session.put', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_update: + self.argus.alerts.update_composite_alert(compAlertID, Alert.from_dict(compalert_D)) + alert_obj = self.argus.alerts.get(compAlertID) + self.assertTrue(isinstance(alert_obj, Alert)) + alert_obj_dict = alert_obj.to_dict() + alert_dict = compalert_D + self.assertEquals(alert_obj_dict, alert_dict) + call_args = mock_update.call_args + uri_path = os.path.join(endpoint, "alerts/{}".format(compAlertID)) + self.assertIn((uri_path,), call_args) \ No newline at end of file From 905c492848209463e3b2b136dbecffc90fcead4d Mon Sep 17 00:00:00 2001 From: Ganesh Date: Thu, 15 Apr 2021 08:37:19 -0700 Subject: [PATCH 02/10] Reformatted files --- argusclient/client.py | 106 ++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 72 deletions(-) diff --git a/argusclient/client.py b/argusclient/client.py index 6d361ad..406189b 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -18,11 +18,10 @@ import os import logging import collections - try: import http.client as httplib # Python 3 except ImportError: - import httplib # Python 2 + import httplib # Python 2 from functools import wraps from .model import Namespace, Metric, Annotation, Dashboard, Alert, Trigger, Notification, JsonEncoder, JsonDecoder, \ @@ -33,28 +32,24 @@ REQ_PARAMS = "req_params" REQ_BODY = "req_body" - class ArgusException(Exception): """ An exception type that is thrown for Argus service errors. """ pass - class ArgusAuthException(ArgusException): """ An exception type that is thrown for Argus authentication errors. """ pass - class ArgusObjectNotFoundException(ArgusException): """ An exception type that is thrown for Argus object not found errors. """ pass - class BaseQuery(object): def __init__(self, baseExpr, *tailParams, **kwargs): self.baseExpr = baseExpr @@ -85,12 +80,9 @@ class MetricQuery(BaseQuery): >>> print str(mquery) -1d:-0d:test.scope:test.metric{test.tag=test.value}:sum:test.namespace """ - - def __init__(self, scope, metric, aggregator, tags=None, namespace=None, downsampler=None, stTimeSpec=None, - enTimeSpec=None): + def __init__(self, scope, metric, aggregator, tags=None, namespace=None, downsampler=None, stTimeSpec=None, enTimeSpec=None): # NOTE: Namespace no longer goes into the metric expression, so we pass it down as a tail parameter. - super(MetricQuery, self).__init__(str(Metric(scope, metric, tags=tags)), aggregator, downsampler, namespace, - stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) + super(MetricQuery, self).__init__(str(Metric(scope, metric, tags=tags)), aggregator, downsampler, namespace, stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) class AnnotationQuery(BaseQuery): @@ -102,10 +94,8 @@ class AnnotationQuery(BaseQuery): >>> print str(mquery) -1d:-0d:test.scope:test.metric{test.tag=test.value}:test.source """ - def __init__(self, scope, metric, source, tags=None, stTimeSpec=None, enTimeSpec=None): - super(AnnotationQuery, self).__init__(str(Annotation(source, scope, metric, None, None, None, tags=tags)), - stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) + super(AnnotationQuery, self).__init__(str(Annotation(source, scope, metric, None, None, None, tags=tags)), stTimeSpec=stTimeSpec, enTimeSpec=enTimeSpec) class BaseCollectionServiceClient(object): @@ -131,8 +121,7 @@ def add(self, data): :return: :class:`argusclient.model.AddListResult` object with a summary of the operation. """ if not data: raise ValueError("need a value for data parameter") - if not isinstance(data, list) or not isinstance(data[0], self.obj_type): raise TypeError( - "data should be a list of %s objects" % self.obj_type) + if not isinstance(data, list) or not isinstance(data[0], self.obj_type): raise TypeError("data should be a list of %s objects" % self.obj_type) return self.argus._request("post", self.coll_path, dataObj=data) @@ -142,7 +131,6 @@ class MetricCollectionServiceClient(BaseCollectionServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.metrics` attribute. """ - def __init__(self, argus): super(MetricCollectionServiceClient, self).__init__(MetricQuery, Metric, argus, "metrics", "collection/metrics") @@ -153,10 +141,8 @@ class AnnotationCollectionServiceClient(BaseCollectionServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.annotations` attribute. """ - def __init__(self, argus): - super(AnnotationCollectionServiceClient, self).__init__(AnnotationQuery, Annotation, argus, "annotations", - "collection/annotations") + super(AnnotationCollectionServiceClient, self).__init__(AnnotationQuery, Annotation, argus, "annotations", "collection/annotations") class BaseModelServiceClient(object): @@ -177,11 +163,9 @@ def _init_all(self, coll=None): if not self._retrieved_all: self._coll = dict((obj.argus_id, self._fill(obj)) for obj in (coll or self.argus._request(self.get_all_req_opts.get(REQ_METHOD, "get"), - self.get_all_req_opts.get(REQ_PATH, None), - params=self.get_all_req_opts.get(REQ_PARAMS, - None), - dataObj=self.get_all_req_opts.get(REQ_BODY, - None)))) + self.get_all_req_opts.get(REQ_PATH, None), + params=self.get_all_req_opts.get(REQ_PARAMS, None), + dataObj=self.get_all_req_opts.get(REQ_BODY, None)))) self._retrieved_all = True def _fill(self, obj): @@ -262,7 +246,6 @@ class UsersServiceClient(BaseModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.users` attribute. """ - def __init__(self, argus): super(UsersServiceClient, self).__init__(argus) self._coll_by_name = {} @@ -293,7 +276,6 @@ class NamespacesServiceClient(BaseModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.namespaces` attribute. """ - def __init__(self, argus): super(NamespacesServiceClient, self).__init__(argus, get_all_req_opts={REQ_PATH: "namespace"}) @@ -306,8 +288,7 @@ def update(self, id, namespace): if not id: raise ValueError("Need to specify an id to update namespace") id = int(id) if not namespace.argus_id: raise ValueError("Namespace needs an id to update") - if id != namespace.argus_id: raise ValueError( - "Namespace id: %s doesn't match the id: %s that you are updating" % (namespace.id, id)) + if id != namespace.argus_id: raise ValueError("Namespace id: %s doesn't match the id: %s that you are updating" % (namespace.id, id)) self._coll[id] = self.argus._request("put", "namespace/%s" % id, dataObj=namespace) return self._coll[id] @@ -361,8 +342,7 @@ def update(self, id, obj): if not isinstance(obj, self.objType): raise TypeError("Need an object of type: %s" % self.objType) if not obj.argus_id: raise ValueError("Object needs an id to update") # Ensure that user doesn't accidentally copy another item. - if id != obj.argus_id: raise ValueError( - "Object id: %s doesn't match the id: %s that you are updating" % (obj.id, id)) + if id != obj.argus_id: raise ValueError("Object id: %s doesn't match the id: %s that you are updating" % (obj.id, id)) self._coll[id] = self.argus._request("put", self.id_path % id, dataObj=obj) return self._coll[id] @@ -383,7 +363,6 @@ class DashboardsServiceClient(BaseUpdatableModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.dashboards` attribute. """ - def __init__(self, argus, get_all_req_opts=None): """ :param get_all_req_opts: See BaseModelServiceClient.__init__() for description. @@ -414,8 +393,7 @@ def get_user_dashboard(self, ownerName, dashboardName, shared=True): """ assert dashboardName, "Expected a dashboard name" assert ownerName, "Expected a owner name" - dashboards = self.argus._request("get", "dashboards", - params=dict(dashboardName=dashboardName, owner=ownerName, shared=shared)) + dashboards = self.argus._request("get", "dashboards", params=dict(dashboardName=dashboardName, owner=ownerName, shared=shared)) if not dashboards: return None else: @@ -429,9 +407,7 @@ def get_user_dashboards(self, ownerName=None, shared=True, limit=None, version=N :return: a list of :class:`argusclient.model.Dashboard` objects with all fields populated. """ - return self.argus._request("get", "dashboards", - params=dict(owner=ownerName, shared=shared, limit=limit, version=version)) - + return self.argus._request("get", "dashboards", params=dict(owner=ownerName, shared=shared, limit=limit, version=version)) class PermissionsServiceClient(BaseUpdatableModelServiceClient): """ @@ -439,7 +415,6 @@ class PermissionsServiceClient(BaseUpdatableModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.ArgusServiceClient.permissions` attribute. """ - def __init__(self, argus, get_all_req_opts=None): """ :param get_all_req_opts: See BaseModelServiceClient.__init__() for description. @@ -456,9 +431,9 @@ def _init_all(self, coll=None): raise TypeError("Unsupported operation on: %s" % type(self)) if not self._retrieved_all: resp = convert(self.argus._request(self.get_all_req_opts.get(REQ_METHOD, "get"), - self.get_all_req_opts.get(REQ_PATH, None), - params=self.get_all_req_opts.get(REQ_PARAMS, None), - dataObj=self.get_all_req_opts.get(REQ_BODY, None))) + self.get_all_req_opts.get(REQ_PATH, None), + params=self.get_all_req_opts.get(REQ_PARAMS, None), + dataObj=self.get_all_req_opts.get(REQ_BODY, None))) for id, perms in resp.items(): self._coll[id] = perms self._retrieved_all = True @@ -471,7 +446,6 @@ def get_permissions_for_entities(self, entityIds): """ return convert(self.argus._request("post", "permission/entityIds", dataObj=entityIds)) - def convert(input): if isinstance(input, Mapping): return {convert(key): convert(value) for key, value in input.iteritems()} @@ -505,7 +479,6 @@ class AlertsServiceClient(BaseUpdatableModelServiceClient): Interfaces with the Argus alert notifications endpoint. """ - def __init__(self, argus, get_all_req_opts=None): """ :param get_all_req_opts: See BaseModelServiceClient.__init__() for description. @@ -569,8 +542,7 @@ def get_notification_trigger(self, alertid, notificationid, triggerid): if not notificationid: raise ValueError("Need to specify a notificationid") if not triggerid: raise ValueError("Need to specify a triggerid") # TODO: Update self._coll - return self.argus._request("get", - "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) + return self.argus._request("get", "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) def add_notification_trigger(self, alertid, notificationid, triggerid): """ @@ -582,8 +554,7 @@ def add_notification_trigger(self, alertid, notificationid, triggerid): if not notificationid: raise ValueError("Need to specify a notificationid") if not triggerid: raise ValueError("Need to specify a triggerid") # TODO: Update self._coll - return self.argus._request("post", - "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) + return self.argus._request("post", "alerts/%s/notifications/%s/triggers/%s" % (alertid, notificationid, triggerid)) def delete_notification_trigger(self, alertid, notificationid, triggerid): """ @@ -603,8 +574,7 @@ def get_user_alert(self, ownerName, alertName, shared=True): """ assert alertName, "Expected an alert name" assert ownerName, "Expected a owner name" - alerts = self.argus._request("get", "alerts/meta", - params=dict(alertname=alertName, ownername=ownerName, shared=shared)) + alerts = self.argus._request("get", "alerts/meta", params=dict(alertname=alertName, ownername=ownerName, shared=shared)) if not alerts: return None else: @@ -619,12 +589,10 @@ def get_alerts_allinfo(self, ownerName=None, alertNameContains=None, shared=Fals :return: the list of :class:`argusclient.model.Alert` objects, with all fields populated, including triggers and notifications """ - return self.argus._request("get", "alerts/allinfo", - params=dict(ownername=ownerName, alertNameContains=alertNameContains, shared=shared, - limit=limit)) + return self.argus._request("get", "alerts/allinfo", params=dict(ownername=ownerName, alertNameContains=alertNameContains, shared=shared, limit=limit)) ''' - Functions for supporting composite alerts + Functions for enabling composite alerts ''' def get_composite_alert_children_info(self, alert_id): @@ -689,6 +657,7 @@ def del_notification(self, alert, notification_id): uri_path = "alerts/{}/notifications/{}".format(alert.id, notification_id) return self.argus._request("delete", uri_path) + class AlertTriggersServiceClient(BaseUpdatableModelServiceClient): """ Service class that interfaces with the Argus alert triggers endpoint. @@ -719,8 +688,7 @@ def add(self, trigger): try: return next(t for t in triggers if t.name == trigger.name) except StopIteration: - raise ArgusException( - "This is unexpected... trigger: %s not found after successfully adding it" % trigger.name) + raise ArgusException("This is unexpected... trigger: %s not found after successfully adding it" % trigger.name) def delete(self, id): super(AlertTriggersServiceClient, self).delete(id) @@ -733,14 +701,11 @@ class AlertNotificationsServiceClient(BaseUpdatableModelServiceClient): There is no need to instantiate this directly, as it is available as :attr:`argusclient.client.AlertsServiceClient.notifications` attribute. """ - def __init__(self, argus, alert): assert alert, "Expected an alert at this point" assert alert.id, "Alert expected to have an id at this point" - super(AlertNotificationsServiceClient, self).__init__(Notification, argus, - id_path="alerts/%s/notifications/%%s" % alert.id, - get_all_req_opts={ - REQ_PATH: "alerts/%s/notifications" % alert.id}) + super(AlertNotificationsServiceClient, self).__init__(Notification, argus, id_path="alerts/%s/notifications/%%s" % alert.id, + get_all_req_opts={REQ_PATH: "alerts/%s/notifications" % alert.id}) self.alert = alert if alert.notifications: self._init_all(alert.notifications) @@ -751,8 +716,7 @@ def add(self, notification): :return: the added :class:`argusclient.model.Notification` with all fields populated. """ - if not isinstance(notification, Notification): raise TypeError( - "Need a Notification object, got: %s" % type(notification)) + if not isinstance(notification, Notification): raise TypeError("Need a Notification object, got: %s" % type(notification)) if notification.argus_id: raise ValueError("A new Notification can't have an id") notifications = self.argus._request("post", "alerts/%s/notifications" % self.alert.id, dataObj=notification) self._init_all(notifications) @@ -760,8 +724,7 @@ def add(self, notification): try: return next(n for n in notifications if n.name == notification.name) except StopIteration: - raise ArgusException( - "This is unexpected... notification: %s not found after successfully adding it" % notification.name) + raise ArgusException("This is unexpected... notification: %s not found after successfully adding it" % notification.name) def delete(self, id): super(AlertNotificationsServiceClient, self).delete(id) @@ -792,8 +755,8 @@ def with_auth_token(*args, **kwargs): if not argus.accessToken and argus.refreshToken: try: res = argus._request_no_auth("post", - "v2/auth/token/refresh", - dataObj=dict(refreshToken=argus.refreshToken)) + "v2/auth/token/refresh", + dataObj=dict(refreshToken=argus.refreshToken)) argus.accessToken = res["accessToken"] except ArgusAuthException: if argus.password: @@ -803,8 +766,8 @@ def with_auth_token(*args, **kwargs): if not argus.accessToken and argus.password: argus.refreshToken = None res = argus._request_no_auth("post", - "v2/auth/login", - dataObj=dict(username=argus.user, password=argus.password)) + "v2/auth/login", + dataObj=dict(username=argus.user, password=argus.password)) argus.refreshToken, argus.accessToken = res["refreshToken"], res["accessToken"] try: @@ -921,7 +884,7 @@ def logout(self): Logs out of the Argus service and destroys the session. """ # The new V2 auth doesn't support a logout, so just clear the tokens. - # self._request("get", "auth/logout") + #self._request("get", "auth/logout") self.refreshToken = self.accessToken = None @retry_auth @@ -949,12 +912,11 @@ def _request_no_auth(self, method, path, params=None, dataObj=None, encCls=JsonE url = os.path.join(self.endpoint, path) req_method = getattr(self.conn, method) data = dataObj and json.dumps(dataObj, cls=encCls) or None - logging.debug("%s request with params: %s data length %s on: %s", method.upper(), params, - data and len(data) or 0, url) # Mainly for the sake of data length + logging.debug("%s request with params: %s data length %s on: %s", method.upper(), params, data and len(data) or 0, url) # Mainly for the sake of data length # Argus seems to recognized "Accept" header for "application/json" and "application/ms-excel", but the former is the default. headers = {"Content-Type": "application/json"} if self.accessToken: - headers["Authorization"] = "Bearer " + self.accessToken + headers["Authorization"] = "Bearer "+self.accessToken resp = req_method(url, data=data, params=params, headers=headers, timeout=self.timeout) From 71c873bd2d7802a4453ca505efb134a750083117 Mon Sep 17 00:00:00 2001 From: Ganesh Date: Sun, 18 Apr 2021 20:32:20 -0700 Subject: [PATCH 03/10] changed names for add/del_notification to add/del_notification_to/from_composite_alert --- argusclient/client.py | 4 ++-- tests/test_service.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/argusclient/client.py b/argusclient/client.py index 406189b..4893db6 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -643,7 +643,7 @@ def del_trigger_from_child_alert(self, alert_id, trigger_id): uri_path = "alerts/{}/triggers/{}".format(alert_id, trigger_id) return self.argus._request("delete", uri_path) - def add_notification(self, alert, notification): + def add_notification_to_composite_alert(self, alert, notification): if not isinstance(alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(alert)) if not isinstance(notification, Notification): raise TypeError( "Need a Notification object, got: %s" % type(notification)) @@ -651,7 +651,7 @@ def add_notification(self, alert, notification): notification_obj = self.argus._request("post", uri_path, dataObj=notification) return notification_obj - def del_notification(self, alert, notification_id): + def del_notification_from_composite_alert(self, alert, notification_id): if not isinstance(alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(alert)) if not isinstance(notification_id, int): raise TypeError("Need a Notifications id, got: %s" % type(alert)) uri_path = "alerts/{}/notifications/{}".format(alert.id, notification_id) diff --git a/tests/test_service.py b/tests/test_service.py index 0fb0b44..0bea32e 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -895,7 +895,7 @@ def testAddNotification(self): self.assertIn((uri_path,), call_args) with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compAlert_notification), 200)) as mock_notification: - notification = self.argus.alerts.add_notification(comp_alert, Notification.from_dict(compAlert_notification)) + notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, Notification.from_dict(compAlert_notification)) self.assertTrue(isinstance(notification, Notification)) call_args = mock_notification.call_args uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) @@ -967,14 +967,14 @@ def testDeleteNotification(self): self.assertIn((uri_path,), call_args) with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compAlert_notification), 200)) as mock_notification: - notification = self.argus.alerts.add_notification(comp_alert, Notification.from_dict(compAlert_notification)) + notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, Notification.from_dict(compAlert_notification)) self.assertTrue(isinstance(notification, Notification)) call_args = mock_notification.call_args uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) self.assertIn((uri_path,), call_args) with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: - self.argus.alerts.del_notification(comp_alert, notification.id) + self.argus.alerts.del_notification_from_composite_alert(comp_alert, notification.id) call_args = tuple(mock_delete.call_args) uri_path = os.path.join(endpoint, "alerts/{}/notifications/{}".format(comp_alert.id, notification.id)) self.assertIn((uri_path,), call_args) From ca27da1c69870b29f5007c12767adcf0a306e322 Mon Sep 17 00:00:00 2001 From: Ganesh Date: Tue, 27 Apr 2021 09:45:29 -0700 Subject: [PATCH 04/10] Added comments, removed redundant code --- argusclient/client.py | 188 +++++++++++++++++++++++++++++++++++------- tests/test_service.py | 105 ++++++++--------------- 2 files changed, 191 insertions(+), 102 deletions(-) diff --git a/argusclient/client.py b/argusclient/client.py index 4893db6..969aa93 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -595,67 +595,193 @@ def get_alerts_allinfo(self, ownerName=None, alertNameContains=None, shared=Fals Functions for enabling composite alerts ''' - def get_composite_alert_children_info(self, alert_id): - if not alert_id: raise ValueError("Need to specify alert id") - uri_path = "alerts/{}/children/info".format(alert_id) + def get_composite_alert_children(self, comp_alert_id): + """ + Get list of child alerts for a composite alert + :param comp_alert_id: ID of an argus composite alert + :type comp_alert_id: integer + :return: list of :class:`argusclient.model.Alert` object with all fields populated. + """ + + if not comp_alert_id: raise ValueError("Need to specify composite alert id") + if not isinstance(comp_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(comp_alert_id)) + + uri_path = "alerts/{}/children".format(comp_alert_id) + child_alerts = self.argus._request("get", uri_path) + child_alerts = [self._fill(child_alert) for child_alert in child_alerts] + return child_alerts + + def get_composite_alert_children_info(self, comp_alert_id): + """ + Get information for all children of a composite alert + + :param comp_alert_id: ID of an argus composite alert + :type comp_alert_id: integer + :return: list of child alerts information (alertid, alertname, triggerids, triggernames etc) + """ + + if not comp_alert_id: raise ValueError("Need to specify composite alert id") + if not isinstance(comp_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(comp_alert_id)) + + uri_path = "alerts/{}/children/info".format(comp_alert_id) return self.argus._request("get", uri_path) - def create_composite_alert(self, payload): - if not payload: raise ValueError("Need to specify an payload") - alert_obj = self.argus._request("post", "alerts", dataObj=payload) + def create_composite_alert(self, comp_alert): + """ + Create a new composite Alert + + :param comp_alert: Object of type argusclient.model.Alert + :type comp_alert: class:`argusclient.model.Alert` object + :return: newly created composite alert object of type class:`argusclient.model.Alert` + """ + if not comp_alert: raise ValueError("Need to specify an Alert object") + if not isinstance(comp_alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(comp_alert)) + + alert_obj = self._fill(self.argus._request("post", "alerts", dataObj=comp_alert)) self._coll[alert_obj.id] = alert_obj return alert_obj - def add_child_alert_to_composite_alert(self, alert_id, payload): - if not alert_id: raise ValueError("Need to specify composite alert id") - if not payload: raise ValueError("Need to specify payload") - uri_path = "alerts/{}/children".format(alert_id) - alert_obj = self.argus._request("post", uri_path, dataObj=payload) + def add_child_alert_to_composite_alert(self, comp_alert_id, alert): + """ + Add child alert to a composite alert + + :param comp_alert_id: ID of a composite alert + :type comp_alert_id: Integer + + :param alert: alert definition + :type alert: class:`argusclient.model.Alert` object + + :return: newly created child alert object of type class:`argusclient.model.Alert` + """ + if not comp_alert_id: raise ValueError("Need to specify a composite alert id") + if not alert: raise ValueError("Need to specify an Alert object") + if not isinstance(comp_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(comp_alert_id)) + if not isinstance(alert, Alert): raise TypeError("Need an Alert object, got: %s" % type(alert)) + + uri_path = "alerts/{}/children".format(comp_alert_id) + alert_obj = self._fill(self.argus._request("post", uri_path, dataObj=alert)) self._coll[alert_obj.id] = alert_obj return alert_obj - def update_composite_alert(self, alert_id, payload): - if not alert_id: raise ValueError("Need to specify composite alert id") - if not payload: raise ValueError("Need to specify payload") - uri_path = "alerts/{}".format(alert_id) - return self.argus._request("put", uri_path, dataObj=payload) + def update_composite_alert(self, comp_alert_id, alert): + """ + Update composite alert + + :param comp_alert_id: ID of a composite alert + :type comp_alert_id: Integer + + :param alert: alert definition + :type alert: class:`argusclient.model.Alert` object + + :return: newly updated composite alert object of type class:`argusclient.model.Alert` + """ + if not comp_alert_id: raise ValueError("Need to specify a composite alert id") + if not alert: raise ValueError("Need to specify an alert") + if not isinstance(comp_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(comp_alert_id)) + if not isinstance(alert, Alert): raise TypeError("Need an Alert object, got: %s" % type(alert)) + + uri_path = "alerts/{}".format(comp_alert_id) + alert_obj = self._fill(self.argus._request("put", uri_path, dataObj=alert)) + self._coll[alert_obj.id] = alert_obj + return alert_obj def delete_child_alert_from_composite_alert(self, comp_alert_id, child_alert_id): """ - Delete a child alert from a composite alert + Delete child alert from a composite alert + + :param comp_alert_id: ID of a composite alert + :type comp_alert_id: Integer + + :param child_alert_id: ID of a child alert + :type child_alert_id: Integer """ - if not comp_alert_id: raise ValueError("Need to specify composite alertid") - if not child_alert_id: raise ValueError("Need to specify a child alertid") + if not comp_alert_id: raise ValueError("Need to specify a composite alert id") + if not child_alert_id: raise ValueError("Need to specify a child alert id") + if not isinstance(comp_alert_id, int): raise TypeError("Need a composite Alert ID, got: %s" % type(comp_alert_id)) + if not isinstance(child_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(child_alert_id)) + uri_path = "alerts/{}/children/{}".format(comp_alert_id, child_alert_id) if id in self._coll: del self._coll[child_alert_id] return self.argus._request("delete", uri_path) def add_trigger_to_child_alert(self, alert_id, trigger): - if not alert_id: raise ValueError("Need to specify alertid") + """ + Return Add trigger to child alert + + :param comp_alert_id: ID of a composite alert + :type comp_alert_id: Integer + + :param trigger: Trigger object to be added to a child alert + :type child_alert_id: class:`argusclient.model.Trigger` + + :return: newly created trigger object of type class:`argusclient.model.Trigger` + """ + if not alert_id: raise ValueError("Need to specify a alert_id") + if not trigger: raise ValueError("Need to specify a trigger object") + if not isinstance(alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(alert_id)) if not isinstance(trigger, Trigger): raise TypeError("Need a Trigger object, got: %s" % type(trigger)) + uri_path = "alerts/{}/triggers".format(alert_id) return self.argus._request("post", uri_path, dataObj=trigger) def del_trigger_from_child_alert(self, alert_id, trigger_id): - if not alert_id: raise ValueError("Need to specify alert id") - if not trigger_id: raise ValueError("Need to specify trigger id") + """ + Delete child alert from a composite alert + + :param comp_alert_id: ID of a composite alert + :type comp_alert_id: Integer + + :param trigger_id: ID of a trigger belonging to child alert + :type trigger_id: Integer + """ + if not alert_id: raise ValueError("Need to specify an alert id") + if not trigger_id: raise ValueError("Need to specify a trigger id") + if not isinstance(alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(alert_id)) + if not isinstance(trigger_id, int): raise TypeError("Need a trigger id, got: %s" % type(trigger_id)) + uri_path = "alerts/{}/triggers/{}".format(alert_id, trigger_id) return self.argus._request("delete", uri_path) + + def add_notification_to_composite_alert(self, comp_alert, notification): + """ + Add notification to composite alert - def add_notification_to_composite_alert(self, alert, notification): - if not isinstance(alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(alert)) + :param comp_alert: composite alert object + :type comp_alert: class:`argusclient.model.Alert` + + :param trigger: Notification object to be added to a composite alert + :type child_alert_id: class:`argusclient.model.Notification` + + :return: Newly created notification object of type class:`argusclient.model.Notification` + """ + if not comp_alert: raise ValueError("Need to specify Alert object") + if not notification: raise ValueError("Need to specify Notification object") + if not isinstance(comp_alert, Alert): raise TypeError("Need an Alert object, got: %s" % type(comp_alert)) if not isinstance(notification, Notification): raise TypeError( "Need a Notification object, got: %s" % type(notification)) - uri_path = "alerts/{}/notifications".format(alert.id) - notification_obj = self.argus._request("post", uri_path, dataObj=notification) + + notification_obj = comp_alert.notifications.add(notification) + notification_obj.notification = notification_obj return notification_obj - def del_notification_from_composite_alert(self, alert, notification_id): - if not isinstance(alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(alert)) - if not isinstance(notification_id, int): raise TypeError("Need a Notifications id, got: %s" % type(alert)) - uri_path = "alerts/{}/notifications/{}".format(alert.id, notification_id) - return self.argus._request("delete", uri_path) + def del_notification_from_composite_alert(self, comp_alert, notification_id): + """ + Delete notification from a composite alert + + :param comp_alert: composite alert object + :type comp_alert: class:`argusclient.model.Alert` + + :param notification_id: ID of a notification belonging to composite alert + :type notification_id: Integer + + """ + if not comp_alert: raise ValueError("Need to specify an Alert ") + if not notification_id: raise ValueError("Need to specify a notification id") + if not isinstance(comp_alert, Alert): raise TypeError("Need an Alert object, got: %s" % type(comp_alert)) + if not isinstance(notification_id, int): raise TypeError("Need a Notifications id, got: %s" % type(notification_id)) + + return comp_alert.notifications.delete(notification_id) class AlertTriggersServiceClient(BaseUpdatableModelServiceClient): diff --git a/tests/test_service.py b/tests/test_service.py index 0bea32e..9f64e03 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -833,39 +833,36 @@ def testGetAlertWithMultipleTriggers(self): -class TestCompAlert(TestServiceBase): +class TestCompositeAlert(TestServiceBase): - @mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) - def testAddCompAlert(self, mockPost): - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - res = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(res, Alert)) - self.assertTrue(hasattr(res, "id")) - self.assertEqual(res.expression['expression']['operator'], 'AND') + def _createCompAlert(self): + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_add_comp_alert: + alert = Alert.from_dict(compalert_D) + self.assertTrue(isinstance(alert, Alert)) + delattr(alert, "id") + comp_alert = self.argus.alerts.create_composite_alert(alert) + self.assertTrue(isinstance(comp_alert, Alert)) + self.assertTrue(hasattr(comp_alert, "id")) + self.assertEqual(comp_alert.expression['expression']['operator'], 'AND') + call_args = mock_add_comp_alert.call_args + uri_path = os.path.join(endpoint, "alerts") + self.assertIn((uri_path,), call_args) + return comp_alert + def testAddCompAlert(self): + self._createCompAlert() - @mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) - def testAddChildAlert(self, mockPost): - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(comp_alert, Alert)) + + def testAddChildAlert(self): + comp_alert = self._createCompAlert() with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)): child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, Alert.from_dict(childAlert_1)) self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') self.assertTrue(isinstance(child_alert, Alert)) - @mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) - def testAddTriggerToChildAlert(self, mockPost): - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(comp_alert, Alert)) + def testAddTriggerToChildAlert(self): + comp_alert = self._createCompAlert() with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)) as mock_add_childalert: child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, @@ -884,18 +881,12 @@ def testAddTriggerToChildAlert(self, mockPost): self.assertIn((uri_path,), call_args) def testAddNotification(self): - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_add_comp_alert: - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(comp_alert, Alert)) - call_args = mock_add_comp_alert.call_args - uri_path = os.path.join(endpoint, "alerts") - self.assertIn((uri_path,), call_args) + comp_alert = self._createCompAlert() - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compAlert_notification), 200)) as mock_notification: - notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, Notification.from_dict(compAlert_notification)) + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps([compAlert_notification]), 200)) as mock_notification: + notification_obj = Notification.from_dict(compAlert_notification) + delattr(notification_obj, "id") + notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, notification_obj) self.assertTrue(isinstance(notification, Notification)) call_args = mock_notification.call_args uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) @@ -903,16 +894,7 @@ def testAddNotification(self): def testDeleteChildAlert(self): - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_alert: - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(comp_alert, Alert)) - call_args = mock_alert.call_args - uri_path = os.path.join(endpoint, "alerts") - self.assertIn((uri_path,), call_args) - + comp_alert = self._createCompAlert() with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)): child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, Alert.from_dict(childAlert_1)) self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') @@ -926,12 +908,7 @@ def testDeleteChildAlert(self): def testDeleteTriggerFromChildAlert(self): - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_alert: - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(comp_alert, Alert)) + comp_alert = self._createCompAlert() with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_1), 200)) as mock_add_childalert: child_alert = self.argus.alerts.add_child_alert_to_composite_alert(comp_alert.id, @@ -956,18 +933,12 @@ def testDeleteTriggerFromChildAlert(self): self.assertIn((uri_path,), call_args) def testDeleteNotification(self): - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_add_comp_alert: - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(comp_alert, Alert)) - call_args = mock_add_comp_alert.call_args - uri_path = os.path.join(endpoint, "alerts") - self.assertIn((uri_path,), call_args) + comp_alert = self._createCompAlert() - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compAlert_notification), 200)) as mock_notification: - notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, Notification.from_dict(compAlert_notification)) + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps([compAlert_notification]), 200)) as mock_notification: + notification_obj = Notification.from_dict(compAlert_notification) + delattr(notification_obj, "id") + notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, notification_obj) self.assertTrue(isinstance(notification, Notification)) call_args = mock_notification.call_args uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) @@ -990,15 +961,7 @@ def testGetCompAlertChildrenInfo(self): self.assertIn((uri_path,), call_args) def testUpdateCompAlert(self): - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_add_comp_alert: - alert = Alert.from_dict(compalert_D) - self.assertTrue(isinstance(alert, Alert)) - delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) - self.assertTrue(isinstance(comp_alert, Alert)) - call_args = mock_add_comp_alert.call_args - uri_path = os.path.join(endpoint, "alerts") - self.assertIn((uri_path,), call_args) + comp_alert = self._createCompAlert() with mock.patch('requests.Session.put', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_update: self.argus.alerts.update_composite_alert(compAlertID, Alert.from_dict(compalert_D)) From 2239f65603606bf4dc5f8892da88ea2514b03b6b Mon Sep 17 00:00:00 2001 From: Ganesh Date: Mon, 24 May 2021 22:36:12 -0700 Subject: [PATCH 05/10] Cleaned up extra functions and code changes from feedback --- argusclient/client.py | 105 ++---------------------------------------- tests/test_service.py | 24 ++++++---- 2 files changed, 17 insertions(+), 112 deletions(-) diff --git a/argusclient/client.py b/argusclient/client.py index 969aa93..d07a1a1 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -592,7 +592,7 @@ def get_alerts_allinfo(self, ownerName=None, alertNameContains=None, shared=Fals return self.argus._request("get", "alerts/allinfo", params=dict(ownername=ownerName, alertNameContains=alertNameContains, shared=shared, limit=limit)) ''' - Functions for enabling composite alerts + Functions to enable support for composite alerts ''' def get_composite_alert_children(self, comp_alert_id): @@ -603,7 +603,7 @@ def get_composite_alert_children(self, comp_alert_id): :return: list of :class:`argusclient.model.Alert` object with all fields populated. """ - if not comp_alert_id: raise ValueError("Need to specify composite alert id") + if not comp_alert_id: raise ValueError("Need to specify comp_alert_id") if not isinstance(comp_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(comp_alert_id)) uri_path = "alerts/{}/children".format(comp_alert_id) @@ -620,7 +620,7 @@ def get_composite_alert_children_info(self, comp_alert_id): :return: list of child alerts information (alertid, alertname, triggerids, triggernames etc) """ - if not comp_alert_id: raise ValueError("Need to specify composite alert id") + if not comp_alert_id: raise ValueError("Need to specify comp_alert_id") if not isinstance(comp_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(comp_alert_id)) uri_path = "alerts/{}/children/info".format(comp_alert_id) @@ -663,27 +663,6 @@ def add_child_alert_to_composite_alert(self, comp_alert_id, alert): self._coll[alert_obj.id] = alert_obj return alert_obj - def update_composite_alert(self, comp_alert_id, alert): - """ - Update composite alert - - :param comp_alert_id: ID of a composite alert - :type comp_alert_id: Integer - - :param alert: alert definition - :type alert: class:`argusclient.model.Alert` object - - :return: newly updated composite alert object of type class:`argusclient.model.Alert` - """ - if not comp_alert_id: raise ValueError("Need to specify a composite alert id") - if not alert: raise ValueError("Need to specify an alert") - if not isinstance(comp_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(comp_alert_id)) - if not isinstance(alert, Alert): raise TypeError("Need an Alert object, got: %s" % type(alert)) - - uri_path = "alerts/{}".format(comp_alert_id) - alert_obj = self._fill(self.argus._request("put", uri_path, dataObj=alert)) - self._coll[alert_obj.id] = alert_obj - return alert_obj def delete_child_alert_from_composite_alert(self, comp_alert_id, child_alert_id): """ @@ -705,84 +684,6 @@ def delete_child_alert_from_composite_alert(self, comp_alert_id, child_alert_id) del self._coll[child_alert_id] return self.argus._request("delete", uri_path) - def add_trigger_to_child_alert(self, alert_id, trigger): - """ - Return Add trigger to child alert - - :param comp_alert_id: ID of a composite alert - :type comp_alert_id: Integer - - :param trigger: Trigger object to be added to a child alert - :type child_alert_id: class:`argusclient.model.Trigger` - - :return: newly created trigger object of type class:`argusclient.model.Trigger` - """ - if not alert_id: raise ValueError("Need to specify a alert_id") - if not trigger: raise ValueError("Need to specify a trigger object") - if not isinstance(alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(alert_id)) - if not isinstance(trigger, Trigger): raise TypeError("Need a Trigger object, got: %s" % type(trigger)) - - uri_path = "alerts/{}/triggers".format(alert_id) - return self.argus._request("post", uri_path, dataObj=trigger) - - def del_trigger_from_child_alert(self, alert_id, trigger_id): - """ - Delete child alert from a composite alert - - :param comp_alert_id: ID of a composite alert - :type comp_alert_id: Integer - - :param trigger_id: ID of a trigger belonging to child alert - :type trigger_id: Integer - """ - if not alert_id: raise ValueError("Need to specify an alert id") - if not trigger_id: raise ValueError("Need to specify a trigger id") - if not isinstance(alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(alert_id)) - if not isinstance(trigger_id, int): raise TypeError("Need a trigger id, got: %s" % type(trigger_id)) - - uri_path = "alerts/{}/triggers/{}".format(alert_id, trigger_id) - return self.argus._request("delete", uri_path) - - def add_notification_to_composite_alert(self, comp_alert, notification): - """ - Add notification to composite alert - - :param comp_alert: composite alert object - :type comp_alert: class:`argusclient.model.Alert` - - :param trigger: Notification object to be added to a composite alert - :type child_alert_id: class:`argusclient.model.Notification` - - :return: Newly created notification object of type class:`argusclient.model.Notification` - """ - if not comp_alert: raise ValueError("Need to specify Alert object") - if not notification: raise ValueError("Need to specify Notification object") - if not isinstance(comp_alert, Alert): raise TypeError("Need an Alert object, got: %s" % type(comp_alert)) - if not isinstance(notification, Notification): raise TypeError( - "Need a Notification object, got: %s" % type(notification)) - - notification_obj = comp_alert.notifications.add(notification) - notification_obj.notification = notification_obj - return notification_obj - - def del_notification_from_composite_alert(self, comp_alert, notification_id): - """ - Delete notification from a composite alert - - :param comp_alert: composite alert object - :type comp_alert: class:`argusclient.model.Alert` - - :param notification_id: ID of a notification belonging to composite alert - :type notification_id: Integer - - """ - if not comp_alert: raise ValueError("Need to specify an Alert ") - if not notification_id: raise ValueError("Need to specify a notification id") - if not isinstance(comp_alert, Alert): raise TypeError("Need an Alert object, got: %s" % type(comp_alert)) - if not isinstance(notification_id, int): raise TypeError("Need a Notifications id, got: %s" % type(notification_id)) - - return comp_alert.notifications.delete(notification_id) - class AlertTriggersServiceClient(BaseUpdatableModelServiceClient): """ diff --git a/tests/test_service.py b/tests/test_service.py index 9f64e03..45cac6d 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -840,7 +840,7 @@ def _createCompAlert(self): alert = Alert.from_dict(compalert_D) self.assertTrue(isinstance(alert, Alert)) delattr(alert, "id") - comp_alert = self.argus.alerts.create_composite_alert(alert) + comp_alert = self.argus.alerts.add(alert) self.assertTrue(isinstance(comp_alert, Alert)) self.assertTrue(hasattr(comp_alert, "id")) self.assertEqual(comp_alert.expression['expression']['operator'], 'AND') @@ -873,8 +873,10 @@ def testAddTriggerToChildAlert(self): uri_path = os.path.join(endpoint, "alerts/{}/children".format(comp_alert.id)) self.assertIn((uri_path,), call_args) - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_trigger_1), 200)) as mock_trigger_post: - trigger = self.argus.alerts.add_trigger_to_child_alert(child_alert.id, Trigger.from_dict(childAlert_trigger_1)) + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps([childAlert_trigger_1]), 200)) as mock_trigger_post: + trigger_obj = Trigger.from_dict(childAlert_trigger_1) + delattr(trigger_obj, "id") + trigger = child_alert.triggers.add(trigger_obj) self.assertTrue(isinstance(trigger, Trigger)) call_args = tuple(mock_trigger_post.call_args) uri_path = os.path.join(endpoint, "alerts/{}/triggers".format(child_alert.id)) @@ -886,7 +888,7 @@ def testAddNotification(self): with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps([compAlert_notification]), 200)) as mock_notification: notification_obj = Notification.from_dict(compAlert_notification) delattr(notification_obj, "id") - notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, notification_obj) + notification = comp_alert.notifications.add(notification_obj) self.assertTrue(isinstance(notification, Notification)) call_args = mock_notification.call_args uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) @@ -919,15 +921,17 @@ def testDeleteTriggerFromChildAlert(self): uri_path = os.path.join(endpoint, "alerts/{}/children".format(comp_alert.id)) self.assertIn((uri_path,), call_args) - with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps(childAlert_trigger_1), 200)) as mock_trigger_post: - trigger = self.argus.alerts.add_trigger_to_child_alert(child_alert.id, Trigger.from_dict(childAlert_trigger_1)) + with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps([childAlert_trigger_1]), 200)) as mock_trigger_post: + trigger_obj = Trigger.from_dict(childAlert_trigger_1) + delattr(trigger_obj,"id") + trigger = child_alert.triggers.add(trigger_obj) self.assertTrue(isinstance(trigger, Trigger)) call_args = tuple(mock_trigger_post.call_args) uri_path = os.path.join(endpoint, "alerts/{}/triggers".format(child_alert.id)) self.assertIn((uri_path,), call_args) with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: - self.argus.alerts.del_trigger_from_child_alert(child_alert.id, trigger.id) + child_alert.triggers.delete(trigger.id) call_args = tuple(mock_trigger_post.call_args) uri_path = os.path.join(endpoint, "alerts/{}/triggers".format(child_alert.id)) self.assertIn((uri_path,), call_args) @@ -938,14 +942,14 @@ def testDeleteNotification(self): with mock.patch('requests.Session.post', return_value=MockResponse(json.dumps([compAlert_notification]), 200)) as mock_notification: notification_obj = Notification.from_dict(compAlert_notification) delattr(notification_obj, "id") - notification = self.argus.alerts.add_notification_to_composite_alert(comp_alert, notification_obj) + notification = comp_alert.notifications.add(notification_obj) self.assertTrue(isinstance(notification, Notification)) call_args = mock_notification.call_args uri_path = os.path.join(endpoint, "alerts/{}/notifications".format(comp_alert.id)) self.assertIn((uri_path,), call_args) with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: - self.argus.alerts.del_notification_from_composite_alert(comp_alert, notification.id) + comp_alert.notifications.delete(notification.id) call_args = tuple(mock_delete.call_args) uri_path = os.path.join(endpoint, "alerts/{}/notifications/{}".format(comp_alert.id, notification.id)) self.assertIn((uri_path,), call_args) @@ -964,7 +968,7 @@ def testUpdateCompAlert(self): comp_alert = self._createCompAlert() with mock.patch('requests.Session.put', return_value=MockResponse(json.dumps(compalert_D), 200)) as mock_update: - self.argus.alerts.update_composite_alert(compAlertID, Alert.from_dict(compalert_D)) + self.argus.alerts.update(compAlertID, Alert.from_dict(compalert_D)) alert_obj = self.argus.alerts.get(compAlertID) self.assertTrue(isinstance(alert_obj, Alert)) alert_obj_dict = alert_obj.to_dict() From 5d32959e53a897ed690e4c52f9cd1cb1d91aefad Mon Sep 17 00:00:00 2001 From: Ganesh Date: Wed, 26 May 2021 21:31:52 -0700 Subject: [PATCH 06/10] Fixed a bug pointed out while deleting child_alert --- argusclient/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/argusclient/client.py b/argusclient/client.py index d07a1a1..af89b86 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -680,7 +680,7 @@ def delete_child_alert_from_composite_alert(self, comp_alert_id, child_alert_id) if not isinstance(child_alert_id, int): raise TypeError("Need an Alert ID, got: %s" % type(child_alert_id)) uri_path = "alerts/{}/children/{}".format(comp_alert_id, child_alert_id) - if id in self._coll: + if child_alert_id in self._coll: del self._coll[child_alert_id] return self.argus._request("delete", uri_path) From c134ebddec79c2d6a34ec9014e184b70777bd36e Mon Sep 17 00:00:00 2001 From: Ganesh Date: Wed, 26 May 2021 21:51:56 -0700 Subject: [PATCH 07/10] Modified some comments --- argusclient/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/argusclient/client.py b/argusclient/client.py index af89b86..591a207 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -613,7 +613,7 @@ def get_composite_alert_children(self, comp_alert_id): def get_composite_alert_children_info(self, comp_alert_id): """ - Get information for all children of a composite alert + Get information for all children (child alerts + triggers associated with them) of a composite alert :param comp_alert_id: ID of an argus composite alert :type comp_alert_id: integer @@ -666,7 +666,7 @@ def add_child_alert_to_composite_alert(self, comp_alert_id, alert): def delete_child_alert_from_composite_alert(self, comp_alert_id, child_alert_id): """ - Delete child alert from a composite alert + Delete a child alert from a composite alert :param comp_alert_id: ID of a composite alert :type comp_alert_id: Integer From d6039e5996e0631638f386388c7041f7460f76c2 Mon Sep 17 00:00:00 2001 From: Ganesh Date: Sun, 30 May 2021 18:55:09 -0700 Subject: [PATCH 08/10] Added logic to test for deleted child alert in testDeleteChildAlert --- tests/test_service.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_service.py b/tests/test_service.py index 45cac6d..c936c0f 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -902,12 +902,17 @@ def testDeleteChildAlert(self): self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') self.assertTrue(isinstance(child_alert, Alert)) + ''' Before delete we can get the object for child_alert.id ''' + res = self.argus.alerts.get(child_alert.id) with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: self.argus.alerts.delete_child_alert_from_composite_alert(comp_alert.id, child_alert.id) call_args = mock_delete.call_args uri_path = os.path.join(endpoint, "alerts/{}/children/{}".format(comp_alert.id, child_alert.id)) self.assertIn((uri_path,), call_args) + ''' After delete we cannot get the object for child_alert.id as its deleted from the argus ''' + with mock.patch('requests.Session.get', return_value=MockResponse("", 404)) as mockGet: + self.failUnlessRaises(ArgusObjectNotFoundException, lambda: self.argus.alerts.get(child_alert.id)) def testDeleteTriggerFromChildAlert(self): comp_alert = self._createCompAlert() From 0b7002a172117287fa89033a0c8269ecbf743e7e Mon Sep 17 00:00:00 2001 From: Ganesh Date: Tue, 1 Jun 2021 20:58:58 -0700 Subject: [PATCH 09/10] Added test for get_composite_alert_children, updated comments as suggested --- argusclient/client.py | 14 -------------- tests/test_service.py | 20 ++++++++++++++++++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/argusclient/client.py b/argusclient/client.py index 591a207..a981b8d 100644 --- a/argusclient/client.py +++ b/argusclient/client.py @@ -626,20 +626,6 @@ def get_composite_alert_children_info(self, comp_alert_id): uri_path = "alerts/{}/children/info".format(comp_alert_id) return self.argus._request("get", uri_path) - def create_composite_alert(self, comp_alert): - """ - Create a new composite Alert - - :param comp_alert: Object of type argusclient.model.Alert - :type comp_alert: class:`argusclient.model.Alert` object - :return: newly created composite alert object of type class:`argusclient.model.Alert` - """ - if not comp_alert: raise ValueError("Need to specify an Alert object") - if not isinstance(comp_alert, Alert): raise TypeError("Need a Alert object, got: %s" % type(comp_alert)) - - alert_obj = self._fill(self.argus._request("post", "alerts", dataObj=comp_alert)) - self._coll[alert_obj.id] = alert_obj - return alert_obj def add_child_alert_to_composite_alert(self, comp_alert_id, alert): """ diff --git a/tests/test_service.py b/tests/test_service.py index c936c0f..8e15769 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -902,7 +902,10 @@ def testDeleteChildAlert(self): self.assertEqual(child_alert.alertType, 'COMPOSITE_CHILD') self.assertTrue(isinstance(child_alert, Alert)) - ''' Before delete we can get the object for child_alert.id ''' + ''' + Right after add, we can access the child_alert.id without triggering an API call (i.e., no mocking is required) + as it gets added to the local cache + ''' res = self.argus.alerts.get(child_alert.id) with mock.patch('requests.Session.delete', return_value=MockResponse("", 200)) as mock_delete: self.argus.alerts.delete_child_alert_from_composite_alert(comp_alert.id, child_alert.id) @@ -910,7 +913,10 @@ def testDeleteChildAlert(self): uri_path = os.path.join(endpoint, "alerts/{}/children/{}".format(comp_alert.id, child_alert.id)) self.assertIn((uri_path,), call_args) - ''' After delete we cannot get the object for child_alert.id as its deleted from the argus ''' + ''' + After delete, the object should be gone from the local cache, so the get should result in an API call which + we are mocking to raise a 404 to mimic the real scenario + ''' with mock.patch('requests.Session.get', return_value=MockResponse("", 404)) as mockGet: self.failUnlessRaises(ArgusObjectNotFoundException, lambda: self.argus.alerts.get(child_alert.id)) @@ -969,6 +975,16 @@ def testGetCompAlertChildrenInfo(self): uri_path = os.path.join(endpoint, "alerts/{}/children/info".format(compAlertID)) self.assertIn((uri_path,), call_args) + def testGetCompAlertChildren(self): + with mock.patch('requests.Session.get', return_value=MockResponse(json.dumps([childAlert_1, childAlert_2]), 200)) as mock_get: + res = self.argus.alerts.get_composite_alert_children(compAlertID) + if res: + for obj in res: + self.assertTrue(isinstance(obj, Alert)) + call_args = tuple(mock_get.call_args) + uri_path = os.path.join(endpoint, "alerts/{}/children".format(compAlertID)) + self.assertIn((uri_path,), call_args) + def testUpdateCompAlert(self): comp_alert = self._createCompAlert() From e8cc0ce397a3c075dd5740938ea7c3dc0be8b47a Mon Sep 17 00:00:00 2001 From: Ganesh Date: Wed, 2 Jun 2021 16:34:22 -0700 Subject: [PATCH 10/10] Bumped the version from 1.1 to 1.2 --- docs/conf.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2bfe80a..0d2f2e2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '1.1' +version = '1.2' # The full version, including alpha/beta/rc tags. -release = '1.1.0' +release = '1.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 08312d0..4091062 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ from setuptools import setup, find_packages -version = '1.1' +version = '1.2' with open("README.rst", 'r') as fin: README = fin.read()