/[marcuscom]/ciscolive/automation/services/dns-hook.py
ViewVC logotype

Contents of /ciscolive/automation/services/dns-hook.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20615 - (show annotations) (download) (as text)
Fri Feb 1 13:44:39 2019 UTC (3 months, 2 weeks ago) by jclarke
File MIME type: text/x-python
File size: 12744 byte(s)
Add a bot to process DNS add requests.

1 #!/usr/local/bin/python2
2 #
3 # Copyright (c) 2017-2019 Joe Clarke <jclarke@cisco.com>
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 # SUCH DAMAGE.
26
27 import sys
28 import json
29 from sparker import Sparker
30 import re
31 import requests
32 from requests.packages.urllib3.exceptions import InsecureRequestWarning
33
34 requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
35 import time
36 import traceback
37 import socket
38 import logging
39 import CLEUCreds
40
41 CMX_GW = 'http://cl-freebsd.ciscolive.network:8002/api/v0.1/cmx'
42 DNS_BASE = 'https://dc1-dns.ciscolive.network:8443/web-services/rest/resource/'
43 DOMAIN = 'ciscolive.network'
44
45 CNR_HEADERS = {
46 'Accept': 'application/json',
47 'Content-Type': 'application/json',
48 'Authorization': CLEUCreds.JCLARKE_BASIC
49 }
50
51 ALLOWED_TO_CREATE = ['jclarke@cisco.com',
52 'ksekula@cisco.com', 'ayourtch@cisco.com', 'rkamerma@cisco.com', 'thulsdau@cisco.com',
53 'lhercot@cisco.com', 'pweijden@cisco.com', 'udiedric@cisco.com']
54
55 spark = Sparker(token=CLEUCreds.SPARK_TOKEN, logit=True)
56
57 SPARK_TEAM = 'CL19 NOC Team'
58 SPARK_ROOM = 'DNS Queries'
59
60
61 def check_for_alias(alias):
62 global DNS_BASE, DOMAIN, CNR_HEADERS
63
64 url = DNS_BASE + 'CCMRRSet' + '/{}'.format(alias)
65
66 response = requests.request(
67 'GET', url, params={'zoneOrigin': DOMAIN}, headers=CNR_HEADERS, verify=False)
68 if response.status_code == 404:
69 return None
70
71 res = {}
72 j = response.json()
73
74 hostname = ''
75
76 for rr in j['rrs']['stringItem']:
77 m = re.search(r'^IN CNAME (.+)', rr)
78 if m:
79 hostname = m.group(1)
80 break
81
82 res['hostname'] = hostname
83
84 return res
85
86
87 def create_alias(hostname, alias):
88 global DNS_BASE, DOMAIN, CNR_HEADERS
89
90 url = DNS_BASE + 'CCMRRSet' + '/{}'.format(alias)
91
92 if re.search(r'\.', hostname) and not hostname.endswith('.'):
93 hostname += '.'
94
95 if not hostname.endswith('.'):
96 hostname += '.' + DOMAIN + '.'
97
98 rr_obj = {
99 'name': alias,
100 'zoneOrigin': DOMAIN,
101 'rrs': {
102 'stringItem': [
103 'IN CNAME {}'.format(hostname)
104 ]
105 }
106 }
107
108 response = requests.request(
109 'PUT', url, headers=CNR_HEADERS, json=rr_obj, verify=False)
110 response.raise_for_status()
111
112
113 def delete_alias(alias):
114 global DNS_BASE, DOMAIN, CNR_HEADERS
115
116 url = DNS_BASE + 'CCMRRSet' + '/{}'.format(alias)
117
118 response = requests.request('DELETE', url, params={
119 'zoneOrigin': DOMAIN}, headers=CNR_HEADERS, verify=False)
120 response.raise_for_status()
121
122
123 def delete_record(hostname):
124 global DNS_BASE, DOMAIN, CNR_HEADERS
125
126 url = DNS_BASE + 'CCMHost' + '/{}'.format(hostname)
127
128 response = requests.request('DELETE', url, params={
129 'zoneOrigin': DOMAIN}, headers=CNR_HEADERS, verify=False)
130 response.raise_for_status()
131
132
133 def check_for_record(hostname):
134 global DNS_BASE, DOMAIN, CNR_HEADERS
135
136 url = DNS_BASE + 'CCMHost' + '/{}'.format(hostname)
137
138 response = requests.request(
139 'GET', url, params={'zoneOrigin': DOMAIN}, headers=CNR_HEADERS, verify=False)
140 if response.status_code == 404:
141 return None
142
143 res = {}
144 j = response.json()
145
146 res['ip'] = j['addrs']['stringItem'][0]
147
148 return res
149
150
151 def create_record(hostname, ip, aliases):
152 global DNS_BASE, DOMAIN, CNR_HEADERS
153
154 url = DNS_BASE + 'CCMHost' + '/{}'.format(hostname)
155 host_obj = {
156 'addrs': {
157 'stringItem': [
158 ip
159 ]
160 },
161 'name': hostname,
162 'zoneOrigin': DOMAIN
163 }
164
165 if aliases is not None:
166 aliases = re.sub(r'\s+', '', aliases)
167 alist = aliases.split(',')
168
169 alist = [x + '.' + DOMAIN +
170 '.' if not x.endswith('.') else x for x in alist]
171 host_obj['aliases'] = {
172 'stringItem': alist
173 }
174
175 response = requests.request(
176 'PUT', url, headers=CNR_HEADERS, json=host_obj, verify=False)
177 response.raise_for_status()
178
179
180 if __name__ == '__main__':
181 print('Content-type: application/json\r\n\r\n')
182
183 output = sys.stdin.read()
184
185 j = json.loads(output)
186
187 logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s : %(message)s',
188 filename='/var/log/dns-hook.log', level=logging.DEBUG)
189 logging.debug(json.dumps(j, indent=4))
190
191 message_from = j['data']['personEmail']
192
193 if message_from == 'livenocbot@sparkbot.io':
194 logging.debug('Person email is our bot')
195 print('{"result":"success"}')
196 sys.exit(0)
197
198 tid = spark.get_team_id(SPARK_TEAM)
199 if tid is None:
200 logging.error('Failed to get Spark Team ID')
201 print('{"result":"fail"}')
202 sys.exit(0)
203
204 rid = spark.get_room_id(tid, SPARK_ROOM)
205 if rid is None:
206 logging.error('Failed to get Spark Room ID')
207 print('{"result":"fail"}')
208 sys.exit(0)
209
210 if rid != j['data']['roomId']:
211 logging.error('Spark Room ID is not the same as in the message ({} vs. {})'.format(
212 rid, j['data']['roomId']))
213 print('{"result":"fail"}')
214 sys.exit(0)
215
216 mid = j['data']['id']
217
218 msg = spark.get_message(mid)
219 if msg is None:
220 logging.error('Did not get a message')
221 print('{"result":"error"}')
222 sys.exit(0)
223
224 txt = msg['text']
225 found_hit = False
226
227 if re.search(r'\bhelp\b', txt, re.I):
228 spark.post_to_spark(
229 SPARK_TEAM, SPARK_ROOM, 'To create a new DNS entry, tell me things like, `Create record for HOST with IP and alias ALIAS`, `Create entry for HOST with IP`, `Add a DNS record for HOST with IP`')
230 found_hit = True
231
232 try:
233 m = re.search(
234 r'(remove|delete)\s+.*?(alias|cname)\s+([\w\-\.]+)', txt, re.I)
235
236 if not found_hit and m:
237 found_hit = True
238 if message_from not in ALLOWED_TO_CREATE:
239 spark.post_to_spark(
240 SPARK_TEAM, SPARK_ROOM, 'I\'m sorry, {}. I can\'t do that for you.'.format(message_from))
241 else:
242 res = check_for_alias(m.group(3))
243 if res is None:
244 spark.post_to_spark(
245 SPARK_TEAM, SPARK_ROOM, 'I didn\'t find an alias {}'.format(m.group(3)))
246 else:
247 try:
248 delete_alias(m.group(3))
249 spark.post_to_spark(
250 SPARK_TEAM, SPARK_ROOM, 'Alias {} deleted successfully.'.format(m.group(3)))
251 except Exception as e:
252 spark.post_to_spark(
253 SPARK_TEAM, SPARK_ROOM, 'Failed to delete alias {}: {}'.format(m.group(3), e))
254
255 m = re.search(
256 r'(remove|delete)\s+.*?for\s+([\w\-\.]+)', txt, re.I)
257
258 if not found_hit and m:
259 found_hit = True
260 if message_from not in ALLOWED_TO_CREATE:
261 spark.post_to_spark(
262 SPARK_TEAM, SPARK_ROOM, 'I\'m sorry, {}. I can\'t do that for you.'.format(message_from))
263 else:
264 res = check_for_record(m.group(2))
265 if res is None:
266 spark.post_to_spark(
267 SPARK_TEAM, SPARK_ROOM, 'I didn\'t find a DNS record for {}.'.format(m.group(2)))
268 else:
269 try:
270 delete_record(m.group(2))
271 spark.post_to_spark(
272 SPARK_TEAM, SPARK_ROOM, 'DNS record for {} deleted successfully.'.format(m.group(2)))
273 except Exception as e:
274 spark.post_to_spark(
275 SPARK_TEAM, SPARK_ROOM, 'Failed to delete DNS record for {}: {}'.format(m.group(2), e))
276
277 m = re.search(
278 r'(make|create|add)\s+.*?for\s+([\w\-\.]+)\s+.*?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(\s+.*?(alias(es)?|cname(s)?)\s+([\w\-\.]+(\s*,\s*[\w\-\.,\s]+)?))?', txt, re.I)
279 if not found_hit and m:
280 found_hit = True
281 if message_from not in ALLOWED_TO_CREATE:
282 spark.post_to_spark(
283 SPARK_TEAM, SPARK_ROOM, 'I\'m sorry, {}. I can\'t do that for you.'.format(message_from))
284 else:
285 res = check_for_record(m.group(2))
286 if res is not None:
287 spark.post_to_spark(
288 SPARK_TEAM, SPARK_ROOM, '_{}_ is already in DNS as **{}**'.format(m.group(2), res['ip']))
289 else:
290 hostname = re.sub(r'\.{}'.format(DOMAIN), '', m.group(2))
291 try:
292 create_record(m.group(2), m.group(3), m.group(8))
293 spark.post_to_spark(
294 SPARK_TEAM, SPARK_ROOM, 'Successfully created record for {}.'.format(m.group(2)))
295 except Exception as e:
296 spark.post_to_spark(
297 SPARK_TEAM, SPARK_ROOM, 'Failed to create record for {}: {}'.format(m.group(2), e))
298
299 m = re.search(
300 r'(make|create|add)\s+(alias(es)?|cname(s)?)\s+([\w\-\.]+(\s*,\s*[\w\-\.,\s]+)?)\s+(for|to)\s+([\w\-\.]+)', txt, re.I)
301 if not found_hit and m:
302 found_hit = True
303 if message_from not in ALLOWED_TO_CREATE:
304 spark.post_to_spark(
305 SPARK_TEAM, SPARK_ROOM, 'I\'m sorry, {}. I can\'t do that for you.'.format(message_from))
306 else:
307 aliases = m.group(5)
308 aliases = re.sub(r'\s+', '', aliases)
309 alist = aliases.split(',')
310 already_exists = False
311 for alias in alist:
312 res = check_for_alias(alias)
313 if res is not None:
314 already_exists = True
315 spark.post_to_spark(
316 SPARK_TEAM, SPARK_ROOM, '_{}_ is already an alias for **{}**'.format(alias, res['hostname']))
317 res = check_for_record(alias)
318 if res is not None:
319 already_exists = True
320 spark.post_to_spark(
321 SPARK_TEAM, SPARK_ROOM, '_{}_ is already a hostname with IP **{}**'.format(alias, res['ip']))
322
323 if not already_exists:
324 success = True
325 for alias in alist:
326 try:
327 create_alias(m.group(8), alias)
328 except Exception as e:
329 spark.post_to_spark(
330 SPARK_TEAM, SPARK_ROOM, 'Failed to create alias {}: {}'.format(alias, e))
331 success = False
332
333 if success:
334 spark.post_to_spark(
335 SPARK_TEAM, SPARK_ROOM, 'Successfully created alias(es) {} for {}'.format(aliases, m.group(8)))
336
337 if not found_hit:
338 spark.post_to_spark(SPARK_TEAM, SPARK_ROOM,
339 'Sorry, I didn\'t get that. Please ask me to create or delete a DNS entry; or just ask for "help".')
340 except Exception as e:
341 logging.error('Error in obtaining data: {}'.format(
342 traceback.format_exc()))
343 spark.post_to_spark(SPARK_TEAM, SPARK_ROOM,
344 'Whoops, I encountered an error:<br>\n```\n{}\n```'.format(traceback.format_exc()))
345
346 print('{"result":"success"}')

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26