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 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 * ============LICENSE_END=========================================================
27 import time, datetime, json, os, sys, subprocess, re
37 from cherrypy.lib.httputil import parse_query_string
38 from cherrypy.lib import auth_basic
40 from multiprocessing import Process, Manager
42 from AnsibleModule import ansibleSysCall
43 from BuildHostFile import buildHostsSysCall
45 from os import listdir
46 from os.path import isfile, join
48 TestRecord = Manager().dict()
51 def validate_password(realm, username, password):
52 comp = crypt.crypt(password, salt)
53 if username in userpassdict and userpassdict[username] == comp:
58 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
59 output = p.stdout.readlines()
62 for i in range(len(output)):
63 output[i] = output[i].strip()
66 def callback (Id, Result, Output, Log, returncode):
68 print "***> in RestServer.callback"
71 time_now = datetime.datetime.utcnow()
72 delta_time = (time_now - TestRecord[Id]['Time']).total_seconds()
73 Result['PlaybookName'] = TestRecord[Id]['PlaybookName']
74 Result['Version'] = TestRecord[Id]['Version']
76 Result['StatusCode'] = 500
77 Result['StatusMessage'] = "TERMINATED"
79 Result['StatusCode'] = 200
80 Result['StatusMessage'] = "FINISHED"
82 # Need to update the whole data structure for key=Id otherwise Manager is not updated
83 TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'],
84 'Version': TestRecord[Id]['Version'],
85 'NodeList': TestRecord[Id]['NodeList'],
86 'HostGroupList': TestRecord[Id]['HostGroupList'],
87 'HostNameList': TestRecord[Id]['HostNameList'],
88 'Time': TestRecord[Id]['Time'],
89 'Timeout': TestRecord[Id]['Timeout'],
90 'Duration': str(delta_time),
91 'EnvParameters': TestRecord[Id]['EnvParameters'],
92 'LocalParameters': TestRecord[Id]['LocalParameters'],
93 'FileParameters': TestRecord[Id]['FileParameters'],
94 'CallBack': TestRecord[Id]['CallBack'],
98 'Path': TestRecord[Id]['Path'],
99 'Mandatory': TestRecord[Id]['Path']}
101 if not TestRecord[Id]['CallBack'] == None:
103 # Posting results to callback server
105 data = {"StatusCode": 200,
106 "StatusMessage": "FINISHED",
107 "PlaybookName": TestRecord[Id]["PlaybookName"],
108 "Version": TestRecord[Id]["Version"],
109 "Duration": TestRecord[Id]["Duration"],
110 "Results": TestRecord[Id]['Result']['Results']}
112 cherrypy.log("CALLBACK: TestRecord[Id]['Output']['Output']:", str(TestRecord[Id]['Output']['Output']))
113 cherrypy.log("CALLBACK: Results:", str(data["Results"]))
115 if not TestRecord[Id]['Output']['Output'] == {}:
116 for key in data["Results"]:
117 if key in TestRecord[Id]['Output']['Output']:
118 data["Results"][key]["Output"] = TestRecord[Id]['Output']['Output'][key]
120 print " Posting to", TestRecord[Id]['CallBack']
122 s = requests.Session()
123 r = s.post(TestRecord[Id]['CallBack'], data = json.dumps(data),
124 headers = {'content-type': 'application/json'})
125 print " Response", r.status_code, r.text
127 def RunAnsible_Playbook (callback, Id, Inventory, Playbook, NodeList, TestRecord,
130 print "***> in RestServer.RunAnsible_Playbook"
132 # Run test in playbook for given target
135 retval, log, returncode = ansibleSysCall (Inventory, Playbook, NodeList,
136 TestRecord[Id]['Mandatory'],
137 TestRecord[Id]['EnvParameters'],
138 TestRecord[Id]['LocalParameters'],
139 TestRecord[Id]['Timeout'],
143 cherrypy.log("Return code:" + str(returncode))
144 cherrypy.log("Return val:" + str(retval))
147 #Output = {'Output': {}}
150 onlyfiles = [f for f in listdir(Path)
151 if isfile(join(Path, f))]
153 cherrypy.log("Checking for results.txt files: ")
154 for file in onlyfiles:
155 if "results.txt" in file:
156 # if file.endswith("results.txt"):
157 cherrypy.log("results file: " + file)
158 f = open(Path + "/" + file, "r")
159 resultsData = f.read() # Not to pass vnf instance name
160 OutputP = json.loads(resultsData)
161 Output['Output'] = OutputP
162 cherrypy.log("Output = " + str(Output['Output']))
163 #Output['Output'][key] = f.read() # To pass vnf instance name
167 Output = {'Output': {}}
169 Result = {'Results': {}}
170 if 'could not be found' in Log:
171 Result['Results'] = {"StatusCode": 101,
172 "StatusMessage": "PLAYBOOK NOT FOUND"}
173 if returncode == 137:
174 Result['Results'] = {"StatusCode": 500,
175 "StatusMessage": "TERMINATED"}
177 elif TestRecord[Id]['NodeList'] == []:
181 if 'TargetNode' in TestRecord[Id]['EnvParameters']:
182 targetlist = TestRecord[Id]['EnvParameters']['TargetNode'].split(' ')
184 targetlist = ["localhost"]
187 for i in range (len(targetlist)):
188 if key in targetlist[i]:
191 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
192 int(retval[key][3]) == 0:
195 Result['Results'][targetlist[host_index]] = \
196 {"GroupName": 'na', "StatusCode": 200, \
197 "StatusMessage": "SUCCESS"}
199 Result['Results'][key] = \
200 {"GroupName": 'na', "StatusCode": 200, \
201 "StatusMessage": "SUCCESS"}
202 elif int(retval[key][2]) > 0:
204 Result['Results'][targetlist[host_index]] = \
205 {"GroupName": 'na', "StatusCode": 400, \
206 "StatusMessage": "NOT REACHABLE"}
208 Result['Results'][key] = \
209 {"GroupName": 'na', "StatusCode": 400, \
210 "StatusMessage": "NOT REACHABLE"}
211 elif int(retval[key][3]) > 0:
213 Result['Results'][targetlist[host_index]] = \
214 {"GroupName": 'na', "StatusCode": 400, \
215 "StatusMessage": "FAILURE"}
217 Result['Results'][key] = \
218 {"GroupName": 'na', "StatusCode": 400, \
219 "StatusMessage": "FAILURE"}
224 if len(TestRecord[Id]['HostNameList']) > 0:
227 for i in range (len(TestRecord[Id]['HostNameList'])):
228 if key in TestRecord[Id]['HostNameList'][i]:
231 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
232 int(retval[key][3]) == 0:
234 if len(host_index) > 0:
235 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
236 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
237 "StatusCode": 200, "StatusMessage": "SUCCESS"}
239 for i in range (1, len(host_index)):
240 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
241 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
243 Result['Results'][key] = \
245 "StatusCode": 200, "StatusMessage": "SUCCESS"}
247 elif int(retval[key][2]) > 0:
249 if len(host_index) > 0:
250 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
251 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
252 "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
254 for i in range (1, len(host_index)):
255 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
256 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
258 Result['Results'][key] = \
260 "StatusCode": 200, "StatusMessage": "NOT REACHABLE"}
262 elif int(retval[key][3]) > 0:
264 if len(host_index) > 0:
265 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
266 {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
267 "StatusCode": 400, "StatusMessage": "FAILURE"}
269 for i in range (1, len(host_index)):
270 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
271 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
273 Result['Results'][key] = \
275 "StatusCode": 200, "StatusMessage": "FAILURE"}
278 for i in range (len(TestRecord[Id]['NodeList'])):
279 if key in TestRecord[Id]['NodeList'][i]:
282 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
283 int(retval[key][3]) == 0:
284 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
285 {"GroupName": 'na', "StatusCode": 200, \
286 "StatusMessage": "SUCCESS"}
287 elif int(retval[key][2]) > 0:
288 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
289 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
290 elif int(retval[key][3]) > 0:
291 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
292 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
294 cherrypy.log("TESTRECORD: " + str(TestRecord[Id]))
295 #cherrypy.log("Output: " + str(Output))
296 callback (Id, Result, Output, Log, returncode)
298 class TestManager (object):
301 @cherrypy.tools.json_out()
302 @cherrypy.tools.json_in()
303 @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
305 def Dispatch(self, **kwargs):
307 # Let cherrypy error handler deal with malformed requests
308 # No need for explicit error handler, we use default ones
310 time_now = datetime.datetime.utcnow()
312 # Erase old test results (2x timeout)
313 # Do cleanup too of ActiveProcess list and old Records - PAP
315 for key in TestRecord.copy():
316 cherrypy.log( "LOOKING AT ALL TestRecords: " + str(key))
317 if key in ActiveProcess:
318 if not ActiveProcess[key].is_alive(): # Just to cleanup defunct processes
319 cherrypy.log( "Not ActiveProcess for ID: " + str(key))
320 delta_time = (time_now - TestRecord[key]['Time']).seconds
321 if delta_time > 2*TestRecord[key]['Timeout']:
322 cherrypy.log( "DELETED HISTORY for ID: " + str(key))
323 if key in ActiveProcess:
324 if not ActiveProcess[key].is_alive():
325 ActiveProcess.pop (key)
326 cherrypy.log( "DELETED ActiveProcess for ID: " + str(key))
327 #if os.path.exists(TestRecord[key]['Path']):
328 # don't remove run dirrectory
329 #shutil.rmtree (TestRecord[key]['Path'])
332 cherrypy.log("RestServer.Dispatch: " + cherrypy.request.method)
335 if 'POST' in cherrypy.request.method:
337 input_json = cherrypy.request.json
338 cherrypy.log("Payload: " + str(input_json))
340 if 'Id' in input_json and 'PlaybookName' in input_json and 'EnvParameters' in input_json:
344 if not input_json['Id'] in TestRecord:
345 # check if Id exists in previous run dirctory
347 s_cmd = 'ls ' + ansible_temp + '/*_' + input_json['Id']
348 #if subprocess.check_output([s_cmd, ]):
349 Id = input_json['Id']
350 if glob.glob( ansible_temp + '/*_' + input_json['Id']):
351 cherrypy.log("Old directory found for ID: " + Id)
352 return {"StatusCode": 101, "StatusMessage": "TEST ID FILE ALREADY DEFINED"}
354 PlaybookName = input_json['PlaybookName']
355 # if required it should be passed as an argument
356 EnvParameters = input_json['EnvParameters']
358 # The lines below are to test multiple EnvParameters being passed
359 #for i in EnvParameters:
360 # cherrypy.log("EnvParameter object: " + i)
361 # cherrypy.log(" EnvParameter Value: " + EnvParameters[ i ])
363 # Now get things out of EnvParameters
365 VNF_instance = EnvParameters.get('vnf_instance')
367 # Get Version if present
369 if 'Version' in input_json:
370 version = input_json['Version']
374 HaveInventoryNames = False
375 inventory_names = None
376 if 'InventoryNames' in input_json:
377 inventory_names = input_json['InventoryNames']
378 HaveInventoryNames = True
380 #AnsibleInvFail = True
381 AnsiblePlaybookFail = True
385 str_uuid = str (uuid.uuid4())
388 VnfType= PlaybookName.split("/")[0]
390 cherrypy.log( "Request USER : " + cherrypy.request.login)
391 cherrypy.log( "Request Decode: ID " + Id)
392 cherrypy.log( "Request Decode: VnfType " + VnfType)
393 cherrypy.log( "Request Decode: EnvParameters " + json.dumps(EnvParameters))
395 # Verify VNF_instance was passed in EnvParameters
396 if VNF_instance != None:
397 cherrypy.log( "Request Decode: VnfInstance " + VNF_instance)
399 cherrypy.log( "StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED" )
400 return {"StatusCode": 107,
401 "StatusMessage": "VNF_instance NOT PROVIDED"}
403 if inventory_names != None:
404 cherrypy.log( "Request Decode: Inventory Names " + inventory_names)
406 cherrypy.log( "Request Decode: Inventory Names " + "Not provided")
408 cherrypy.log( "Request Decode: PlaybookName " + PlaybookName)
409 PlayBookFunction = PlaybookName.rsplit("/",2)[1]
410 PlayBookFile = PlayBookFunction + "/site.yml"
411 cherrypy.log( "Request Decode: PlaybookFunction " + PlayBookFunction)
412 cherrypy.log( "Request Decode: Playbook file " + PlayBookFile)
414 BaseDir = ansible_path + "/" + PlaybookName.rsplit("/",1)[0]
415 CopyDir = ansible_path + "/" + PlaybookName.rsplit("/",2)[0]
416 cherrypy.log( "Request Decode: Basedir " + BaseDir)
417 cherrypy.log( "Request Decode: Copydir " + CopyDir)
420 PlaybookDir = ansible_temp + "/" + \
421 VNF_instance + "_" + str_uuid + "_" + str(Id)
423 # AnsibleInv is the directory where the host file to be run exsists
424 AnsibleInv = ansible_path + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance
427 # Create base run directory if it doesn't exist
428 if not os.path.exists(ansible_temp):
429 cherrypy.log( "Creating Base Run Directory: " + ansible_temp)
430 os.makedirs(ansible_temp)
432 if not os.path.exists( CopyDir ):
433 cherrypy.log("Playbook Not Found")
434 return {"StatusCode": 101,
435 "StatusMessage": "PLAYBOOK NOT FOUND"}
437 # copy static playbook dir to run dir
438 cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir)
439 shutil.copytree(CopyDir, PlaybookDir)
440 cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;"
441 cmd="/usr/bin/find " + PlaybookDir + " -exec chmod +rx {} \;"
445 cherrypy.log( "PlaybookDir: " + PlaybookDir)
446 cherrypy.log( "AnsibleInv: " + AnsibleInv)
448 # Process inventory file for target
454 if 'NodeList' in input_json:
455 NodeList = input_json['NodeList']
457 cherrypy.log("NodeList: " + str(NodeList));
461 cherrypy.log( "*** NodeList - Empty ***")
466 ###############################################################################
467 ##### Host file processing ###########################
468 ##### 1. Use file delivered with playbook ###########################
469 ##### 2. If HostNames + NodeList generate and use ###########################
470 ###############################################################################
472 #Verify inventory directory exists
473 path = PlaybookDir + "/inventory/"
474 if not os.path.isdir(path):
475 cherrypy.log ("Inventory directory %s does not exist - create it" % path)
479 cherrypy.log ("Creation of the directory %s failed" % path)
481 cherrypy.log ("Successfully created the directory %s " % path)
483 #location of host file - Default
484 HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
485 cherrypy.log("HostFile: " + HostFile)
487 # if NodeList and InventoryNames need to build host file
488 if HaveInventoryNames & HaveNodeList:
489 cherrypy.log("Build host file from NodeList")
490 ret = buildHostsSysCall (input_json, PlaybookDir, inventory_names)
492 cherrypy.log("Returning Error: Not running Playbook")
493 return {"StatusCode": 105,
494 "StatusMessage": "NodeList: Missing vnfc-type field"}
496 # Having been built now copy new file to correct file
497 shutil.copy(PlaybookDir + "/host_file.txt", HostFile)
498 cherrypy.log("Copying Generated host file to: " + HostFile)
500 timeout = timeout_seconds
501 if 'Timeout' in input_json:
502 timeout = int (input_json['Timeout'])
503 cherrypy.log("Timeout from API: " + str(timeout))
506 cherrypy.log("Timeout not passed from API using default: " + str(timeout))
509 if 'EnvParameters' in input_json:
510 EnvParam = input_json['EnvParameters']
513 if 'LocalParameters' in input_json:
514 LocalParam = input_json['LocalParameters']
517 if 'FileParameters' in input_json:
518 FileParam = input_json['FileParameters']
521 if 'CallBack' in input_json:
522 callback_flag = input_json['CallBack']
524 # if AnsibleServer is not set to 'na' don't send AnsibleServer in PENDING responce.
525 if AnsibleServer != 'na':
526 TestRecord[Id] = {'PlaybookName': PlaybookName,
528 'NodeList': NodeList,
529 'HostGroupList': hostgrouplist,
530 'HostNameList': hostnamelist,
534 'EnvParameters': EnvParam,
535 'LocalParameters': LocalParam,
536 'FileParameters': FileParam,
537 'CallBack': callback_flag,
538 'Result': {"StatusCode": 100,
539 "StatusMessage": 'PENDING',
540 "AnsibleServer": str(AnsibleServer),
541 "ExpectedDuration": str(timeout) + "sec"},
547 TestRecord[Id] = {'PlaybookName': PlaybookName,
549 'NodeList': NodeList,
550 'HostGroupList': hostgrouplist,
551 'HostNameList': hostnamelist,
555 'EnvParameters': EnvParam,
556 'LocalParameters': LocalParam,
557 'FileParameters': FileParam,
558 'CallBack': callback_flag,
559 'Result': {"StatusCode": 100,
560 "StatusMessage": 'PENDING',
561 "ExpectedDuration": str(timeout) + "sec"},
567 cherrypy.log("Test_Record: " + str(TestRecord[Id]))
570 if not TestRecord[Id]['FileParameters'] == {}:
571 for key in TestRecord[Id]['FileParameters']:
573 filecontent = TestRecord[Id]['FileParameters'][key]
574 f = open(PlaybookDir + "/" + filename, "w")
580 if os.path.exists( ansible_path + '/' + PlaybookName):
581 AnsiblePlaybookFail = False
583 if AnsiblePlaybookFail:
584 #if os.path.exists(PlaybookDir):
585 #shutil.rmtree (PlaybookDir)
587 return {"StatusCode": 101,
588 "StatusMessage": "PLAYBOOK NOT FOUND"}
592 playbook_path = PlaybookDir
595 if not os.path.exists(playbook_path + "/vars"):
596 os.mkdir(playbook_path + "/vars")
597 if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
598 os.mknod(playbook_path + "/vars/defaults.yml")
600 ###################################################
602 #write local parameters passed into defaults.yml
604 f = open(playbook_path + "/vars/defaults.yml","a")
605 #for id, record in TestRecord.items():
606 print TestRecord[Id]['LocalParameters']
607 local_parms = TestRecord[Id]['LocalParameters']
608 for key, value in local_parms.items():
609 f.write(key +"=" + value + "\n");
611 ###################################################
613 for key in TestRecord[Id]['LocalParameters']:
615 for i in range(len(TestRecord[Id]['HostNameList'])):
616 if key in TestRecord[Id]['HostNameList'][i]:
618 if len(host_index) == 0:
619 for i in range(len(TestRecord[Id]['HostGroupList'])):
620 if key in TestRecord[Id]['HostGroupList'][i]:
622 if len(host_index) > 0:
623 for i in range(len(host_index)):
624 f = open(playbook_path + "/vars/" +
625 TestRecord[Id]['HostNameList'][host_index[i]] +
627 for param in TestRecord[Id]['LocalParameters'][key]:
628 f.write(param + ": " +
629 str (TestRecord[Id]['LocalParameters'][key][param]) +
634 # write some info out to files before running
636 f = open(playbook_path + "/User.txt", "a")
637 f.write(cherrypy.request.login)
639 f = open(playbook_path + "/PlaybookName.txt", "a")
640 f.write(PlaybookName)
642 f = open(playbook_path + "/PlaybookExDir.txt", "a")
643 f.write(PlaybookDir + "/" + PlayBookFunction)
645 f = open(playbook_path + "/JsonRequest.txt", "w")
646 #f.write(str(input_json))
647 #print( json.dumps(input_json, indent=4, sort_keys=True))
648 f.write( json.dumps(input_json, indent=4, sort_keys=True))
652 # Check that HostFile exists
653 if not os.path.isfile(HostFile):
654 cherrypy.log("Inventory file Not Found: " + HostFile)
655 return {"StatusCode": 101,
656 "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"}
658 # Cannot use thread because ansible module uses
659 # signals which are only supported in main thread.
660 # So use multiprocess with shared object
661 # args = (callback, Id, PlaybookDir + "/" + AnsibleInv,
663 p = Process(target = RunAnsible_Playbook,
664 args = (callback, Id, HostFile,
665 PlaybookDir + '/' + PlayBookFile,
666 NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction,
669 ActiveProcess[Id] = p
670 return TestRecord[Id]['Result']
672 cherrypy.log("TEST ID ALREADY DEFINED")
673 return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
676 return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
679 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"}
681 elif 'GET' in cherrypy.request.method:
683 # Lets pause for a second just incase the resquest was just kicked off
686 input_data = parse_query_string(cherrypy.request.query_string)
688 # Verify we have a Type passed in GET request
689 if not ( 'Type' in input_data):
690 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
693 cherrypy.log( "Request USER: " + cherrypy.request.login)
694 cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type'])
696 if 'LogRest' in input_data['Type']:
698 sys.stdout = open("/var/log/RestServer.log", "w")
700 # Just a debug to dump any records
701 if 'GetStatus' in input_data['Type']:
702 cherrypy.log( "******** Dump Records **********")
703 if TestRecord.items():
704 for id, record in TestRecord.items():
705 cherrypy.log( " Id: " + id)
706 cherrypy.log( "Record: " + str(record))
708 cherrypy.log(" No Records to dump")
710 if 'Id' in input_data and 'Type' in input_data:
711 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']):
712 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
713 if input_data['Id'] in TestRecord:
715 if 'GetResult' in input_data['Type']:
717 cherrypy.log( " ** GetResult for: " + str (input_data['Id']))
719 if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
722 #check if playbook is still running
723 while ActiveProcess[input_data['Id']].is_alive():
724 cherrypy.log( "*** Playbook running returning PENDING for " + str(input_data['Id']))
726 ## If still running return PENDING response
728 if AnsibleServer != 'na':
729 return {"StatusCode": 100,
730 "StatusMessage": 'PENDING',
731 "AnsibleServer": str(AnsibleServer)}
733 return {"StatusCode": 100,
734 "StatusMessage": 'PENDING'}
737 #cherrypy.log( "*** Request released " + input_data['Id'])
739 cherrypy.log(str( TestRecord[input_data['Id']]['Result']))
740 cherrypy.log("Output: " + str( TestRecord[input_data['Id']]['Output']))
741 cherrypy.log("StatusCode: " + str( TestRecord[input_data['Id']]['Result']['StatusCode']))
742 cherrypy.log("StatusMessage: " + str( TestRecord[input_data['Id']]['Result']['StatusMessage']))
744 #out_obj gets returned to GET request
745 if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
746 out_obj = TestRecord[input_data['Id']]['Result']['Results']
748 out_obj = {"StatusCode": 200,
749 "StatusMessage": "FINISHED",
750 "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
751 "Version": TestRecord[input_data['Id']]["Version"],
752 "Duration": TestRecord[input_data['Id']]["Duration"],
753 "Output": TestRecord[input_data['Id']]["Output"]["Output"],
754 "Results": TestRecord[input_data['Id']]['Result']['Results']}
755 if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
756 cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output']))
758 for key in out_obj["Results"]:
759 cherrypy.log("Output key: " + str(key))
760 if key in TestRecord[input_data['Id']]['Output']['Output']:
761 out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
763 cherrypy.log("***** GET RETURNING RESULTS Back ****")
764 cherrypy.log(str(out_obj))
767 elif 'GetStatus' in input_data['Type']:
768 print " Dump Records"
769 for id, record in TestRecord,items():
771 print " Record:" + str(reecord)
773 elif 'GetTheOutput' in input_data['Type']:
775 if TestRecord[input_data['Id']]['Output'] == {} and \
778 cherrypy.log( "*** Request blocked " + input_data['Id'])
780 while TestRecord[input_data['Id']]['Output'] == {} \
781 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
784 cherrypy.log( "*** Request released " + input_data['Id'])
786 cherrypy.log( "Output: " + str(TestRecord[input_data['Id']]['Output']))
787 return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
789 elif 'GetOutputLog' in input_data['Type']:
790 cherrypy.log("GetOutputLog: processing.")
791 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
792 id = input_data['Id']
793 cherrypy.log("Old directory found for ID: " + id)
794 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
797 if os.path.exists (rdir + "/PlaybookExDir.txt"):
798 cherrypy.log("Found PlaybookExDir.txt file")
799 f = open( rdir + '/PlaybookExDir.txt', 'r')
800 playbookexdir = f.readline()
803 cherrypy.log("Id: " + id)
804 cherrypy.log("RunDir: " + rdir)
805 if os.path.exists( rdir + "/output.log"):
806 cherrypy.log("Found output.log file")
807 f = open( rdir + '/output.log', 'r')
808 output_log = f.readline()
812 cherrypy.log("Globglob failed:")
818 if TestRecord[input_data['Id']]['Log'] == '' and \
821 cherrypy.log( "*** Request blocked " + input_data['Id'])
823 while TestRecord[input_data['Id']]['Log'] == '' \
824 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
827 cherrypy.log( "*** Request released " + input_data['Id'])
829 cherrypy.log( "Log:" + str(TestRecord[input_data['Id']]['Log']))
830 return {"Log": TestRecord[input_data['Id']]['Log']}
832 # Not in memory check for a file
833 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
834 id = input_data['Id']
835 cherrypy.log("Old directory found for ID: " + id)
836 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
839 if os.path.exists (rdir + "/PlaybookExDir.txt"):
840 cherrypy.log("Found PlaybookExDir.txt file")
841 f = open( rdir + '/PlaybookExDir.txt', 'r')
842 playbookexdir = f.readline()
845 cherrypy.log("Id: " + id)
846 cherrypy.log("RunDir: " + rdir)
847 if 'GetLog' in input_data['Type']:
848 if os.path.exists( rdir + "/output.log"):
849 cherrypy.log("Found output.log file")
850 f = open( rdir + '/output.log', 'r')
851 output_log = f.readline()
854 elif 'GetOutputLog' in input_data['Type']:
855 if os.path.exists( rdir + "/output.log"):
856 cherrypy.log("Found output.log file")
857 f = open( rdir + '/output.log', 'r')
858 output_log = f.readline()
861 elif 'GetResult' in input_data['Type']:
862 if os.path.exists (rdir + "/PlaybookName.txt"):
863 cherrypy.log("Found PlaybookName.txt file")
864 f = open( rdir + '/PlaybookName.txt', 'r')
865 playbooknametxt = f.readline()
868 playbooknametxt = "NA"
870 # Add code to get other items not just output.log from files
871 if os.path.exists( rdir + "/log.file"):
872 cherrypy.log("Found log.file")
874 f = open( rdir + '/log.file', 'r')
879 out_results = out_results + line
880 elif "RECAP" in line:
881 out_results = out_results + line
882 recap_line = f.readline()
884 out_results = out_results + recap_line
885 recap_line = f.readline()
888 out_obj = {"StatusCode": 200,
889 "StatusMessage": "FINISHED",
890 "PlaybookName": playbooknametxt,
891 "Version": "Version",
893 "Results": out_results}
896 return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
899 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
901 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
902 elif 'DELETE' in cherrypy.request.method:
903 input_data = parse_query_string(cherrypy.request.query_string)
905 cherrypy.log( "***> in RestServer.DELETE")
906 cherrypy.log("Payload: " + str(input_data))
908 if input_data['Id'] in TestRecord:
909 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
910 cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
911 TestRecord.pop (input_data['Id'])
912 if input_data['Id'] in ActiveProcess:
913 ActiveProcess.pop (input_data['Id'])
915 return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
917 return {"StatusCode": 200, "StatusMessage": "PENDING"}
919 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
922 if __name__ == '__main__':
926 config_file_path = "RestServer_config"
928 if not os.path.exists(config_file_path):
929 print '[INFO] The config file does not exist'
939 timeout_seconds = 'na'
944 getresults_block = False
947 file = open(config_file_path, 'r')
948 for line in file.readlines():
951 ip = line.split(':')[1].strip()
952 elif 'AnsibleServer:' in line:
953 AnsibleServer = line.split(':')[1].strip()
954 elif 'port:' in line:
955 port = line.split(':')[1].strip()
956 elif 'ksalt:' in line:
957 salt = line.split(':')[1].strip()
959 tls = 'YES' in line.split(':')[1].strip().upper()
960 elif 'auth:' in line:
961 auth = 'YES' in line.split(':')[1].strip().upper()
962 if tls and 'priv:' in line:
963 priv = line.split(':')[1].strip()
964 if tls and 'pub:' in line:
965 pub = line.split(':')[1].strip()
966 if tls and 'inter_cert:' in line:
967 intermediate = line.split(':')[1].strip()
968 if 'timeout_seconds' in line:
969 timeout_seconds = int (line.split(':')[1].strip())
970 if 'ansible_path' in line:
971 ansible_path = line.split(':')[1].strip()
972 if 'ansible_temp' in line:
973 ansible_temp = line.split(':')[1].strip()
975 host = line.split(':')[1].strip()
977 users = line.split(':')[1].strip()
978 if 'getresults_block' in line:
979 getresults_block = 'YES' in line.split(':')[1].strip().upper()
980 if 'from_files' in line:
981 from_files = 'YES' in line.split(':')[1].strip().upper()
989 'response.timeout': 5400,
990 'server.socket_host': ip,
991 'server.socket_port': int(port),
992 'server.protocol_version': 'HTTP/1.1'
997 # Use pythons built-in SSL
998 cherrypy.server.ssl_module = 'builtin'
1000 # Point to certificate files
1002 if not os.path.exists(pub):
1003 print '[INFO] The public certificate does not exist'
1006 if not os.path.exists(priv):
1007 print '[INFO] The private key does not exist'
1010 if not os.path.exists(intermediate):
1011 print '[INFO] The intermediate certificate does not exist'
1015 cherrypy.server.ssl_certificate = pub
1016 cherrypy.server.ssl_certificate_chain = intermediate
1017 cherrypy.server.ssl_private_key = priv
1020 # Read in and build user dictionary
1021 if not os.path.exists(users):
1022 print '[INFO] The users file does not exist: ' + users
1025 user_file = open(users, 'r')
1026 for line in user_file.readlines():
1028 id = line.split(':')[0].strip()
1029 pw = line.split(':')[1].strip()
1030 userpassdict[id] = pw
1031 #print str(userpassdict)
1034 {'tools.auth_basic.on': True,
1035 'tools.auth_basic.realm': 'earth',
1036 'tools.auth_basic.checkpassword': validate_password,
1040 application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1042 application = cherrypy.tree.mount(TestManager(), '/')
1044 cherrypy.config.update({
1045 'log.access_file': "/var/log/RestServer.access"
1047 accessLogName = "/var/log/RestServer.access"
1048 applicationLogName = "/var/log/RestServer.log"
1049 cherrypy.config.update(global_conf)
1051 log = application.log
1053 log.access_file = ""
1054 from logging import handlers
1055 applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1056 accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1058 applicationLogFileHandler.setLevel(logging.DEBUG)
1059 log.error_log.addHandler(applicationLogFileHandler)
1060 log.access_log.addHandler(accessLogFileHandler)
1064 cherrypy.engine.start()
1065 cherrypy.engine.block()