blob: 470cdf065983d7d985d88ef89c0cbee5ad61a30d [file] [log] [blame]
ecnoelc8bb28a2017-07-06 09:02:01 -05001'''
2/*-
3* ============LICENSE_START=======================================================
4* APPC
5* ================================================================================
6* Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
7* ================================================================================
8* Licensed under the Apache License, Version 2.0 (the "License");
9* you may not use this file except in compliance with the License.
10* You may obtain a copy of the License at
11*
12* http://www.apache.org/licenses/LICENSE-2.0
13*
14* Unless required by applicable law or agreed to in writing, software
15* distributed under the License is distributed on an "AS IS" BASIS,
16* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17* See the License for the specific language governing permissions and
18* limitations under the License.
19* ============LICENSE_END=========================================================
20* ECOMP is a trademark and service mark of AT&T Intellectual Property.
21*/
22'''
23
24import time, datetime, json, os, sys, subprocess, re
25import uuid
26import tarfile
27import shutil
28
29import requests
30
31import cherrypy
32from cherrypy.lib.httputil import parse_query_string
33from cherrypy.lib import auth_basic
34
35from multiprocessing import Process, Manager
36
37from AnsibleModule import ansibleSysCall
38
39import AnsibleSql
40from AnsibleSql import readPlaybook, readCredentials
41
42from os import listdir
43from os.path import isfile, join
44
45TestRecord = Manager().dict()
46ActiveProcess = {}
47
48def sys_call (cmd):
49 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
50 output = p.stdout.readlines()
51 retval = p.wait()
52 if len (output) > 0:
53 for i in range(len(output)):
54 output[i] = output[i].strip()
55 return retval, output
56
57def callback (Id, Result, Output, Log, returncode):
58
59 print "***> in RestServer.callback"
60
61 if Id in TestRecord:
62 time_now = datetime.datetime.utcnow()
63 delta_time = (time_now - TestRecord[Id]['Time']).total_seconds()
64 Result['PlaybookName'] = TestRecord[Id]['PlaybookName']
65 Result['Version'] = TestRecord[Id]['Version']
66 if returncode == 137:
67 Result['StatusCode'] = 500
68 Result['StatusMessage'] = "TERMINATED"
69 else:
70 Result['StatusCode'] = 200
71 Result['StatusMessage'] = "FINISHED"
72
73 # Need to update the whole data structure for key=Id otherwise Manager is not updated
74 TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'],
75 'LCM': TestRecord[Id]['LCM'],
76 'Version': TestRecord[Id]['Version'],
77 'NodeList': TestRecord[Id]['NodeList'],
78 'HostGroupList': TestRecord[Id]['HostGroupList'],
79 'HostNameList': TestRecord[Id]['HostNameList'],
80 'Time': TestRecord[Id]['Time'],
81 'Timeout': TestRecord[Id]['Timeout'],
82 'Duration': str(delta_time),
83 'EnvParameters': TestRecord[Id]['EnvParameters'],
84 'LocalParameters': TestRecord[Id]['LocalParameters'],
85 'FileParameters': TestRecord[Id]['FileParameters'],
86 'CallBack': TestRecord[Id]['CallBack'],
87 'Result': Result,
88 'Log': Log,
89 'Output': Output,
90 'Path': TestRecord[Id]['Path'],
91 'Mandatory': TestRecord[Id]['Path']}
92
93 if not TestRecord[Id]['CallBack'] == None:
94
95 # Posting results to callback server
96
97 data = {"StatusCode": 200,
98 "StatusMessage": "FINISHED",
99 "PlaybookName": TestRecord[Id]["PlaybookName"],
100 "Version": TestRecord[Id]["Version"],
101 "Duration": TestRecord[Id]["Duration"],
102 "Results": TestRecord[Id]['Result']['Results']}
103
104 if not TestRecord[Id]['Output']['Output'] == {}:
105 for key in data["Results"]:
106 if key in TestRecord[Id]['Output']['Output']:
107 data["Results"][key]["Output"] = TestRecord[Id]['Output']['Output'][key]
108
109 print " Posting to", TestRecord[Id]['CallBack']
110
111 s = requests.Session()
112 r = s.post(TestRecord[Id]['CallBack'], data = json.dumps(data),
113 headers = {'content-type': 'application/json'})
114 print " Response", r.status_code, r.text
115
116def RunAnsible_Playbook (callback, Id, Inventory, Playbook, NodeList, TestRecord,
117 Path, ArchiveFlag):
118
119 print "***> in RestServer.RunAnsible_Playbook"
120
121 # Run test in playbook for given target
122 Result = ''
123
124 retval, log, returncode = ansibleSysCall (Inventory, Playbook, NodeList,
125 TestRecord[Id]['Mandatory'],
126 TestRecord[Id]['EnvParameters'],
127 TestRecord[Id]['LocalParameters'],
128 TestRecord[Id]['LCM'],
129 TestRecord[Id]['Timeout'])
130
131
132 print " returncode:", returncode
133 print " retval: ", retval
134 print " log: ", log
135
136 Log = ''.join(log)
137 Output = {'Output': {}}
138
139 onlyfiles = [f for f in listdir(Path)
140 if isfile(join(Path, f))]
141
142 for file in onlyfiles:
143 if "results.txt" in file:
144 f = open(Path + "/" + file, "r")
145 key = file.split("_")[0]
146 Output['Output'][key] = f.read()
147 f.close()
148
149 Result = {'Results': {}}
150 if 'could not be found' in Log:
151 Result['Results'] = {"StatusCode": 101,
152 "StatusMessage": "PLAYBOOK NOT FOUND"}
153 if returncode == 137:
154 Result['Results'] = {"StatusCode": 500,
155 "StatusMessage": "TERMINATED"}
156
157 elif TestRecord[Id]['NodeList'] == []:
158
159 host_index = None
160
161 if 'TargetNode' in TestRecord[Id]['EnvParameters']:
162 targetlist = TestRecord[Id]['EnvParameters']['TargetNode'].split(' ')
163 else:
164 targetlist = ["localhost"]
165
166 for key in retval:
167 for i in range (len(targetlist)):
168 if key in targetlist[i]:
169 host_index = i
170
171 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
172 int(retval[key][3]) == 0:
173
174 if host_index:
175 Result['Results'][targetlist[host_index]] = \
176 {"GroupName": 'na', "StatusCode": 200, \
177 "StatusMessage": "SUCCESS"}
178 else:
179 Result['Results'][key] = \
180 {"GroupName": 'na', "StatusCode": 200, \
181 "StatusMessage": "SUCCESS"}
182 elif int(retval[key][2]) > 0:
183 if host_index:
184 Result['Results'][targetlist[host_index]] = \
185 {"GroupName": 'na', "StatusCode": 400, \
186 "StatusMessage": "NOT REACHABLE"}
187 else:
188 Result['Results'][key] = \
189 {"GroupName": 'na', "StatusCode": 400, \
190 "StatusMessage": "NOT REACHABLE"}
191 elif int(retval[key][3]) > 0:
192 if host_index:
193 Result['Results'][targetlist[host_index]] = \
194 {"GroupName": 'na', "StatusCode": 400, \
195 "StatusMessage": "FAILURE"}
196 else:
197 Result['Results'][key] = \
198 {"GroupName": 'na', "StatusCode": 400, \
199 "StatusMessage": "FAILURE"}
200 else:
201
202 for key in retval:
203
204 if len(TestRecord[Id]['HostNameList']) > 0:
205
206 host_index = []
207 for i in range (len(TestRecord[Id]['HostNameList'])):
208 if key in TestRecord[Id]['HostNameList'][i]:
209 host_index.append(i)
210
211 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
212 int(retval[key][3]) == 0:
213
214 if len(host_index) > 0:
215 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
216 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
217 "StatusCode": 200, "StatusMessage": "SUCCESS"}
218
219 for i in range (1, len(host_index)):
220 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
221 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
222 else:
223 Result['Results'][key] = \
224 {"GroupName": key,
225 "StatusCode": 200, "StatusMessage": "SUCCESS"}
226
227 elif int(retval[key][2]) > 0:
228
229 if len(host_index) > 0:
230 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
231 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
232 "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
233
234 for i in range (1, len(host_index)):
235 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
236 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
237 else:
238 Result['Results'][key] = \
239 {"GroupName": key,
240 "StatusCode": 200, "StatusMessage": "NOT REACHABLE"}
241
242 elif int(retval[key][3]) > 0:
243
244 if len(host_index) > 0:
245 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
246 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
247 "StatusCode": 400, "StatusMessage": "FAILURE"}
248
249 for i in range (1, len(host_index)):
250 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
251 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
252 else:
253 Result['Results'][key] = \
254 {"GroupName": key,
255 "StatusCode": 200, "StatusMessage": "FAILURE"}
256 else:
257 host_index = None
258 for i in range (len(TestRecord[Id]['NodeList'])):
259 if key in TestRecord[Id]['NodeList'][i]:
260 host_index = i
261
262 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
263 int(retval[key][3]) == 0:
264 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
265 {"GroupName": 'na', "StatusCode": 200, \
266 "StatusMessage": "SUCCESS"}
267 elif int(retval[key][2]) > 0:
268 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
269 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
270 elif int(retval[key][3]) > 0:
271 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
272 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
273
274 callback (Id, Result, Output, Log, returncode)
275
276class TestManager (object):
277
278 @cherrypy.expose
279 @cherrypy.tools.json_out()
280 @cherrypy.tools.json_in()
281 @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
282
283 def Dispatch(self, **kwargs):
284
285 # Let cherrypy error handler deal with malformed requests
286 # No need for explicit error handler, we use default ones
287
288 time_now = datetime.datetime.utcnow()
289
290 # Erase old test results (2x timeout)
291 if TestRecord:
292 for key in TestRecord.copy():
293 delta_time = (time_now - TestRecord[key]['Time']).seconds
294 if delta_time > 2*TestRecord[key]['Timeout']:
295 print "Deleted history for test", key
296 if os.path.exists(TestRecord[key]['Path']):
297 shutil.rmtree (TestRecord[key]['Path'])
298 del TestRecord[key]
299
300 print "***> in RestServer.Dispatch:", cherrypy.request.method
301
302 HomeDir = os.path.dirname(os.path.realpath("~/"))
303
304 if 'POST' in cherrypy.request.method:
305
306 input_json = cherrypy.request.json
307 print " Payload: ", input_json
308
309 if 'Id' in input_json and 'PlaybookName' in input_json:
310
311 if True:
312
313 if not input_json['Id'] in TestRecord:
314
315 Id = input_json['Id']
316 PlaybookName = input_json['PlaybookName']
317
318 version = None
319 if 'Version' in input_json:
320 version = input_json['Version']
321
322 AnsibleInvFail = True
323 AnsiblePlaybookFail = True
324
325 MySqlConFail = True
326 MySqlCause = ''
327
328 LocalNodeList = None
329
330 str_uuid = str (uuid.uuid4())
331
332 LCM = PlaybookName.split(".")[0].split('_')[-1]
333 PlaybookDir = HomeDir + "/" + ansible_temp + "/" + \
334 PlaybookName.split(".")[0] + "_" + str_uuid
335 AnsibleInv = LCM + "_" + "inventory"
336 ArchiveFlag = False
337
338 print " LCM: ", LCM
339 print " PlaybookDir: ", ansible_temp + PlaybookDir.split(ansible_temp)[1]
340 print " AnsibleInv: ", AnsibleInv
341 print " ansible_temp: ", ansible_temp
342
343 if not os.path.exists(HomeDir + "/" + ansible_temp):
344 os.makedirs(HomeDir + "/" + ansible_temp)
345
346 os.mkdir(PlaybookDir)
347
348 # Process inventory file for target
349
350 hostgrouplist = []
351 hostnamelist = []
352
353 NodeList = []
354 if 'NodeList' in input_json:
355 NodeList = input_json['NodeList']
356
357 print " NodeList: ", NodeList
358
359 if NodeList == []:
360 # By default set to local host
361 AnsibleInvFail = False
362
363 LocalNodeList = "host"
364 LocalCredentials = "localhost ansible_connection=local"
365 f = open(PlaybookDir + "/" + AnsibleInv, "w")
366 f.write("[" + LocalNodeList + "]\n")
367 f.write(LocalCredentials)
368 f.close()
369
370 else:
371
372 if from_files:
373
374 # Get credentials from file
375
376 data_inventory_orig = {}
377 data_inventory_target = {}
378 curr_group = None
379
380 print "***>", ansible_path + "/" + ansible_inv
381 f = open(ansible_path + "/" + ansible_inv, "r")
382 for line in f:
383 line = line.rstrip()
384
385 if len(line)> 0:
386 if '#' not in line:
387 if "[" in line and "]" in line:
388 data_inventory_orig[line] = []
389 curr_group = line
390 else:
391 data_inventory_orig[curr_group].append(line)
392 f.close()
393
394 for node in NodeList:
395 Fail = True
396 if "[" + node + "]" in data_inventory_orig:
397 if not "[" + node + "]" in data_inventory_target:
398
399 print "RESET", "[" + node + "]"
400 data_inventory_target["[" + node + "]"] = []
401 else:
402 print "OK", "[" + node + "]"
403 Fail = False
404 for cred in data_inventory_orig["[" + node + "]"]:
405 data_inventory_target["[" + node + "]"].append(cred)
406
407 else:
408 for key in data_inventory_orig:
409 if node in " ".join(data_inventory_orig[key]):
410 if not key in data_inventory_target:
411 data_inventory_target[key] = []
412 for cred in data_inventory_orig[key]:
413 if node in cred:
414 data_inventory_target[key].append(cred)
415 Fail = False
416
417 if Fail:
418 data_inventory_target["["+node+"]"] = \
419 [node + " ansible_connection=ssh ansible_ssh_user=na ansible_ssh_private_key_file=na"]
420
421 AnsibleInvFail = False
422
423 f = open(PlaybookDir + "/" + AnsibleInv, "w")
424 for key in data_inventory_target:
425 f.write(key + "\n")
426 for rec in data_inventory_target[key]:
427 hostgrouplist.append(key.replace("[", '').replace("]", ''))
428 hostnamelist.append(rec.split(' ')[0])
429 f.write(rec + "\n")
430 f.close()
431
432 else:
433
434 # Get credentials from mySQL
435
436 sqlintf = AnsibleSql.mySql (host, user, passwd,
437 db)
438 if sqlintf.con:
439 MySqlConFail = False
440 errorCode, diag = readCredentials (sqlintf,
441 NodeList)
442
443 print errorCode, diag
444 if len (diag) > 0:
445 f = open(PlaybookDir + "/" + AnsibleInv,
446 "w")
447 AnsibleInvFail = False
448 # [hostgroup, hostname, credentials]
449 for i in range(len(diag)):
450 f.write('[' + diag[i][0] + ']' + "\n")
451 f.write(diag[i][1]+ " " + diag[i][2] + "\n\n")
452 hostgrouplist.append(diag[i][0])
453 hostnamelist.append(diag[i][1])
454 f.close()
455 else:
456 MySqlConFailCause = sqlintf.error
457 sqlintf.Close()
458
459 timeout = timeout_seconds
460 if 'Timeout' in input_json:
461 timeout = int (input_json['Timeout'])
462
463 EnvParam = {}
464 if 'EnvParameters' in input_json:
465 EnvParam = input_json['EnvParameters']
466
467 LocalParam = {}
468 if 'LocalParameters' in input_json:
469 LocalParam = input_json['LocalParameters']
470
471 FileParam = {}
472 if 'FileParameters' in input_json:
473 FileParam = input_json['FileParameters']
474
475 callback_flag = None
476 if 'CallBack' in input_json:
477 callback_flag = input_json['CallBack']
478
479 TestRecord[Id] = {'PlaybookName': PlaybookName,
480 'LCM': LCM,
481 'Version': version,
482 'NodeList': NodeList,
483 'HostGroupList': hostgrouplist,
484 'HostNameList': hostnamelist,
485 'Time': time_now,
486 'Duration': timeout,
487 'Timeout': timeout,
488 'EnvParameters': EnvParam,
489 'LocalParameters': LocalParam,
490 'FileParameters': FileParam,
491 'CallBack': callback_flag,
492 'Result': {"StatusCode": 100,
493 "StatusMessage": 'PENDING',
494 "ExpectedDuration": str(timeout) + "sec"},
495 'Log': '',
496 'Output': {},
497 'Path': PlaybookDir,
498 'Mandatory': None}
499
500 # Write files
501
502 if not TestRecord[Id]['FileParameters'] == {}:
503 for key in TestRecord[Id]['FileParameters']:
504 filename = key
505 filecontent = TestRecord[Id]['FileParameters'][key]
506 f = open(PlaybookDir + "/" + filename, "w")
507 f.write(filecontent)
508 f.close()
509
510
511 # Process playbook
512
513 if from_files:
514
515 # Get playbooks from files
516
517 MySqlConFail = False
518
519 version = None
520 target_PlaybookName = None
521
522 if '@' in PlaybookName:
523 version = PlaybookName.split("@")[1]
524 version = version.replace('.yml','')
525 version = version.replace('.tar.gz','')
526
527 onlyfiles = [f for f in listdir(ansible_path)
528 if isfile(join(ansible_path, f))]
529
530 version_max = '0.00'
531 version_target = ''
532
533 for file in onlyfiles:
534 if LCM in file:
535 temp_version = file.split("@")[1]
536 temp_version = temp_version.replace('.yml','')
537 temp_version = temp_version.replace('.tar.gz','')
538 if version_max < temp_version:
539 version_max = temp_version
540
541 if not version == None:
542 if version in PlaybookName:
543 version_target = version
544 target_PlaybookName = file
545
546 if target_PlaybookName == None:
547 for file in onlyfiles:
548 if LCM in file and version_max in file:
549 target_PlaybookName = file
550 version_target = version_max
551
552 if target_PlaybookName:
553 AnsiblePlaybookFail = False
554 readversion = version_target
555 src = ansible_path + "/" + target_PlaybookName
556 if ".tar.gz" in target_PlaybookName:
557 dest = PlaybookDir + "/" + LCM + ".tar.gz"
558 shutil.copy2(src, dest)
559 retcode = subprocess.call(['tar', '-xvzf',
560 dest, "-C", PlaybookDir])
561 ArchiveFlag = True
562 else:
563 dest = PlaybookDir + "/" + LCM + ".yml"
564 shutil.copy2(src, dest)
565
566 else:
567 # Get playbooks from mySQL
568
569 sqlintf = AnsibleSql.mySql (host, user, passwd, db)
570 if sqlintf.con:
571 MySqlConFail = False
572
573 name, readversion, AnsiblePlaybookFail, diag = \
574 readPlaybook (sqlintf, PlaybookName.split(".")[0],
575 version)
576
577 if not AnsiblePlaybookFail:
578
579 f = open(PlaybookDir + "/" + LCM + diag[1], "w")
580 f.write(diag[0])
581 f.close()
582
583 if ".tar.gz" in diag[1]:
584 retcode = subprocess.call(['tar', '-xvzf',
585 PlaybookDir + "/" + LCM + diag[1], "-C", PlaybookDir])
586 f.close()
587 ArchiveFlag = True
588 else:
589 MySqlConFailCause = sqlintf.error
590 sqlintf.Close()
591
592 if MySqlConFail:
593 if os.path.exists(PlaybookDir):
594 shutil.rmtree (PlaybookDir)
595 del TestRecord[Id]
596 return {"StatusCode": 101,
597 "StatusMessage": "CANNOT CONNECT TO MYSQL: " \
598 + MySqlConFailCause}
599 elif AnsiblePlaybookFail:
600 if os.path.exists(PlaybookDir):
601 shutil.rmtree (PlaybookDir)
602 del TestRecord[Id]
603 return {"StatusCode": 101,
604 "StatusMessage": "PLAYBOOK NOT FOUND"}
605 elif AnsibleInvFail:
606 if os.path.exists(PlaybookDir):
607 shutil.rmtree (PlaybookDir)
608 del TestRecord[Id]
609 return {"StatusCode": 101,
610 "StatusMessage": "NODE LIST CREDENTIALS NOT FOUND"}
611 else:
612
613 # Test EnvParameters
614 playbook_path = None
615 if ArchiveFlag:
616 for dName, sdName, fList in os.walk(PlaybookDir):
617 if LCM+".yml" in fList:
618 playbook_path = dName
619 else:
620 playbook_path = PlaybookDir
621
622 # Store local vars
623 if not os.path.exists(playbook_path + "/vars"):
624 os.mkdir(playbook_path + "/vars")
625 if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
626 os.mknod(playbook_path + "/vars/defaults.yml")
627
628 for key in TestRecord[Id]['LocalParameters']:
629 host_index = []
630 for i in range(len(TestRecord[Id]['HostNameList'])):
631 if key in TestRecord[Id]['HostNameList'][i]:
632 host_index.append(i)
633 if len(host_index) == 0:
634 for i in range(len(TestRecord[Id]['HostGroupList'])):
635 if key in TestRecord[Id]['HostGroupList'][i]:
636 host_index.append(i)
637 if len(host_index) > 0:
638 for i in range(len(host_index)):
639 f = open(playbook_path + "/vars/" +
640 TestRecord[Id]['HostNameList'][host_index[i]] +
641 ".yml", "a")
642 for param in TestRecord[Id]['LocalParameters'][key]:
643 f.write(param + ": " +
644 str (TestRecord[Id]['LocalParameters'][key][param]) +
645 "\n")
646 f.close()
647
648 # Get mandatory parameters from playbook
649 Mandatory = []
650 with open(playbook_path + "/" + LCM + ".yml") as origin_file:
651 for line in origin_file:
652 if "Mandatory" in line:
653 temp = line.split(":")[1].strip().replace(' ', '')
654 if len(temp) > 0:
655 Mandatory = temp.split(",")
656
657 TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'],
658 'LCM': TestRecord[Id]['LCM'],
659 'Version': readversion,
660 'NodeList': TestRecord[Id]['NodeList'],
661 'HostGroupList': TestRecord[Id]['HostGroupList'],
662 'HostNameList': TestRecord[Id]['HostNameList'],
663 'Time': TestRecord[Id]['Time'],
664 'Timeout': TestRecord[Id]['Timeout'],
665 'Duration': TestRecord[Id]['Duration'],
666 'EnvParameters': TestRecord[Id]['EnvParameters'],
667 'LocalParameters': TestRecord[Id]['LocalParameters'],
668 'FileParameters': TestRecord[Id]['FileParameters'],
669 'CallBack': TestRecord[Id]['CallBack'],
670 'Result': TestRecord[Id]['Result'],
671 'Log': TestRecord[Id]['Log'],
672 'Output': TestRecord[Id]['Output'],
673 'Path': TestRecord[Id]['Path'],
674 'Mandatory': Mandatory}
675
676 TestKey = False
677
678 if Mandatory:
679 for val in Mandatory:
680 if EnvParam:
681 if val in EnvParam:
682 TestKey = True
683 else:
684 if LocalParam:
685 for key in TestRecord[Id]['NodeList']:
686 if key in LocalParam:
687 if val in LocalParam[key]:
688 TestKey = True
689 else:
690 if LocalParam:
691 for key in TestRecord[Id]['NodeList']:
692 if key in LocalParam:
693 if val in LocalParam[key]:
694 TestKey = True
695
696 if not TestKey:
697 if os.path.exists(PlaybookDir):
698 shutil.rmtree (PlaybookDir)
699 del TestRecord[Id]
700 return {"StatusCode": 101,
701 "StatusMessage": "MISSING MANDATORY PARAMETER: " + \
702 " ".join(str(x) for x in Mandatory)}
703
704
705 # Cannot use thread because ansible module uses
706 # signals which are only supported in main thread.
707 # So use multiprocess with shared object
708
709 p = Process(target = RunAnsible_Playbook,
710 args = (callback, Id, PlaybookDir + "/" + AnsibleInv,
711 playbook_path + "/" + LCM + ".yml",
712 NodeList, TestRecord, PlaybookDir,
713 ArchiveFlag))
714 p.start()
715 ActiveProcess[Id] = p
716 return TestRecord[Id]['Result']
717 else:
718 return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
719
720 else:
721 return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
722
723 else:
724 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME"}
725
726 elif 'GET' in cherrypy.request.method:
727
728 input_data = parse_query_string(cherrypy.request.query_string)
729
730 print "***> in RestServer.GET"
731 print " Payload: ", input_data, input_data['Type']
732
733 if 'Id' in input_data and 'Type' in input_data:
734 if not ('GetResult' in input_data['Type'] or 'GetOutput' in input_data['Type'] or 'GetLog' in input_data['Type']):
735 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
736 if input_data['Id'] in TestRecord:
737
738 if 'GetResult' in input_data['Type']:
739
740 print "Result:", TestRecord[input_data['Id']]['Result']
741
742 if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
743
744 print "*** Request blocked", input_data['Id']
745
746 while ActiveProcess[input_data['Id']].is_alive():
747 time.sleep(5)
748
749 print "*** Request released ", input_data['Id']
750
751 print TestRecord[input_data['Id']]['Result']
752 if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
753 out_obj = TestRecord[input_data['Id']]['Result']['Results']
754 else:
755 out_obj = {"StatusCode": 200,
756 "StatusMessage": "FINISHED",
757 "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
758 "Version": TestRecord[input_data['Id']]["Version"],
759 "Duration": TestRecord[input_data['Id']]["Duration"],
760 "Results": TestRecord[input_data['Id']]['Result']['Results']}
761 if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
762 for key in out_obj["Results"]:
763 if key in TestRecord[input_data['Id']]['Output']['Output']:
764 out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
765
766 return out_obj
767
768 elif 'GetOutput' in input_data['Type']:
769
770 if TestRecord[input_data['Id']]['Output'] == {} and \
771 getresults_block:
772
773 print "*** Request blocked", input_data['Id']
774
775 while TestRecord[input_data['Id']]['Output'] == {} \
776 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
777 time.sleep(5)
778
779 print "*** Request released ", input_data['Id']
780
781 print "Output:", TestRecord[input_data['Id']]['Output']
782 return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
783 else:
784 # GetLog
785
786 if TestRecord[input_data['Id']]['Log'] == '' and \
787 getresults_block:
788
789 print "*** Request blocked", input_data['Id']
790
791 while TestRecord[input_data['Id']]['Log'] == '' \
792 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
793 time.sleep(5)
794
795 print "*** Request released ", input_data['Id']
796
797 print "Log:", TestRecord[input_data['Id']]['Log']
798 return {"Log": TestRecord[input_data['Id']]['Log']}
799 else:
800 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
801 else:
802 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
803 elif 'DELETE' in cherrypy.request.method:
804 input_data = parse_query_string(cherrypy.request.query_string)
805
806 print "***> in RestServer.DELETE"
807 print " Payload: ", input_data
808
809 if input_data['Id'] in TestRecord:
810 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
811 print " Path:", TestRecord[input_data['Id']]['Path']
812 if os.path.exists(TestRecord[input_data['Id']]['Path']):
813 shutil.rmtree (TestRecord[input_data['Id']]['Path'])
814 TestRecord.pop (input_data['Id'])
815 if input_data['Id'] in ActiveProcess:
816 ActiveProcess.pop (input_data['Id'])
817
818 return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
819 else:
820 return {"StatusCode": 200, "StatusMessage": "PENDING"}
821 else:
822 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
823
824
825if __name__ == '__main__':
826
827 # Read configuration
828
829 config_file_path = "RestServer_config"
830
831 if not os.path.exists(config_file_path):
832 print '[INFO] The config file does not exist'
833 sys.exit(0)
834
835 ip = 'na'
836 port = 'na'
837 tls = False
838 auth = False
839 pub = 'na'
840 id = 'na'
841 priv = 'na'
842 psswd = 'na'
843 timeout_seconds = 'na'
844 ansible_path = 'na'
845 ansible_inv = 'na'
846 ansible_temp = 'na'
847 host = 'na'
848 user = 'na'
849 passwd = 'na'
850 db = 'na'
851 getresults_block = False
852 from_files = False
853
854 file = open(config_file_path, 'r')
855 for line in file.readlines():
856 if '#' not in line:
857 if 'ip:' in line:
858 ip = line.split(':')[1].strip()
859 elif 'port:' in line:
860 port = line.split(':')[1].strip()
861 elif 'tls:' in line:
862 tls = 'YES' in line.split(':')[1].strip().upper()
863 elif 'auth:' in line:
864 auth = 'YES' in line.split(':')[1].strip().upper()
865 if tls and 'priv:' in line:
866 priv = line.split(':')[1].strip()
867 if tls and 'pub:' in line:
868 pub = line.split(':')[1].strip()
869 if auth and 'id:' in line:
870 id = line.split(':')[1].strip()
871 if auth and 'psswd:' in line:
872 psswd = line.split(':')[1].strip()
873 if 'timeout_seconds' in line:
874 timeout_seconds = int (line.split(':')[1].strip())
875 if 'ansible_path' in line:
876 ansible_path = line.split(':')[1].strip()
877 if 'ansible_inv' in line:
878 ansible_inv = line.split(':')[1].strip()
879 if not os.path.exists(ansible_path + "/" + ansible_inv):
880 print '[INFO] The ansible_inv file does not exist'
881 sys.exit(0)
882 if 'ansible_temp' in line:
883 ansible_temp = line.split(':')[1].strip()
884 if 'host' in line:
885 host = line.split(':')[1].strip()
886 if 'user' in line:
887 user = line.split(':')[1].strip()
888 if 'passwd' in line:
889 passwd = line.split(':')[1].strip()
890 if 'db' in line:
891 db = line.split(':')[1].strip()
892 if 'getresults_block' in line:
893 getresults_block = 'YES' in line.split(':')[1].strip().upper()
894 if 'from_files' in line:
895 from_files = 'YES' in line.split(':')[1].strip().upper()
896 file.close()
897
898 # Initialization
899
900 global_conf = {
901 'global': {
902 'server.socket_host': ip,
903 'server.socket_port': int(port),
904 'server.protocol_version': 'HTTP/1.1'
905 }
906 }
907
908 if tls:
909 # Use pythons built-in SSL
910 cherrypy.server.ssl_module = 'builtin'
911
912 # Point to certificate files
913
914 if not os.path.exists(pub):
915 print '[INFO] The public certificate does not exist'
916 sys.exit(0)
917
918 if not os.path.exists(priv):
919 print '[INFO] The private key does not exist'
920 sys.exit(0)
921
922 cherrypy.server.ssl_certificate = pub
923 cherrypy.server.ssl_private_key = priv
924
925 if auth:
926 userpassdict = {id: psswd}
927 checkpassword = cherrypy.lib.auth_basic.checkpassword_dict(userpassdict)
928
929 app_conf = {'/':
930 {'tools.auth_basic.on': True,
931 'tools.auth_basic.realm': 'earth',
932 'tools.auth_basic.checkpassword': checkpassword,
933 }
934 }
935
936 cherrypy.tree.mount(TestManager(), '/', app_conf)
937 else:
938 cherrypy.tree.mount(TestManager(), '/')
939
940 cherrypy.config.update(global_conf)
941
942 # Start server
943
944 cherrypy.engine.start()
945 cherrypy.engine.block()