Skip to content

Commit 8bc072f

Browse files
philrzChobicus
authored andcommitted
SMPROD-1309: Example for restoring Alerts now removes non-existent notification channel IDs (#54)
* Handle the case of a Notification Channel with no name * In the example for restoring Alerts, drop channel IDs that don't exist in the target environment * Handle restore of Alert JSON that was downloaded via Export JSON in the web UI * Handle null description on Alert updates as well
1 parent 352225f commit 8bc072f

File tree

2 files changed

+103
-44
lines changed

2 files changed

+103
-44
lines changed

examples/restore_alerts.py

100644100755
Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import os
77
import sys
88
import json
9+
import datetime
10+
import calendar
911
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), '..'))
1012
from sdcclient import SdcClient
1113

@@ -41,20 +43,54 @@
4143
print res[1]
4244
sys.exit(1)
4345

46+
#
47+
# Someone might be restoring Alert configs from another environment,
48+
# in which case the Notification Channel IDs in the saved Alert JSON
49+
# is not expected to match the Notification Channel IDs in the target
50+
# environment. We'll get the list of target IDs so we can drop non-
51+
# matching IDs when we restore.
52+
#
53+
res = sdclient.get_notification_ids()
54+
if res[0]:
55+
existing_notification_channel_ids = res[1]
56+
else:
57+
print res[1]
58+
sys.exit(1)
59+
4460
created_count = 0
4561
updated_count = 0
4662

4763
with open(alerts_dump_file, 'r') as f:
4864
j = json.load(f)
4965
for a in j['alerts']:
66+
if 'notificationChannelIds' in a:
67+
for channel_id in a['notificationChannelIds']:
68+
if channel_id not in existing_notification_channel_ids:
69+
print 'Notification Channel ID ' + str(channel_id) + ' referenced in Alert "' + a['name'] + '" does not exist.\n Restoring without this ID.'
70+
a['notificationChannelIds'].remove(channel_id)
71+
72+
# JSON Alerts from the list_alerts.py example are in epoch time, but ones
73+
# downloaded using the "Export JSON" button of the web interface are ISO
74+
# timestamps in string form. If we see these fields as strings, assume
75+
# they came from the web UI and convert them to epoch.
76+
for timefield in ['createdOn', 'modifiedOn']:
77+
if isinstance(a.get(timefield), basestring):
78+
a[timefield] = calendar.timegm(datetime.datetime.strptime(a[timefield], '%Y-%m-%dT%H:%M:%S.%fZ').timetuple())
79+
5080
if a['name'] in existing_alerts:
5181
a['id'] = existing_alerts[a['name']]['id']
5282
a['version'] = existing_alerts[a['name']]['version']
53-
a['description'] += ' (updated via restore_alerts.py)'
83+
if a.get('description') is None:
84+
a['description'] = '(updated via restore_alerts.py)'
85+
else:
86+
a['description'] += ' (updated via restore_alerts.py)'
5487
res = sdclient.update_alert(a)
5588
updated_count += 1
5689
else:
57-
a['description'] += ' (created via restore_alerts.py)'
90+
if a.get('description') is None:
91+
a['description'] = '(created via restore_alerts.py)'
92+
else:
93+
a['description'] += ' (created via restore_alerts.py)'
5894
res = sdclient.create_alert(alert_obj=a)
5995
created_count += 1
6096
if not res[0]:

sdcclient/_client.py

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -111,57 +111,80 @@ def get_n_connected_agents(self):
111111
data = res.json()
112112
return [True, data['total']]
113113

114-
def get_notification_ids(self, channels):
115-
res = requests.get(self.url + '/api/notificationChannels', headers=self.hdrs, verify=self.ssl_verify)
114+
def get_notification_ids(self, channels=None):
115+
'''**Description**
116+
Get an array of all configured Notification Channel IDs, or a filtered subset of them.
116117
117-
if not self._checkResponse(res):
118-
return [False, self.lasterr]
118+
**Arguments**
119+
- **channels**: an optional array of dictionaries to limit the set of Notification Channel IDs returned. If not specified, IDs for all configured Notification Channels are returned. Each dictionary contains a ``type`` field that can be one of the available types of Notification Channel (``EMAIL``, ``SNS``, ``PAGER_DUTY``, ``SLACK``, ``OPSGENIE``, ``WEBHOOK``) as well as additional elements specific to each channel type.
120+
121+
**Success Return Value**
122+
An array of Notification Channel IDs (integers).
123+
124+
**Examples**
125+
- `examples/create_alert.py <https://github.com/draios/python-sdc-client/blob/master/examples/create_alert.py>`_
126+
- `examples/restore_alerts.py <https://github.com/draios/python-sdc-client/blob/master/examples/restore_alerts.py>`_
127+
'''
128+
129+
res = requests.get(self.url + '/api/notificationChannels', headers=self.hdrs, verify=self.ssl_verify)
119130

