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