3 * ============LICENSE_START=======================================================
5 * ================================================================================
6 * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
7 * ================================================================================
8 * Copyright (C) 2019 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 cherrypy.log("TESTRECORD: " + str(TestRecord[Id]))
294 cherrypy.log("Output: " + str(Output))
295 callback (Id, Result, Output, Log, returncode)
297 class TestManager (object):
300 @cherrypy.tools.json_out()
301 @cherrypy.tools.json_in()
302 @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
304 def Dispatch(self, **kwargs):
306 # Let cherrypy error handler deal with malformed requests
307 # No need for explicit error handler, we use default ones
309 time_now = datetime.datetime.utcnow()
311 # Erase old test results (2x timeout)
312 # Do cleanup too of ActiveProcess list and old Records - PAP
314 for key in TestRecord.copy():
315 cherrypy.log( "LOOKING AT ALL TestRecords: " + str(key))
316 if key in ActiveProcess:
317 if not ActiveProcess[key].is_alive(): # Just to cleanup defunct processes
318 cherrypy.log( "Not ActiveProcess for ID: " + str(key))
319 delta_time = (time_now - TestRecord[key]['Time']).seconds
320 if delta_time > 2*TestRecord[key]['Timeout']:
321 cherrypy.log( "DELETED HISTORY for ID: " + str(key))
322 if key in ActiveProcess:
323 if not ActiveProcess[key].is_alive():
324 ActiveProcess.pop (key)
325 cherrypy.log( "DELETED ActiveProcess for ID: " + str(key))
326 #if os.path.exists(TestRecord[key]['Path']):
327 # don't remove run dirrectory
328 #shutil.rmtree (TestRecord[key]['Path'])
331 cherrypy.log("RestServer.Dispatch: " + cherrypy.request.method)
334 if 'POST' in cherrypy.request.method:
336 input_json = cherrypy.request.json
337 cherrypy.log("Payload: " + str(input_json))
339 if 'Id' in input_json and 'PlaybookName' in input_json and 'EnvParameters' in input_json:
343 if not input_json['Id'] in TestRecord:
344 # check if Id exists in previous run dirctory
346 s_cmd = 'ls ' + ansible_temp + '/*_' + input_json['Id']
347 #if subprocess.check_output([s_cmd, ]):
348 Id = input_json['Id']
349 if glob.glob( ansible_temp + '/*_' + input_json['Id']):
350 cherrypy.log("Old directory found for ID: " + Id)
351 return {"StatusCode": 101, "StatusMessage": "TEST ID FILE ALREADY DEFINED"}
353 PlaybookName = input_json['PlaybookName']
354 # if required it should be passed as an argument
355 EnvParameters = input_json['EnvParameters']
357 # The lines below are to test multiple EnvParameters being passed
358 #for i in EnvParameters:
359 # cherrypy.log("EnvParameter object: " + i)
360 # cherrypy.log(" EnvParameter Value: " + EnvParameters[ i ])
362 # Now get things out of EnvParameters
364 VNF_instance = EnvParameters.get('vnf_instance')
366 # Get Version if present
368 if 'Version' in input_json:
369 version = input_json['Version']
373 HaveInventoryNames = False
374 inventory_names = None
375 if 'InventoryNames' in input_json:
376 inventory_names = input_json['InventoryNames']
377 HaveInventoryNames = True
379 #AnsibleInvFail = True
380 AnsiblePlaybookFail = True
384 str_uuid = str (uuid.uuid4())
387 VnfType= PlaybookName.split("/")[0]
388 cherrypy.log( "Request USER : " + cherrypy.request.login)
389 cherrypy.log( "Request Decode: ID " + Id)
390 cherrypy.log( "Request Decode: VnfType " + VnfType)
391 cherrypy.log( "Request Decode: EnvParameters " + json.dumps(EnvParameters))
393 # Verify VNF_instance was passed in EnvParameters
394 if VNF_instance != None:
395 cherrypy.log( "Request Decode: VnfInstance " + VNF_instance)
397 cherrypy.log( "StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED" )
398 return {"StatusCode": 107,
399 "StatusMessage": "VNF_instance NOT PROVIDED"}
401 if inventory_names != None:
402 cherrypy.log( "Request Decode: Inventory Names " + inventory_names)
404 cherrypy.log( "Request Decode: Inventory Names " + "Not provided")
406 cherrypy.log( "Request Decode: PlaybookName " + PlaybookName)
407 PlayBookFunction = PlaybookName.rsplit("/",2)[1]
408 PlayBookFile = PlayBookFunction + "/site.yml"
409 cherrypy.log( "Request Decode: PlaybookFunction " + PlayBookFunction)
410 cherrypy.log( "Request Decode: Playbook file " + PlayBookFile)
412 BaseDir = ansible_path + "/" + PlaybookName.rsplit("/",1)[0]
413 CopyDir = ansible_path + "/" + PlaybookName.rsplit("/",2)[0]
414 cherrypy.log( "Request Decode: Basedir " + BaseDir)
415 cherrypy.log( "Request Decode: Copydir " + CopyDir)
418 PlaybookDir = ansible_temp + "/" + \
419 VNF_instance + "_" + str_uuid + "_" + str(Id)
421 # AnsibleInv is the directory where the host file to be run exsists
422 AnsibleInv = ansible_path + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance
425 # Create base run directory if it doesn't exist
426 if not os.path.exists(ansible_temp):
427 cherrypy.log( "Creating Base Run Directory: " + ansible_temp)
428 os.makedirs(ansible_temp)
430 if not os.path.exists( CopyDir ):
431 cherrypy.log("Playbook Not Found")
432 return {"StatusCode": 101,
433 "StatusMessage": "PLAYBOOK NOT FOUND"}
435 # copy static playbook dir to run dir
436 cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir)
437 shutil.copytree(CopyDir, PlaybookDir)
438 cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;"
439 cmd="/usr/bin/find " + PlaybookDir + " -exec chmod +rx {} \;"
443 cherrypy.log( "PlaybookDir: " + PlaybookDir)
444 cherrypy.log( "AnsibleInv: " + AnsibleInv)
446 #location of host file
447 #HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
448 #cherrypy.log("HostFile: " + HostFile)
450 # Process inventory file for target
456 if 'NodeList' in input_json:
457 NodeList = input_json['NodeList']
459 cherrypy.log("NodeList: " + str(NodeList));
463 cherrypy.log( "*** NodeList - Empty ***")
464 #AnsibleInvFail = False
467 #AnsibleInvFail = False # ???
470 ###############################################################################
471 ##### Host file processing ###########################
472 ##### 1. Use file delivered with playbook ###########################
473 ##### 2. If HostNames + NodeList generate and use ###########################
474 ##### 3. If HostNames = VM or NVF copy and use. ###########################
475 ###############################################################################
477 #location of host file - Default
478 HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
479 cherrypy.log("HostFile: " + HostFile)
481 # if NodeList and InventoryNames need to build host file
482 if HaveInventoryNames & HaveNodeList:
483 cherrypy.log("Build host file from NodeList")
484 ret = buildHostsSysCall (input_json, PlaybookDir, inventory_names)
486 cherrypy.log("Returning Error: Not running Playbook")
487 return {"StatusCode": 105,
488 "StatusMessage": "NodeList: Missing vnfc-type field"}
490 # Having been built now copy new file to correct file
491 shutil.copy(PlaybookDir + "/host_file.txt", HostFile)
492 cherrypy.log("Copying Generated host file to: " + HostFile)
493 elif HaveInventoryNames & (not HaveNodeList):
494 ### Copy Instar based Hostfile
495 if inventory_names == "VNFC":
497 host_file_path = "/storage/inventory/VNFC/" + VNF_instance + "hosts"
498 if os.path.exists(host_file_path):
500 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
501 shutil.copy(host_file_path, HostFile)
503 cherrypy.log("Inventory file not found: " + host_file_path)
504 elif inventory_names == "None":
506 host_file_path = "/storage/inventory/None/" + VNF_instance + "hosts"
507 if os.path.exists(host_file_path):
509 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
510 shutil.copy(host_file_path, HostFile)
512 cherrypy.log("Inventory file not found: " + host_file_path)
513 elif inventory_names == "VM":
515 host_file_path = "/storage/inventory/VM/" + VNF_instance + "hosts"
516 if os.path.exists(host_file_path):
518 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
519 shutil.copy(host_file_path, HostFile)
521 cherrypy.log("Inventory file not found: " + host_file_path)
524 timeout = timeout_seconds
525 if 'Timeout' in input_json:
526 timeout = int (input_json['Timeout'])
527 cherrypy.log("Timeout from API: " + str(timeout))
530 cherrypy.log("Timeout not passed from API using default: " + str(timeout))
533 if 'EnvParameters' in input_json:
534 EnvParam = input_json['EnvParameters']
537 if 'LocalParameters' in input_json:
538 LocalParam = input_json['LocalParameters']
541 if 'FileParameters' in input_json:
542 FileParam = input_json['FileParameters']
545 if 'CallBack' in input_json:
546 callback_flag = input_json['CallBack']
548 # if AnsibleServer is not set to 'na' don't send AnsibleServer in PENDING responce.
549 if AnsibleServer != 'na':
550 TestRecord[Id] = {'PlaybookName': PlaybookName,
552 'NodeList': NodeList,
553 'HostGroupList': hostgrouplist,
554 'HostNameList': hostnamelist,
558 'EnvParameters': EnvParam,
559 'LocalParameters': LocalParam,
560 'FileParameters': FileParam,
561 'CallBack': callback_flag,
562 'Result': {"StatusCode": 100,
563 "StatusMessage": 'PENDING',
564 "AnsibleServer": str(AnsibleServer),
565 "ExpectedDuration": str(timeout) + "sec"},
571 TestRecord[Id] = {'PlaybookName': PlaybookName,
573 'NodeList': NodeList,
574 'HostGroupList': hostgrouplist,
575 'HostNameList': hostnamelist,
579 'EnvParameters': EnvParam,
580 'LocalParameters': LocalParam,
581 'FileParameters': FileParam,
582 'CallBack': callback_flag,
583 'Result': {"StatusCode": 100,
584 "StatusMessage": 'PENDING',
585 "ExpectedDuration": str(timeout) + "sec"},
591 cherrypy.log("Test_Record: " + str(TestRecord[Id]))
594 if not TestRecord[Id]['FileParameters'] == {}:
595 for key in TestRecord[Id]['FileParameters']:
597 filecontent = TestRecord[Id]['FileParameters'][key]
598 f = open(PlaybookDir + "/" + filename, "w")
604 if os.path.exists( ansible_path + '/' + PlaybookName):
605 AnsiblePlaybookFail = False
607 if AnsiblePlaybookFail:
608 #if os.path.exists(PlaybookDir):
609 #shutil.rmtree (PlaybookDir)
611 return {"StatusCode": 101,
612 "StatusMessage": "PLAYBOOK NOT FOUND"}
616 playbook_path = PlaybookDir
619 if not os.path.exists(playbook_path + "/vars"):
620 os.mkdir(playbook_path + "/vars")
621 if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
622 os.mknod(playbook_path + "/vars/defaults.yml")
624 ###################################################
626 #write local parameters passed into defaults.yml
628 f = open(playbook_path + "/vars/defaults.yml","a")
629 #for id, record in TestRecord.items():
630 print TestRecord[Id]['LocalParameters']
631 local_parms = TestRecord[Id]['LocalParameters']
632 for key, value in local_parms.items():
633 f.write(key +"=" + value + "\n");
635 ###################################################
637 for key in TestRecord[Id]['LocalParameters']:
639 for i in range(len(TestRecord[Id]['HostNameList'])):
640 if key in TestRecord[Id]['HostNameList'][i]:
642 if len(host_index) == 0:
643 for i in range(len(TestRecord[Id]['HostGroupList'])):
644 if key in TestRecord[Id]['HostGroupList'][i]:
646 if len(host_index) > 0:
647 for i in range(len(host_index)):
648 f = open(playbook_path + "/vars/" +
649 TestRecord[Id]['HostNameList'][host_index[i]] +
651 for param in TestRecord[Id]['LocalParameters'][key]:
652 f.write(param + ": " +
653 str (TestRecord[Id]['LocalParameters'][key][param]) +
658 # write some info out to files before running
659 f = open(playbook_path + "/PlaybookName.txt", "a")
660 f.write(PlaybookName)
662 f = open(playbook_path + "/PlaybookExDir.txt", "a")
663 f.write(PlaybookDir + "/" + PlayBookFunction)
665 f = open(playbook_path + "/JsonRequest.txt", "w")
666 #f.write(str(input_json))
667 print( json.dumps(input_json, indent=4, sort_keys=True))
668 f.write( json.dumps(input_json, indent=4, sort_keys=True))
672 # Check that HostFile exists
673 if not os.path.isfile(HostFile):
674 cherrypy.log("Inventory file Not Found: " + HostFile)
675 return {"StatusCode": 101,
676 "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"}
678 # Cannot use thread because ansible module uses
679 # signals which are only supported in main thread.
680 # So use multiprocess with shared object
681 # args = (callback, Id, PlaybookDir + "/" + AnsibleInv,
683 p = Process(target = RunAnsible_Playbook,
684 args = (callback, Id, HostFile,
685 PlaybookDir + '/' + PlayBookFile,
686 NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction,
689 ActiveProcess[Id] = p
690 return TestRecord[Id]['Result']
692 cherrypy.log("TEST ID ALREADY DEFINED")
693 return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
696 return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
699 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"}
701 elif 'GET' in cherrypy.request.method:
703 # Lets pause for a second just incase the resquest was just kicked off
706 input_data = parse_query_string(cherrypy.request.query_string)
708 # Verify we have a Type passed in GET request
709 if not ( 'Type' in input_data):
710 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
712 cherrypy.log( "Request USER: " + cherrypy.request.login)
713 cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type'])
715 if 'LogRest' in input_data['Type']:
717 sys.stdout = open("/var/log/RestServer.log", "w")
719 # Just a debug to dump any records
720 if 'GetStatus' in input_data['Type']:
721 cherrypy.log( "******** Dump Records **********")
722 if TestRecord.items():
723 for id, record in TestRecord.items():
724 cherrypy.log( " Id: " + id)
725 cherrypy.log( "Record: " + str(record))
727 cherrypy.log(" No Records to dump")
729 if 'Id' in input_data and 'Type' in input_data:
730 if not ('GetResult' in input_data['Type'] or 'GetOutputLog' in input_data['Type'] or'GetOutput' in input_data['Type'] or 'GetLog' in input_data['Type']):
731 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
732 if input_data['Id'] in TestRecord:
734 if 'GetResult' in input_data['Type']:
736 cherrypy.log( " ** GetResult for: " + str (input_data['Id']))
738 if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
741 #check if playbook is still running
742 while ActiveProcess[input_data['Id']].is_alive():
743 cherrypy.log( "*** Playbook running returning PENDING for " + str(input_data['Id']))
745 ## If still running return PENDING response
747 if AnsibleServer != 'na':
748 return {"StatusCode": 100,
749 "StatusMessage": 'PENDING',
750 "AnsibleServer": str(AnsibleServer)}
752 return {"StatusCode": 100,
753 "StatusMessage": 'PENDING'}
756 #cherrypy.log( "*** Request released " + input_data['Id'])
758 cherrypy.log(str( TestRecord[input_data['Id']]['Result']))
759 cherrypy.log("Output: " + str( TestRecord[input_data['Id']]['Output']))
760 cherrypy.log("StatusCode: " + str( TestRecord[input_data['Id']]['Result']['StatusCode']))
761 cherrypy.log("StatusMessage: " + str( TestRecord[input_data['Id']]['Result']['StatusMessage']))
763 #out_obj gets returned to GET request
764 if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
765 out_obj = TestRecord[input_data['Id']]['Result']['Results']
767 out_obj = {"StatusCode": 200,
768 "StatusMessage": "FINISHED",
769 "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
770 "Version": TestRecord[input_data['Id']]["Version"],
771 "Duration": TestRecord[input_data['Id']]["Duration"],
772 "Output": TestRecord[input_data['Id']]["Output"]["Output"],
773 "Results": TestRecord[input_data['Id']]['Result']['Results']}
774 if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
775 cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output']))
777 for key in out_obj["Results"]:
778 cherrypy.log("Output key: " + str(key))
779 if key in TestRecord[input_data['Id']]['Output']['Output']:
780 out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
782 cherrypy.log("***** GET RETURNING RESULTS Back ****")
783 cherrypy.log(str(out_obj))
786 elif 'GetStatus' in input_data['Type']:
787 print " Dump Records"
788 for id, record in TestRecord,items():
790 print " Record:" + str(reecord)
792 elif 'GetOutput' in input_data['Type']:
794 if TestRecord[input_data['Id']]['Output'] == {} and \
797 cherrypy.log( "*** Request blocked " + input_data['Id'])
799 while TestRecord[input_data['Id']]['Output'] == {} \
800 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
803 cherrypy.log( "*** Request released " + input_data['Id'])
805 cherrypy.log( "Output: " + str(TestRecord[input_data['Id']]['Output']))
806 return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
807 elif 'GetOutputLog' in input_data['Type']:
809 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
810 id = input_data['Id']
811 cherrypy.log("Old directory found for ID: " + id)
812 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
815 if os.path.exists (rdir + "/PlaybookExDir.txt"):
816 cherrypy.log("Found PlaybookExDir.txt file")
817 f = open( rdir + '/PlaybookExDir.txt', 'r')
818 playbookexdir = f.readline()
821 cherrypy.log("Id: " + id)
822 cherrypy.log("RunDir: " + rdir)
823 if os.path.exists( rdir + "/output.log"):
824 cherrypy.log("Found output.log file")
825 f = open( rdir + '/output.log', 'r')
826 output_log = f.readline()
836 if TestRecord[input_data['Id']]['Log'] == '' and \
839 cherrypy.log( "*** Request blocked " + input_data['Id'])
841 while TestRecord[input_data['Id']]['Log'] == '' \
842 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
845 cherrypy.log( "*** Request released " + input_data['Id'])
847 cherrypy.log( "Log:" + str(TestRecord[input_data['Id']]['Log']))
848 return {"Log": TestRecord[input_data['Id']]['Log']}
850 # Not in memory check for a file
851 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
852 id = input_data['Id']
853 cherrypy.log("Old directory found for ID: " + id)
854 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
857 if os.path.exists (rdir + "/PlaybookExDir.txt"):
858 cherrypy.log("Found PlaybookExDir.txt file")
859 f = open( rdir + '/PlaybookExDir.txt', 'r')
860 playbookexdir = f.readline()
863 cherrypy.log("Id: " + id)
864 cherrypy.log("RunDir: " + rdir)
865 if 'GetLog' in input_data['Type']:
866 if os.path.exists( rdir + "/output.log"):
867 cherrypy.log("Found output.log file")
868 f = open( rdir + '/output.log', 'r')
869 output_log = f.readline()
872 elif 'GetOutputLog' in input_data['Type']:
873 if os.path.exists( rdir + "/output.log"):
874 cherrypy.log("Found output.log file")
875 f = open( rdir + '/output.log', 'r')
876 output_log = f.readline()
879 elif 'GetResult' in input_data['Type']:
880 if os.path.exists (rdir + "/PlaybookName.txt"):
881 cherrypy.log("Found PlaybookName.txt file")
882 f = open( rdir + '/PlaybookName.txt', 'r')
883 playbooknametxt = f.readline()
886 playbooknametxt = "NA"
888 # Add code to get other items not just output.log from files
889 if os.path.exists( rdir + "/log.file"):
890 cherrypy.log("Found log.file")
892 f = open( rdir + '/log.file', 'r')
897 out_results = out_results + line
898 elif "RECAP" in line:
899 out_results = out_results + line
900 recap_line = f.readline()
902 out_results = out_results + recap_line
903 recap_line = f.readline()
906 out_obj = {"StatusCode": 200,
907 "StatusMessage": "FINISHED",
908 "PlaybookName": playbooknametxt,
909 "Version": "Version",
911 "Results": out_results}
914 return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
917 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
919 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
920 elif 'DELETE' in cherrypy.request.method:
921 input_data = parse_query_string(cherrypy.request.query_string)
923 cherrypy.log( "***> in RestServer.DELETE")
924 cherrypy.log("Payload: " + str(input_data))
926 if input_data['Id'] in TestRecord:
927 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
928 cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
929 TestRecord.pop (input_data['Id'])
930 if input_data['Id'] in ActiveProcess:
931 ActiveProcess.pop (input_data['Id'])
933 return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
935 return {"StatusCode": 200, "StatusMessage": "PENDING"}
937 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
940 if __name__ == '__main__':
944 config_file_path = "RestServer_config"
946 if not os.path.exists(config_file_path):
947 print '[INFO] The config file does not exist'
957 timeout_seconds = 'na'
962 getresults_block = False
965 file = open(config_file_path, 'r')
966 for line in file.readlines():
969 ip = line.split(':')[1].strip()
970 elif 'AnsibleServer:' in line:
971 AnsibleServer = line.split(':')[1].strip()
972 elif 'port:' in line:
973 port = line.split(':')[1].strip()
974 elif 'ksalt:' in line:
975 salt = line.split(':')[1].strip()
977 tls = 'YES' in line.split(':')[1].strip().upper()
978 elif 'auth:' in line:
979 auth = 'YES' in line.split(':')[1].strip().upper()
980 if tls and 'priv:' in line:
981 priv = line.split(':')[1].strip()
982 if tls and 'pub:' in line:
983 pub = line.split(':')[1].strip()
984 if tls and 'inter_cert:' in line:
985 intermediate = line.split(':')[1].strip()
986 if 'timeout_seconds' in line:
987 timeout_seconds = int (line.split(':')[1].strip())
988 if 'ansible_path' in line:
989 ansible_path = line.split(':')[1].strip()
990 if 'ansible_temp' in line:
991 ansible_temp = line.split(':')[1].strip()
993 host = line.split(':')[1].strip()
995 users = line.split(':')[1].strip()
996 if 'getresults_block' in line:
997 getresults_block = 'YES' in line.split(':')[1].strip().upper()
998 if 'from_files' in line:
999 from_files = 'YES' in line.split(':')[1].strip().upper()
1007 'response.timeout': 5400,
1008 'server.socket_host': ip,
1009 'server.socket_port': int(port),
1010 'server.protocol_version': 'HTTP/1.1'
1015 # Use pythons built-in SSL
1016 cherrypy.server.ssl_module = 'builtin'
1018 # Point to certificate files
1020 if not os.path.exists(pub):
1021 print '[INFO] The public certificate does not exist'
1024 if not os.path.exists(priv):
1025 print '[INFO] The private key does not exist'
1028 if not os.path.exists(intermediate):
1029 print '[INFO] The intermediate certificate does not exist'
1033 cherrypy.server.ssl_certificate = pub
1034 cherrypy.server.ssl_certificate_chain = intermediate
1035 cherrypy.server.ssl_private_key = priv
1038 # Read in and build user dictionary
1039 if not os.path.exists(users):
1040 print '[INFO] The users file does not exist: ' + users
1043 user_file = open(users, 'r')
1044 for line in user_file.readlines():
1046 id = line.split(':')[0].strip()
1047 pw = line.split(':')[1].strip()
1048 userpassdict[id] = pw
1049 #print str(userpassdict)
1052 {'tools.auth_basic.on': True,
1053 'tools.auth_basic.realm': 'earth',
1054 'tools.auth_basic.checkpassword': validate_password,
1058 application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1060 application = cherrypy.tree.mount(TestManager(), '/')
1062 cherrypy.config.update({
1063 'log.access_file': "/var/log/RestServer.access"
1065 accessLogName = "/var/log/RestServer.access"
1066 applicationLogName = "/var/log/RestServer.log"
1067 cherrypy.config.update(global_conf)
1069 log = application.log
1071 log.access_file = ""
1072 from logging import handlers
1073 applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1074 accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1076 applicationLogFileHandler.setLevel(logging.DEBUG)
1077 log.error_log.addHandler(applicationLogFileHandler)
1078 log.access_log.addHandler(accessLogFileHandler)
1082 cherrypy.engine.start()
1083 cherrypy.engine.block()