3 * ============LICENSE_START=======================================================
5 * ================================================================================
6 * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
7 * ================================================================================
8 * Copyright (C) 2017 Amdocs
9 * =============================================================================
10 * 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
14 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 * ============LICENSE_END=========================================================
26 import time, datetime, json, os, sys, subprocess, re
36 from cherrypy.lib.httputil import parse_query_string
37 from cherrypy.lib import auth_basic
39 from multiprocessing import Process, Manager
41 from AnsibleModule import ansibleSysCall
42 from BuildHostFile import buildHostsSysCall
44 from os import listdir
45 from os.path import isfile, join
47 TestRecord = Manager().dict()
50 def validate_password(realm, username, password):
51 comp = crypt.crypt(password, salt)
52 if username in userpassdict and userpassdict[username] == comp:
57 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
58 output = p.stdout.readlines()
61 for i in range(len(output)):
62 output[i] = output[i].strip()
65 def callback (Id, Result, Output, Log, returncode):
67 print "***> in RestServer.callback"
70 time_now = datetime.datetime.utcnow()
71 delta_time = (time_now - TestRecord[Id]['Time']).total_seconds()
72 Result['PlaybookName'] = TestRecord[Id]['PlaybookName']
73 Result['Version'] = TestRecord[Id]['Version']
75 Result['StatusCode'] = 500
76 Result['StatusMessage'] = "TERMINATED"
78 Result['StatusCode'] = 200
79 Result['StatusMessage'] = "FINISHED"
81 # Need to update the whole data structure for key=Id otherwise Manager is not updated
82 TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'],
83 'Version': TestRecord[Id]['Version'],
84 'NodeList': TestRecord[Id]['NodeList'],
85 'HostGroupList': TestRecord[Id]['HostGroupList'],
86 'HostNameList': TestRecord[Id]['HostNameList'],
87 'Time': TestRecord[Id]['Time'],
88 'Timeout': TestRecord[Id]['Timeout'],
89 'Duration': str(delta_time),
90 'EnvParameters': TestRecord[Id]['EnvParameters'],
91 'LocalParameters': TestRecord[Id]['LocalParameters'],
92 'FileParameters': TestRecord[Id]['FileParameters'],
93 'CallBack': TestRecord[Id]['CallBack'],
97 'Path': TestRecord[Id]['Path'],
98 'Mandatory': TestRecord[Id]['Path']}
100 if not TestRecord[Id]['CallBack'] == None:
102 # Posting results to callback server
104 data = {"StatusCode": 200,
105 "StatusMessage": "FINISHED",
106 "PlaybookName": TestRecord[Id]["PlaybookName"],
107 "Version": TestRecord[Id]["Version"],
108 "Duration": TestRecord[Id]["Duration"],
109 "Results": TestRecord[Id]['Result']['Results']}
111 cherrypy.log("CALLBACK: TestRecord[Id]['Output']['Output']:", str(TestRecord[Id]['Output']['Output']))
112 cherrypy.log("CALLBACK: Results:", str(data["Results"]))
114 if not TestRecord[Id]['Output']['Output'] == {}:
115 for key in data["Results"]:
116 if key in TestRecord[Id]['Output']['Output']:
117 data["Results"][key]["Output"] = TestRecord[Id]['Output']['Output'][key]
119 print " Posting to", TestRecord[Id]['CallBack']
121 s = requests.Session()
122 r = s.post(TestRecord[Id]['CallBack'], data = json.dumps(data),
123 headers = {'content-type': 'application/json'})
124 print " Response", r.status_code, r.text
126 def RunAnsible_Playbook (callback, Id, Inventory, Playbook, NodeList, TestRecord,
129 print "***> in RestServer.RunAnsible_Playbook"
131 # Run test in playbook for given target
134 retval, log, returncode = ansibleSysCall (Inventory, Playbook, NodeList,
135 TestRecord[Id]['Mandatory'],
136 TestRecord[Id]['EnvParameters'],
137 TestRecord[Id]['LocalParameters'],
138 TestRecord[Id]['Timeout'],
142 cherrypy.log("Return code:" + str(returncode))
143 cherrypy.log("Return val:" + str(retval))
146 #Output = {'Output': {}}
149 onlyfiles = [f for f in listdir(Path)
150 if isfile(join(Path, f))]
152 cherrypy.log("Checking for results.txt files: ")
153 for file in onlyfiles:
154 if "results.txt" in file:
155 # if file.endswith("results.txt"):
156 cherrypy.log("results file: " + file)
157 f = open(Path + "/" + file, "r")
158 resultsData = f.read() # Not to pass vnf instance name
159 OutputP = json.loads(resultsData)
160 Output['Output'] = OutputP
161 cherrypy.log("Output = " + str(Output['Output']))
162 #Output['Output'][key] = f.read() # To pass vnf instance name
166 Output = {'Output': {}}
168 Result = {'Results': {}}
169 if 'could not be found' in Log:
170 Result['Results'] = {"StatusCode": 101,
171 "StatusMessage": "PLAYBOOK NOT FOUND"}
172 if returncode == 137:
173 Result['Results'] = {"StatusCode": 500,
174 "StatusMessage": "TERMINATED"}
176 elif TestRecord[Id]['NodeList'] == []:
180 if 'TargetNode' in TestRecord[Id]['EnvParameters']:
181 targetlist = TestRecord[Id]['EnvParameters']['TargetNode'].split(' ')
183 targetlist = ["localhost"]
186 for i in range (len(targetlist)):
187 if key in targetlist[i]:
190 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
191 int(retval[key][3]) == 0:
194 Result['Results'][targetlist[host_index]] = \
195 {"GroupName": 'na', "StatusCode": 200, \
196 "StatusMessage": "SUCCESS"}
198 Result['Results'][key] = \
199 {"GroupName": 'na', "StatusCode": 200, \
200 "StatusMessage": "SUCCESS"}
201 elif int(retval[key][2]) > 0:
203 Result['Results'][targetlist[host_index]] = \
204 {"GroupName": 'na', "StatusCode": 400, \
205 "StatusMessage": "NOT REACHABLE"}
207 Result['Results'][key] = \
208 {"GroupName": 'na', "StatusCode": 400, \
209 "StatusMessage": "NOT REACHABLE"}
210 elif int(retval[key][3]) > 0:
212 Result['Results'][targetlist[host_index]] = \
213 {"GroupName": 'na', "StatusCode": 400, \
214 "StatusMessage": "FAILURE"}
216 Result['Results'][key] = \
217 {"GroupName": 'na', "StatusCode": 400, \
218 "StatusMessage": "FAILURE"}
223 if len(TestRecord[Id]['HostNameList']) > 0:
226 for i in range (len(TestRecord[Id]['HostNameList'])):
227 if key in TestRecord[Id]['HostNameList'][i]:
230 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
231 int(retval[key][3]) == 0:
233 if len(host_index) > 0:
234 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
235 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
236 "StatusCode": 200, "StatusMessage": "SUCCESS"}
238 for i in range (1, len(host_index)):
239 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
240 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
242 Result['Results'][key] = \
244 "StatusCode": 200, "StatusMessage": "SUCCESS"}
246 elif int(retval[key][2]) > 0:
248 if len(host_index) > 0:
249 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
250 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
251 "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
253 for i in range (1, len(host_index)):
254 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
255 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
257 Result['Results'][key] = \
259 "StatusCode": 200, "StatusMessage": "NOT REACHABLE"}
261 elif int(retval[key][3]) > 0:
263 if len(host_index) > 0:
264 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
265 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
266 "StatusCode": 400, "StatusMessage": "FAILURE"}
268 for i in range (1, len(host_index)):
269 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
270 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
272 Result['Results'][key] = \
274 "StatusCode": 200, "StatusMessage": "FAILURE"}
277 for i in range (len(TestRecord[Id]['NodeList'])):
278 if key in TestRecord[Id]['NodeList'][i]:
281 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
282 int(retval[key][3]) == 0:
283 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
284 {"GroupName": 'na', "StatusCode": 200, \
285 "StatusMessage": "SUCCESS"}
286 elif int(retval[key][2]) > 0:
287 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
288 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
289 elif int(retval[key][3]) > 0:
290 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
291 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
293 callback (Id, Result, Output, Log, returncode)
295 class TestManager (object):
298 @cherrypy.tools.json_out()
299 @cherrypy.tools.json_in()
300 @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
302 def Dispatch(self, **kwargs):
304 # Let cherrypy error handler deal with malformed requests
305 # No need for explicit error handler, we use default ones
307 time_now = datetime.datetime.utcnow()
309 # Erase old test results (2x timeout)
310 # Do cleanup too of ActiveProcess list and old Records - PAP
312 for key in TestRecord.copy():
313 cherrypy.log( "LOOKING AT ALL TestRecords: " + str(key))
314 if key in ActiveProcess:
315 if not ActiveProcess[key].is_alive(): # Just to cleanup defunct processes
316 cherrypy.log( "Not ActiveProcess for ID: " + str(key))
317 delta_time = (time_now - TestRecord[key]['Time']).seconds
318 if delta_time > 2*TestRecord[key]['Timeout']:
319 cherrypy.log( "DELETED HISTORY for ID: " + str(key))
320 if key in ActiveProcess:
321 if not ActiveProcess[key].is_alive():
322 ActiveProcess.pop (key)
323 cherrypy.log( "DELETED ActiveProcess for ID: " + str(key))
324 #if os.path.exists(TestRecord[key]['Path']):
325 # don't remove run dirrectory
326 #shutil.rmtree (TestRecord[key]['Path'])
329 cherrypy.log("RestServer.Dispatch: " + cherrypy.request.method)
332 if 'POST' in cherrypy.request.method:
334 input_json = cherrypy.request.json
335 cherrypy.log("Payload: " + str(input_json))
337 if 'Id' in input_json and 'PlaybookName' in input_json and 'EnvParameters' in input_json:
341 if not input_json['Id'] in TestRecord:
342 # check if Id exists in previous run dirctory
344 s_cmd = 'ls ' + ansible_temp + '/*_' + input_json['Id']
345 #if subprocess.check_output([s_cmd, ]):
346 Id = input_json['Id']
347 if glob.glob( ansible_temp + '/*_' + input_json['Id']):
348 cherrypy.log("Old directory found for ID: " + Id)
349 return {"StatusCode": 101, "StatusMessage": "TEST ID FILE ALREADY DEFINED"}
351 PlaybookName = input_json['PlaybookName']
352 # if required it should be passed as an argument
353 EnvParameters = input_json['EnvParameters']
355 # The lines below are to test multiple EnvParameters being passed
356 #for i in EnvParameters:
357 # cherrypy.log("EnvParameter object: " + i)
358 # cherrypy.log(" EnvParameter Value: " + EnvParameters[ i ])
360 # Now get things out of EnvParameters
362 VNF_instance = EnvParameters.get('vnf_instance')
364 # Get Version if present
366 if 'Version' in input_json:
367 version = input_json['Version']
371 HaveInventoryNames = False
372 inventory_names = None
373 if 'InventoryNames' in input_json:
374 inventory_names = input_json['InventoryNames']
375 HaveInventoryNames = True
377 #AnsibleInvFail = True
378 AnsiblePlaybookFail = True
382 str_uuid = str (uuid.uuid4())
385 VnfType= PlaybookName.split("/")[0]
387 cherrypy.log( "Request USER : " + cherrypy.request.login)
388 cherrypy.log( "Request Decode: ID " + Id)
389 cherrypy.log( "Request Decode: VnfType " + VnfType)
390 cherrypy.log( "Request Decode: EnvParameters " + json.dumps(EnvParameters))
392 # Verify VNF_instance was passed in EnvParameters
393 if VNF_instance != None:
394 cherrypy.log( "Request Decode: VnfInstance " + VNF_instance)
396 cherrypy.log( "StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED" )
397 return {"StatusCode": 107,
398 "StatusMessage": "VNF_instance NOT PROVIDED"}
400 if inventory_names != None:
401 cherrypy.log( "Request Decode: Inventory Names " + inventory_names)
403 cherrypy.log( "Request Decode: Inventory Names " + "Not provided")
405 cherrypy.log( "Request Decode: PlaybookName " + PlaybookName)
406 PlayBookFunction = PlaybookName.rsplit("/",2)[1]
407 PlayBookFile = PlayBookFunction + "/site.yml"
408 cherrypy.log( "Request Decode: PlaybookFunction " + PlayBookFunction)
409 cherrypy.log( "Request Decode: Playbook file " + PlayBookFile)
411 BaseDir = ansible_path + "/" + PlaybookName.rsplit("/",1)[0]
412 CopyDir = ansible_path + "/" + PlaybookName.rsplit("/",2)[0]
413 cherrypy.log( "Request Decode: Basedir " + BaseDir)
414 cherrypy.log( "Request Decode: Copydir " + CopyDir)
417 PlaybookDir = ansible_temp + "/" + \
418 VNF_instance + "_" + str_uuid + "_" + str(Id)
420 # AnsibleInv is the directory where the host file to be run exsists
421 AnsibleInv = ansible_path + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance
424 # Create base run directory if it doesn't exist
425 if not os.path.exists(ansible_temp):
426 cherrypy.log( "Creating Base Run Directory: " + ansible_temp)
427 os.makedirs(ansible_temp)
429 if not os.path.exists( CopyDir ):
430 cherrypy.log("Playbook Not Found")
431 return {"StatusCode": 101,
432 "StatusMessage": "PLAYBOOK NOT FOUND"}
434 # copy static playbook dir to run dir
435 cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir)
436 shutil.copytree(CopyDir, PlaybookDir)
437 cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;"
438 cmd="/usr/bin/find " + PlaybookDir + " -exec chmod +rx {} \;"
442 cherrypy.log( "PlaybookDir: " + PlaybookDir)
443 cherrypy.log( "AnsibleInv: " + AnsibleInv)
445 # Process inventory file for target
451 if 'NodeList' in input_json:
452 NodeList = input_json['NodeList']
454 cherrypy.log("NodeList: " + str(NodeList));
458 cherrypy.log( "*** NodeList - Empty ***")
463 ###############################################################################
464 ##### Host file processing ###########################
465 ##### 1. Use file delivered with playbook ###########################
466 ##### 2. If HostNames + NodeList generate and use ###########################
467 ###############################################################################
469 #Verify inventory directory exists
470 path = PlaybookDir + "/inventory/"
471 if not os.path.isdir(path):
472 cherrypy.log ("Inventory directory %s does not exist - create it" % path)
476 cherrypy.log ("Creation of the directory %s failed" % path)
478 cherrypy.log ("Successfully created the directory %s " % path)
480 #location of host file - Default
481 HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
482 cherrypy.log("HostFile: " + HostFile)
484 # if NodeList and InventoryNames need to build host file
485 if HaveInventoryNames & HaveNodeList:
486 cherrypy.log("Build host file from NodeList")
487 ret = buildHostsSysCall (input_json, PlaybookDir, inventory_names)
489 cherrypy.log("Returning Error: Not running Playbook")
490 return {"StatusCode": 105,
491 "StatusMessage": "NodeList: Missing vnfc-type field"}
493 # Having been built now copy new file to correct file
494 shutil.copy(PlaybookDir + "/host_file.txt", HostFile)
495 cherrypy.log("Copying Generated host file to: " + HostFile)
497 timeout = timeout_seconds
498 if 'Timeout' in input_json:
499 timeout = int (input_json['Timeout'])
500 cherrypy.log("Timeout from API: " + str(timeout))
503 cherrypy.log("Timeout not passed from API using default: " + str(timeout))
506 if 'EnvParameters' in input_json:
507 EnvParam = input_json['EnvParameters']
510 if 'LocalParameters' in input_json:
511 LocalParam = input_json['LocalParameters']
514 if 'FileParameters' in input_json:
515 FileParam = input_json['FileParameters']
518 if 'CallBack' in input_json:
519 callback_flag = input_json['CallBack']
521 # if AnsibleServer is not set to 'na' don't send AnsibleServer in PENDING responce.
522 if AnsibleServer != 'na':
523 TestRecord[Id] = {'PlaybookName': PlaybookName,
525 'NodeList': NodeList,
526 'HostGroupList': hostgrouplist,
527 'HostNameList': hostnamelist,
531 'EnvParameters': EnvParam,
532 'LocalParameters': LocalParam,
533 'FileParameters': FileParam,
534 'CallBack': callback_flag,
535 'Result': {"StatusCode": 100,
536 "StatusMessage": 'PENDING',
537 "AnsibleServer": str(AnsibleServer),
538 "ExpectedDuration": str(timeout) + "sec"},
544 TestRecord[Id] = {'PlaybookName': PlaybookName,
546 'NodeList': NodeList,
547 'HostGroupList': hostgrouplist,
548 'HostNameList': hostnamelist,
552 'EnvParameters': EnvParam,
553 'LocalParameters': LocalParam,
554 'FileParameters': FileParam,
555 'CallBack': callback_flag,
556 'Result': {"StatusCode": 100,
557 "StatusMessage": 'PENDING',
558 "ExpectedDuration": str(timeout) + "sec"},
564 cherrypy.log("Test_Record: " + str(TestRecord[Id]))
567 if not TestRecord[Id]['FileParameters'] == {}:
568 for key in TestRecord[Id]['FileParameters']:
570 filecontent = TestRecord[Id]['FileParameters'][key]
571 f = open(PlaybookDir + "/" + filename, "w")
577 if os.path.exists( ansible_path + '/' + PlaybookName):
578 AnsiblePlaybookFail = False
580 if AnsiblePlaybookFail:
581 #if os.path.exists(PlaybookDir):
582 #shutil.rmtree (PlaybookDir)
584 return {"StatusCode": 101,
585 "StatusMessage": "PLAYBOOK NOT FOUND"}
589 playbook_path = PlaybookDir
592 if not os.path.exists(playbook_path + "/vars"):
593 os.mkdir(playbook_path + "/vars")
594 if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
595 os.mknod(playbook_path + "/vars/defaults.yml")
597 ###################################################
599 #write local parameters passed into defaults.yml
601 f = open(playbook_path + "/vars/defaults.yml","a")
602 #for id, record in TestRecord.items():
603 print TestRecord[Id]['LocalParameters']
604 local_parms = TestRecord[Id]['LocalParameters']
605 for key, value in local_parms.items():
606 f.write(key +"=" + value + "\n");
608 ###################################################
610 for key in TestRecord[Id]['LocalParameters']:
612 for i in range(len(TestRecord[Id]['HostNameList'])):
613 if key in TestRecord[Id]['HostNameList'][i]:
615 if len(host_index) == 0:
616 for i in range(len(TestRecord[Id]['HostGroupList'])):
617 if key in TestRecord[Id]['HostGroupList'][i]:
619 if len(host_index) > 0:
620 for i in range(len(host_index)):
621 f = open(playbook_path + "/vars/" +
622 TestRecord[Id]['HostNameList'][host_index[i]] +
624 for param in TestRecord[Id]['LocalParameters'][key]:
625 f.write(param + ": " +
626 str (TestRecord[Id]['LocalParameters'][key][param]) +
631 # write some info out to files before running
633 f = open(playbook_path + "/User.txt", "a")
634 f.write(cherrypy.request.login)
636 f = open(playbook_path + "/PlaybookName.txt", "a")
637 f.write(PlaybookName)
639 f = open(playbook_path + "/PlaybookExDir.txt", "a")
640 f.write(PlaybookDir + "/" + PlayBookFunction)
642 f = open(playbook_path + "/JsonRequest.txt", "w")
643 #f.write(str(input_json))
644 #print( json.dumps(input_json, indent=4, sort_keys=True))
645 f.write( json.dumps(input_json, indent=4, sort_keys=True))
649 # Check that HostFile exists
650 if not os.path.isfile(HostFile):
651 cherrypy.log("Inventory file Not Found: " + HostFile)
652 return {"StatusCode": 101,
653 "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"}
655 # Cannot use thread because ansible module uses
656 # signals which are only supported in main thread.
657 # So use multiprocess with shared object
658 # args = (callback, Id, PlaybookDir + "/" + AnsibleInv,
660 p = Process(target = RunAnsible_Playbook,
661 args = (callback, Id, HostFile,
662 PlaybookDir + '/' + PlayBookFile,
663 NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction,
666 ActiveProcess[Id] = p
667 return TestRecord[Id]['Result']
669 cherrypy.log("TEST ID ALREADY DEFINED")
670 return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
673 return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
676 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"}
678 elif 'GET' in cherrypy.request.method:
680 # Lets pause for a second just incase the resquest was just kicked off
683 input_data = parse_query_string(cherrypy.request.query_string)
685 # Verify we have a Type passed in GET request
686 if not ( 'Type' in input_data):
687 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
690 cherrypy.log( "Request USER: " + cherrypy.request.login)
691 cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type'])
693 if 'LogRest' in input_data['Type']:
695 sys.stdout = open("/var/log/RestServer.log", "w")
697 # Just a debug to dump any records
698 if 'GetStatus' in input_data['Type']:
699 cherrypy.log( "******** Dump Records **********")
700 if TestRecord.items():
701 for id, record in TestRecord.items():
702 cherrypy.log( " Id: " + id)
703 cherrypy.log( "Record: " + str(record))
705 cherrypy.log(" No Records to dump")
707 if 'Id' in input_data and 'Type' in input_data:
708 if not ('GetResult' in input_data['Type'] or 'GetOutputLog' in input_data['Type'] or'GetTheOutput' in input_data['Type'] or 'GetLog' in input_data['Type']):
709 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
710 if input_data['Id'] in TestRecord:
712 if 'GetResult' in input_data['Type']:
714 cherrypy.log( " ** GetResult for: " + str (input_data['Id']))
716 if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
719 #check if playbook is still running
720 while ActiveProcess[input_data['Id']].is_alive():
721 cherrypy.log( "*** Playbook running returning PENDING for " + str(input_data['Id']))
723 ## If still running return PENDING response
725 if AnsibleServer != 'na':
726 return {"StatusCode": 100,
727 "StatusMessage": 'PENDING',
728 "AnsibleServer": str(AnsibleServer)}
730 return {"StatusCode": 100,
731 "StatusMessage": 'PENDING'}
734 #cherrypy.log( "*** Request released " + input_data['Id'])
736 cherrypy.log(str( TestRecord[input_data['Id']]['Result']))
737 cherrypy.log("Output: " + str( TestRecord[input_data['Id']]['Output']))
738 cherrypy.log("StatusCode: " + str( TestRecord[input_data['Id']]['Result']['StatusCode']))
739 cherrypy.log("StatusMessage: " + str( TestRecord[input_data['Id']]['Result']['StatusMessage']))
741 #out_obj gets returned to GET request
742 if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
743 out_obj = TestRecord[input_data['Id']]['Result']['Results']
745 out_obj = {"StatusCode": 200,
746 "StatusMessage": "FINISHED",
747 "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
748 "Version": TestRecord[input_data['Id']]["Version"],
749 "Duration": TestRecord[input_data['Id']]["Duration"],
750 "Output": TestRecord[input_data['Id']]["Output"]["Output"],
751 "Results": TestRecord[input_data['Id']]['Result']['Results']}
752 if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
753 cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output']))
755 for key in out_obj["Results"]:
756 cherrypy.log("Output key: " + str(key))
757 if key in TestRecord[input_data['Id']]['Output']['Output']:
758 out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
760 cherrypy.log("***** GET RETURNING RESULTS Back ****")
761 cherrypy.log(str(out_obj))
764 elif 'GetStatus' in input_data['Type']:
765 print " Dump Records"
766 for id, record in TestRecord,items():
768 print " Record:" + str(reecord)
770 elif 'GetTheOutput' in input_data['Type']:
772 if TestRecord[input_data['Id']]['Output'] == {} and \
775 cherrypy.log( "*** Request blocked " + input_data['Id'])
777 while TestRecord[input_data['Id']]['Output'] == {} \
778 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
781 cherrypy.log( "*** Request released " + input_data['Id'])
783 cherrypy.log( "Output: " + str(TestRecord[input_data['Id']]['Output']))
784 return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
786 elif 'GetOutputLog' in input_data['Type']:
787 cherrypy.log("GetOutputLog: processing.")
788 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
789 id = input_data['Id']
790 cherrypy.log("Old directory found for ID: " + id)
791 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
794 if os.path.exists (rdir + "/PlaybookExDir.txt"):
795 cherrypy.log("Found PlaybookExDir.txt file")
796 f = open( rdir + '/PlaybookExDir.txt', 'r')
797 playbookexdir = f.readline()
800 cherrypy.log("Id: " + id)
801 cherrypy.log("RunDir: " + rdir)
802 if os.path.exists( rdir + "/output.log"):
803 cherrypy.log("Found output.log file")
804 f = open( rdir + '/output.log', 'r')
805 output_log = f.readline()
809 cherrypy.log("Globglob failed:")
815 if TestRecord[input_data['Id']]['Log'] == '' and \
818 cherrypy.log( "*** Request blocked " + input_data['Id'])
820 while TestRecord[input_data['Id']]['Log'] == '' \
821 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
824 cherrypy.log( "*** Request released " + input_data['Id'])
826 cherrypy.log( "Log:" + str(TestRecord[input_data['Id']]['Log']))
827 return {"Log": TestRecord[input_data['Id']]['Log']}
829 # Not in memory check for a file
830 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
831 id = input_data['Id']
832 cherrypy.log("Old directory found for ID: " + id)
833 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
836 if os.path.exists (rdir + "/PlaybookExDir.txt"):
837 cherrypy.log("Found PlaybookExDir.txt file")
838 f = open( rdir + '/PlaybookExDir.txt', 'r')
839 playbookexdir = f.readline()
842 cherrypy.log("Id: " + id)
843 cherrypy.log("RunDir: " + rdir)
844 if 'GetLog' in input_data['Type']:
845 if os.path.exists( rdir + "/output.log"):
846 cherrypy.log("Found output.log file")
847 f = open( rdir + '/output.log', 'r')
848 output_log = f.readline()
851 elif 'GetOutputLog' in input_data['Type']:
852 if os.path.exists( rdir + "/output.log"):
853 cherrypy.log("Found output.log file")
854 f = open( rdir + '/output.log', 'r')
855 output_log = f.readline()
858 elif 'GetResult' in input_data['Type']:
859 if os.path.exists (rdir + "/PlaybookName.txt"):
860 cherrypy.log("Found PlaybookName.txt file")
861 f = open( rdir + '/PlaybookName.txt', 'r')
862 playbooknametxt = f.readline()
865 playbooknametxt = "NA"
867 # Add code to get other items not just output.log from files
868 if os.path.exists( rdir + "/log.file"):
869 cherrypy.log("Found log.file")
871 f = open( rdir + '/log.file', 'r')
876 out_results = out_results + line
877 elif "RECAP" in line:
878 out_results = out_results + line
879 recap_line = f.readline()
881 out_results = out_results + recap_line
882 recap_line = f.readline()
885 out_obj = {"StatusCode": 200,
886 "StatusMessage": "FINISHED",
887 "PlaybookName": playbooknametxt,
888 "Version": "Version",
890 "Results": out_results}
893 return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
896 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
898 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
899 elif 'DELETE' in cherrypy.request.method:
900 input_data = parse_query_string(cherrypy.request.query_string)
902 cherrypy.log( "***> in RestServer.DELETE")
903 cherrypy.log("Payload: " + str(input_data))
905 if input_data['Id'] in TestRecord:
906 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
907 cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
908 TestRecord.pop (input_data['Id'])
909 if input_data['Id'] in ActiveProcess:
910 ActiveProcess.pop (input_data['Id'])
912 return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
914 return {"StatusCode": 200, "StatusMessage": "PENDING"}
916 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
919 if __name__ == '__main__':
923 config_file_path = "RestServer_config"
925 if not os.path.exists(config_file_path):
926 print '[INFO] The config file does not exist'
936 timeout_seconds = 'na'
941 getresults_block = False
944 file = open(config_file_path, 'r')
945 for line in file.readlines():
948 ip = line.split(':')[1].strip()
949 elif 'AnsibleServer:' in line:
950 AnsibleServer = line.split(':')[1].strip()
951 elif 'port:' in line:
952 port = line.split(':')[1].strip()
953 elif 'ksalt:' in line:
954 salt = line.split(':')[1].strip()
956 tls = 'YES' in line.split(':')[1].strip().upper()
957 elif 'auth:' in line:
958 auth = 'YES' in line.split(':')[1].strip().upper()
959 if tls and 'priv:' in line:
960 priv = line.split(':')[1].strip()
961 if tls and 'pub:' in line:
962 pub = line.split(':')[1].strip()
963 if tls and 'inter_cert:' in line:
964 intermediate = line.split(':')[1].strip()
965 if 'timeout_seconds' in line:
966 timeout_seconds = int (line.split(':')[1].strip())
967 if 'ansible_path' in line:
968 ansible_path = line.split(':')[1].strip()
969 if 'ansible_temp' in line:
970 ansible_temp = line.split(':')[1].strip()
972 host = line.split(':')[1].strip()
974 users = line.split(':')[1].strip()
975 if 'getresults_block' in line:
976 getresults_block = 'YES' in line.split(':')[1].strip().upper()
977 if 'from_files' in line:
978 from_files = 'YES' in line.split(':')[1].strip().upper()
986 'response.timeout': 5400,
987 'server.socket_host': ip,
988 'server.socket_port': int(port),
989 'server.protocol_version': 'HTTP/1.1'
994 # Use pythons built-in SSL
995 cherrypy.server.ssl_module = 'builtin'
997 # Point to certificate files
999 if not os.path.exists(pub):
1000 print '[INFO] The public certificate does not exist'
1003 if not os.path.exists(priv):
1004 print '[INFO] The private key does not exist'
1007 if not os.path.exists(intermediate):
1008 print '[INFO] The intermediate certificate does not exist'
1012 cherrypy.server.ssl_certificate = pub
1013 cherrypy.server.ssl_certificate_chain = intermediate
1014 cherrypy.server.ssl_private_key = priv
1017 # Read in and build user dictionary
1018 if not os.path.exists(users):
1019 print '[INFO] The users file does not exist: ' + users
1022 user_file = open(users, 'r')
1023 for line in user_file.readlines():
1025 id = line.split(':')[0].strip()
1026 pw = line.split(':')[1].strip()
1027 userpassdict[id] = pw
1028 #print str(userpassdict)
1031 {'tools.auth_basic.on': True,
1032 'tools.auth_basic.realm': 'earth',
1033 'tools.auth_basic.checkpassword': validate_password,
1037 application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1039 application = cherrypy.tree.mount(TestManager(), '/')
1041 cherrypy.config.update({
1042 'log.access_file': "/var/log/RestServer.access"
1044 accessLogName = "/var/log/RestServer.access"
1045 applicationLogName = "/var/log/RestServer.log"
1046 cherrypy.config.update(global_conf)
1048 log = application.log
1050 log.access_file = ""
1051 from logging import handlers
1052 applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1053 accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1055 applicationLogFileHandler.setLevel(logging.DEBUG)
1056 log.error_log.addHandler(applicationLogFileHandler)
1057 log.access_log.addHandler(accessLogFileHandler)
1061 cherrypy.engine.start()
1062 cherrypy.engine.block()