120-
# Should try and improve this M * N lookup
121-
ids = []
122-
for c in channels:
123-
found = False
124-
for ch in res.json()["notificationChannels"]:
125-
if c['type'] == ch['type']:
126-
if c['type'] == 'SNS':
127-
opt = ch['options']
128-
if set(opt['snsTopicARNs']) == set(c['snsTopicARNs']):
131+
if not self._checkResponse(res):
132+
return [False, self.lasterr]
133+
134+
ids = []
135+
136+
# If no array of channel types/names was provided to filter by,
137+
# just return them all.
138+
if channels is None:
139+
for ch in res.json()["notificationChannels"]:
140+
ids.append(ch['id'])
141+
return [True, ids]
142+
143+
# Return the filtered set of channels based on the provided types/names array.
144+
# Should try and improve this M * N lookup
145+
for c in channels:
146+
found = False
147+
for ch in res.json()["notificationChannels"]:
148+
if c['type'] == ch['type']:
149+
if c['type'] == 'SNS':
150+
opt = ch['options']
151+
if set(opt['snsTopicARNs']) == set(c['snsTopicARNs']):
152+
found = True
153+
ids.append(ch['id'])
154+
elif c['type'] == 'EMAIL':
155+
opt = ch['options']
156+
if 'emailRecipients' in c:
157+
if set(c['emailRecipients']) == set(opt['emailRecipients']):
158+
found = True
159+
ids.append(ch['id'])
160+
elif 'name' in c:
161+
if c['name'] == ch.get('name'):
129162
found = True
130163
ids.append(ch['id'])
131-
elif c['type'] == 'EMAIL':
132-
opt = ch['options']
133-
if 'emailRecipients' in c:
134-
if set(c['emailRecipients']) == set(opt['emailRecipients']):
135-
found = True
136-
ids.append(ch['id'])
137-
elif 'name' in c:
138-
if c['name'] == ch['name']:
139-
found = True
140-
ids.append(ch['id'])
141-
elif c['type'] == 'PAGER_DUTY':
142-
opt = ch['options']
143-
if opt['account'] == c['account'] and opt['serviceName'] == c['serviceName']:
164+
elif c['type'] == 'PAGER_DUTY':
165+
opt = ch['options']
166+
if opt['account'] == c['account'] and opt['serviceName'] == c['serviceName']:
167+
found = True
168+
ids.append(ch['id'])
169+
elif c['type'] == 'SLACK':
170+
opt = ch['options']
171+
if 'channel' in opt and opt['channel'] == c['channel']:
172+
found = True
173+
ids.append(ch['id'])
174+
elif c['type'] == 'OPSGENIE':
175+
if 'name' in c:
176+
if c['name'] == ch.get('name'):
144177
found = True
145178
ids.append(ch['id'])
146-
elif c['type'] == 'SLACK':
147-
opt = ch['options']
148-
if 'channel' in opt and opt['channel'] == c['channel']:
179+
elif c['type'] == 'WEBHOOK':
180+
if 'name' in c:
181+
if c['name'] == ch.get('name'):
149182
found = True
150183
ids.append(ch['id'])
151-
elif c['type'] == 'OPSGENIE':
152-
if 'name' in c:
153-
if c['name'] == ch['name']:
154-
found = True
155-
ids.append(ch['id'])
156-
elif c['type'] == 'WEBHOOK':
157-
if 'name' in c:
158-
if c['name'] == ch['name']:
159-
found = True
160-
ids.append(ch['id'])
161-
if not found:
162-
return [False, "Channel not found: " + str(c)]
184+
if not found:
185+
return [False, "Channel not found: " + str(c)]
163186

164-
return [True, ids]
187+
return [True, ids]
165188

166189
def create_email_notification_channel(self, channel_name, email_recipients):
167190
channel_json = {

0 commit comments

Comments
 (0)