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 * 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]
389 cherrypy.log( "Request USER : " + cherrypy.request.login)
390 cherrypy.log( "Request Decode: ID " + Id)
391 cherrypy.log( "Request Decode: VnfType " + VnfType)
392 cherrypy.log( "Request Decode: EnvParameters " + json.dumps(EnvParameters))
394 # Verify VNF_instance was passed in EnvParameters
395 if VNF_instance != None:
396 cherrypy.log( "Request Decode: VnfInstance " + VNF_instance)
398 cherrypy.log( "StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED" )
399 return {"StatusCode": 107,
400 "StatusMessage": "VNF_instance NOT PROVIDED"}
402 if inventory_names != None:
403 cherrypy.log( "Request Decode: Inventory Names " + inventory_names)
405 cherrypy.log( "Request Decode: Inventory Names " + "Not provided")
407 cherrypy.log( "Request Decode: PlaybookName " + PlaybookName)
408 PlayBookFunction = PlaybookName.rsplit("/",2)[1]
409 PlayBookFile = PlayBookFunction + "/site.yml"
410 cherrypy.log( "Request Decode: PlaybookFunction " + PlayBookFunction)
411 cherrypy.log( "Request Decode: Playbook file " + PlayBookFile)
413 BaseDir = ansible_path + "/" + PlaybookName.rsplit("/",1)[0]
414 CopyDir = ansible_path + "/" + PlaybookName.rsplit("/",2)[0]
415 cherrypy.log( "Request Decode: Basedir " + BaseDir)
416 cherrypy.log( "Request Decode: Copydir " + CopyDir)
419 PlaybookDir = ansible_temp + "/" + \
420 VNF_instance + "_" + str_uuid + "_" + str(Id)
422 # AnsibleInv is the directory where the host file to be run exsists
423 AnsibleInv = ansible_path + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance
426 # Create base run directory if it doesn't exist
427 if not os.path.exists(ansible_temp):
428 cherrypy.log( "Creating Base Run Directory: " + ansible_temp)
429 os.makedirs(ansible_temp)
431 if not os.path.exists( CopyDir ):
432 cherrypy.log("Playbook Not Found")
433 return {"StatusCode": 101,
434 "StatusMessage": "PLAYBOOK NOT FOUND"}
436 # copy static playbook dir to run dir
437 cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir)
438 shutil.copytree(CopyDir, PlaybookDir)
439 cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;"
440 cmd="/usr/bin/find " + PlaybookDir + " -exec chmod +rx {} \;"
444 cherrypy.log( "PlaybookDir: " + PlaybookDir)
445 cherrypy.log( "AnsibleInv: " + AnsibleInv)
447 #location of host file
448 #HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
449 #cherrypy.log("HostFile: " + HostFile)
451 # Process inventory file for target
457 if 'NodeList' in input_json:
458 NodeList = input_json['NodeList']
460 cherrypy.log("NodeList: " + str(NodeList));
464 cherrypy.log( "*** NodeList - Empty ***")
465 #AnsibleInvFail = False
468 #AnsibleInvFail = False # ???
471 ###############################################################################
472 ##### Host file processing ###########################
473 ##### 1. Use file delivered with playbook ###########################
474 ##### 2. If HostNames + NodeList generate and use ###########################
475 ##### 3. If HostNames = VM or NVF copy and use. ###########################
476 ###############################################################################
478 #location of host file - Default
479 HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
480 cherrypy.log("HostFile: " + HostFile)
482 # if NodeList and InventoryNames need to build host file
483 if HaveInventoryNames & HaveNodeList:
484 cherrypy.log("Build host file from NodeList")
485 ret = buildHostsSysCall (input_json, PlaybookDir, inventory_names)
487 cherrypy.log("Returning Error: Not running Playbook")
488 return {"StatusCode": 105,
489 "StatusMessage": "NodeList: Missing vnfc-type field"}
491 # Having been built now copy new file to correct file
492 shutil.copy(PlaybookDir + "/host_file.txt", HostFile)
493 cherrypy.log("Copying Generated host file to: " + HostFile)
494 elif HaveInventoryNames & (not HaveNodeList):
495 ### Copy Instar based Hostfile
496 if inventory_names == "VNFC":
498 host_file_path = "/storage/inventory/VNFC/" + VNF_instance + "hosts"
499 if os.path.exists(host_file_path):
501 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
502 shutil.copy(host_file_path, HostFile)
504 cherrypy.log("Inventory file not found: " + host_file_path)
505 elif inventory_names == "None":
507 host_file_path = "/storage/inventory/None/" + VNF_instance + "hosts"
508 if os.path.exists(host_file_path):
510 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
511 shutil.copy(host_file_path, HostFile)
513 cherrypy.log("Inventory file not found: " + host_file_path)
514 elif inventory_names == "VM":
516 host_file_path = "/storage/inventory/VM/" + VNF_instance + "hosts"
517 if os.path.exists(host_file_path):
519 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
520 shutil.copy(host_file_path, HostFile)
522 cherrypy.log("Inventory file not found: " + host_file_path)
525 timeout = timeout_seconds
526 if 'Timeout' in input_json:
527 timeout = int (input_json['Timeout'])
528 cherrypy.log("Timeout from API: " + str(timeout))
531 cherrypy.log("Timeout not passed from API using default: " + str(timeout))
534 if 'EnvParameters' in input_json:
535 EnvParam = input_json['EnvParameters']
538 if 'LocalParameters' in input_json:
539 LocalParam = input_json['LocalParameters']
542 if 'FileParameters' in input_json:
543 FileParam = input_json['FileParameters']
546 if 'CallBack' in input_json:
547 callback_flag = input_json['CallBack']
549 # if AnsibleServer is not set to 'na' don't send AnsibleServer in PENDING responce.
550 if AnsibleServer != 'na':
551 TestRecord[Id] = {'PlaybookName': PlaybookName,
553 'NodeList': NodeList,
554 'HostGroupList': hostgrouplist,
555 'HostNameList': hostnamelist,
559 'EnvParameters': EnvParam,
560 'LocalParameters': LocalParam,
561 'FileParameters': FileParam,
562 'CallBack': callback_flag,
563 'Result': {"StatusCode": 100,
564 "StatusMessage": 'PENDING',
565 "AnsibleServer": str(AnsibleServer),
566 "ExpectedDuration": str(timeout) + "sec"},
572 TestRecord[Id] = {'PlaybookName': PlaybookName,
574 'NodeList': NodeList,
575 'HostGroupList': hostgrouplist,
576 'HostNameList': hostnamelist,
580 'EnvParameters': EnvParam,
581 'LocalParameters': LocalParam,
582 'FileParameters': FileParam,
583 'CallBack': callback_flag,
584 'Result': {"StatusCode": 100,
585 "StatusMessage": 'PENDING',
586 "ExpectedDuration": str(timeout) + "sec"},
592 cherrypy.log("Test_Record: " + str(TestRecord[Id]))
595 if not TestRecord[Id]['FileParameters'] == {}:
596 for key in TestRecord[Id]['FileParameters']:
598 filecontent = TestRecord[Id]['FileParameters'][key]
599 f = open(PlaybookDir + "/" + filename, "w")
605 if os.path.exists( ansible_path + '/' + PlaybookName):
606 AnsiblePlaybookFail = False
608 if AnsiblePlaybookFail:
609 #if os.path.exists(PlaybookDir):
610 #shutil.rmtree (PlaybookDir)
612 return {"StatusCode": 101,
613 "StatusMessage": "PLAYBOOK NOT FOUND"}
617 playbook_path = PlaybookDir
620 if not os.path.exists(playbook_path + "/vars"):
621 os.mkdir(playbook_path + "/vars")
622 if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
623 os.mknod(playbook_path + "/vars/defaults.yml")
625 ###################################################
627 #write local parameters passed into defaults.yml
629 f = open(playbook_path + "/vars/defaults.yml","a")
630 #for id, record in TestRecord.items():
631 print TestRecord[Id]['LocalParameters']
632 local_parms = TestRecord[Id]['LocalParameters']
633 for key, value in local_parms.items():
634 f.write(key +"=" + value + "\n");
636 ###################################################
638 for key in TestRecord[Id]['LocalParameters']:
640 for i in range(len(TestRecord[Id]['HostNameList'])):
641 if key in TestRecord[Id]['HostNameList'][i]:
643 if len(host_index) == 0:
644 for i in range(len(TestRecord[Id]['HostGroupList'])):
645 if key in TestRecord[Id]['HostGroupList'][i]:
647 if len(host_index) > 0:
648 for i in range(len(host_index)):
649 f = open(playbook_path + "/vars/" +
650 TestRecord[Id]['HostNameList'][host_index[i]] +
652 for param in TestRecord[Id]['LocalParameters'][key]:
653 f.write(param + ": " +
654 str (TestRecord[Id]['LocalParameters'][key][param]) +
659 # write some info out to files before running
660 f = open(playbook_path + "/PlaybookName.txt", "a")
661 f.write(PlaybookName)
663 f = open(playbook_path + "/PlaybookExDir.txt", "a")
664 f.write(PlaybookDir + "/" + PlayBookFunction)
666 f = open(playbook_path + "/JsonRequest.txt", "w")
667 #f.write(str(input_json))
668 print( json.dumps(input_json, indent=4, sort_keys=True))
669 f.write( json.dumps(input_json, indent=4, sort_keys=True))
673 # Check that HostFile exists
674 if not os.path.isfile(HostFile):
675 cherrypy.log("Inventory file Not Found: " + HostFile)
676 return {"StatusCode": 101,
677 "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"}
679 # Cannot use thread because ansible module uses
680 # signals which are only supported in main thread.
681 # So use multiprocess with shared object
682 # args = (callback, Id, PlaybookDir + "/" + AnsibleInv,
684 p = Process(target = RunAnsible_Playbook,
685 args = (callback, Id, HostFile,
686 PlaybookDir + '/' + PlayBookFile,
687 NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction,
690 ActiveProcess[Id] = p
691 return TestRecord[Id]['Result']
693 cherrypy.log("TEST ID ALREADY DEFINED")
694 return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
697 return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
700 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"}
702 elif 'GET' in cherrypy.request.method:
704 # Lets pause for a second just incase the resquest was just kicked off
707 input_data = parse_query_string(cherrypy.request.query_string)
709 # Verify we have a Type passed in GET request
710 if not ( 'Type' in input_data):
711 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
713 cherrypy.log( "Request USER: " + cherrypy.request.login)
714 cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type'])
716 if 'LogRest' in input_data['Type']:
718 sys.stdout = open("/var/log/RestServer.log", "w")
720 # Just a debug to dump any records
721 if 'GetStatus' in input_data['Type']:
722 cherrypy.log( "******** Dump Records **********")
723 if TestRecord.items():
724 for id, record in TestRecord.items():
725 cherrypy.log( " Id: " + id)
726 cherrypy.log( "Record: " + str(record))
728 cherrypy.log(" No Records to dump")
730 if 'Id' in input_data and 'Type' in input_data:
731 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']):
732 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
733 if input_data['Id'] in TestRecord:
735 if 'GetResult' in input_data['Type']:
737 cherrypy.log( " ** GetResult for: " + str (input_data['Id']))
739 if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
742 #check if playbook is still running
743 while ActiveProcess[input_data['Id']].is_alive():
744 cherrypy.log( "*** Playbook running returning PENDING for " + str(input_data['Id']))
746 ## If still running return PENDING response
748 if AnsibleServer != 'na':
749 return {"StatusCode": 100,
750 "StatusMessage": 'PENDING',
751 "AnsibleServer": str(AnsibleServer)}
753 return {"StatusCode": 100,
754 "StatusMessage": 'PENDING'}
757 #cherrypy.log( "*** Request released " + input_data['Id'])
759 cherrypy.log(str( TestRecord[input_data['Id']]['Result']))
760 cherrypy.log("Output: " + str( TestRecord[input_data['Id']]['Output']))
761 cherrypy.log("StatusCode: " + str( TestRecord[input_data['Id']]['Result']['StatusCode']))
762 cherrypy.log("StatusMessage: " + str( TestRecord[input_data['Id']]['Result']['StatusMessage']))
764 #out_obj gets returned to GET request
765 if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
766 out_obj = TestRecord[input_data['Id']]['Result']['Results']
768 out_obj = {"StatusCode": 200,
769 "StatusMessage": "FINISHED",
770 "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
771 "Version": TestRecord[input_data['Id']]["Version"],
772 "Duration": TestRecord[input_data['Id']]["Duration"],
773 "Output": TestRecord[input_data['Id']]["Output"]["Output"],
774 "Results": TestRecord[input_data['Id']]['Result']['Results']}
775 if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
776 cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output']))
778 for key in out_obj["Results"]:
779 cherrypy.log("Output key: " + str(key))
780 if key in TestRecord[input_data['Id']]['Output']['Output']:
781 out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
783 cherrypy.log("***** GET RETURNING RESULTS Back ****")
784 cherrypy.log(str(out_obj))
787 elif 'GetStatus' in input_data['Type']:
788 print " Dump Records"
789 for id, record in TestRecord,items():
791 print " Record:" + str(reecord)
793 elif 'GetOutput' in input_data['Type']:
795 if TestRecord[input_data['Id']]['Output'] == {} and \
798 cherrypy.log( "*** Request blocked " + input_data['Id'])
800 while TestRecord[input_data['Id']]['Output'] == {} \
801 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
804 cherrypy.log( "*** Request released " + input_data['Id'])
806 cherrypy.log( "Output: " + str(TestRecord[input_data['Id']]['Output']))
807 return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
808 elif 'GetOutputLog' in input_data['Type']:
810 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
811 id = input_data['Id']
812 cherrypy.log("Old directory found for ID: " + id)
813 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
816 if os.path.exists (rdir + "/PlaybookExDir.txt"):
817 cherrypy.log("Found PlaybookExDir.txt file")
818 f = open( rdir + '/PlaybookExDir.txt', 'r')
819 playbookexdir = f.readline()
822 cherrypy.log("Id: " + id)
823 cherrypy.log("RunDir: " + rdir)
824 if os.path.exists( rdir + "/output.log"):
825 cherrypy.log("Found output.log file")
826 f = open( rdir + '/output.log', 'r')
827 output_log = f.readline()
837 if TestRecord[input_data['Id']]['Log'] == '' and \
840 cherrypy.log( "*** Request blocked " + input_data['Id'])
842 while TestRecord[input_data['Id']]['Log'] == '' \
843 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
846 cherrypy.log( "*** Request released " + input_data['Id'])
848 cherrypy.log( "Log:" + str(TestRecord[input_data['Id']]['Log']))
849 return {"Log": TestRecord[input_data['Id']]['Log']}
851 # Not in memory check for a file
852 if glob.glob( ansible_temp + '/*_' + input_data['Id']):
853 id = input_data['Id']
854 cherrypy.log("Old directory found for ID: " + id)
855 run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id'])
858 if os.path.exists (rdir + "/PlaybookExDir.txt"):
859 cherrypy.log("Found PlaybookExDir.txt file")
860 f = open( rdir + '/PlaybookExDir.txt', 'r')
861 playbookexdir = f.readline()
864 cherrypy.log("Id: " + id)
865 cherrypy.log("RunDir: " + rdir)
866 if 'GetLog' in input_data['Type']:
867 if os.path.exists( rdir + "/output.log"):
868 cherrypy.log("Found output.log file")
869 f = open( rdir + '/output.log', 'r')
870 output_log = f.readline()
873 elif 'GetOutputLog' in input_data['Type']:
874 if os.path.exists( rdir + "/output.log"):
875 cherrypy.log("Found output.log file")
876 f = open( rdir + '/output.log', 'r')
877 output_log = f.readline()
880 elif 'GetResult' in input_data['Type']:
881 if os.path.exists (rdir + "/PlaybookName.txt"):
882 cherrypy.log("Found PlaybookName.txt file")
883 f = open( rdir + '/PlaybookName.txt', 'r')
884 playbooknametxt = f.readline()
887 playbooknametxt = "NA"
889 # Add code to get other items not just output.log from files
890 if os.path.exists( rdir + "/log.file"):
891 cherrypy.log("Found log.file")
893 f = open( rdir + '/log.file', 'r')
898 out_results = out_results + line
899 elif "RECAP" in line:
900 out_results = out_results + line
901 recap_line = f.readline()
903 out_results = out_results + recap_line
904 recap_line = f.readline()
907 out_obj = {"StatusCode": 200,
908 "StatusMessage": "FINISHED",
909 "PlaybookName": playbooknametxt,
910 "Version": "Version",
912 "Results": out_results}
915 return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
918 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
920 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
921 elif 'DELETE' in cherrypy.request.method:
922 input_data = parse_query_string(cherrypy.request.query_string)
924 cherrypy.log( "***> in RestServer.DELETE")
925 cherrypy.log("Payload: " + str(input_data))
927 if input_data['Id'] in TestRecord:
928 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
929 cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
930 TestRecord.pop (input_data['Id'])
931 if input_data['Id'] in ActiveProcess:
932 ActiveProcess.pop (input_data['Id'])
934 return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
936 return {"StatusCode": 200, "StatusMessage": "PENDING"}
938 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
941 if __name__ == '__main__':
945 config_file_path = "RestServer_config"
947 if not os.path.exists(config_file_path):
948 print '[INFO] The config file does not exist'
958 timeout_seconds = 'na'
963 getresults_block = False
966 file = open(config_file_path, 'r')
967 for line in file.readlines():
970 ip = line.split(':')[1].strip()
971 elif 'AnsibleServer:' in line:
972 AnsibleServer = line.split(':')[1].strip()
973 elif 'port:' in line:
974 port = line.split(':')[1].strip()
975 elif 'ksalt:' in line:
976 salt = line.split(':')[1].strip()
978 tls = 'YES' in line.split(':')[1].strip().upper()
979 elif 'auth:' in line:
980 auth = 'YES' in line.split(':')[1].strip().upper()
981 if tls and 'priv:' in line:
982 priv = line.split(':')[1].strip()
983 if tls and 'pub:' in line:
984 pub = line.split(':')[1].strip()
985 if tls and 'inter_cert:' in line:
986 intermediate = line.split(':')[1].strip()
987 if 'timeout_seconds' in line:
988 timeout_seconds = int (line.split(':')[1].strip())
989 if 'ansible_path' in line:
990 ansible_path = line.split(':')[1].strip()
991 if 'ansible_temp' in line:
992 ansible_temp = line.split(':')[1].strip()
994 host = line.split(':')[1].strip()
996 users = line.split(':')[1].strip()
997 if 'getresults_block' in line:
998 getresults_block = 'YES' in line.split(':')[1].strip().upper()
999 if 'from_files' in line:
1000 from_files = 'YES' in line.split(':')[1].strip().upper()
1008 'response.timeout': 5400,
1009 'server.socket_host': ip,
1010 'server.socket_port': int(port),
1011 'server.protocol_version': 'HTTP/1.1'
1016 # Use pythons built-in SSL
1017 cherrypy.server.ssl_module = 'builtin'
1019 # Point to certificate files
1021 if not os.path.exists(pub):
1022 print '[INFO] The public certificate does not exist'
1025 if not os.path.exists(priv):
1026 print '[INFO] The private key does not exist'
1029 if not os.path.exists(intermediate):
1030 print '[INFO] The intermediate certificate does not exist'
1034 cherrypy.server.ssl_certificate = pub
1035 cherrypy.server.ssl_certificate_chain = intermediate
1036 cherrypy.server.ssl_private_key = priv
1039 # Read in and build user dictionary
1040 if not os.path.exists(users):
1041 print '[INFO] The users file does not exist: ' + users
1044 user_file = open(users, 'r')
1045 for line in user_file.readlines():
1047 id = line.split(':')[0].strip()
1048 pw = line.split(':')[1].strip()
1049 userpassdict[id] = pw
1050 #print str(userpassdict)
1053 {'tools.auth_basic.on': True,
1054 'tools.auth_basic.realm': 'earth',
1055 'tools.auth_basic.checkpassword': validate_password,
1059 application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1061 application = cherrypy.tree.mount(TestManager(), '/')
1063 cherrypy.config.update({
1064 'log.access_file': "/var/log/RestServer.access"
1066 accessLogName = "/var/log/RestServer.access"
1067 applicationLogName = "/var/log/RestServer.log"
1068 cherrypy.config.update(global_conf)
1070 log = application.log
1072 log.access_file = ""
1073 from logging import handlers
1074 applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1075 accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1077 applicationLogFileHandler.setLevel(logging.DEBUG)
1078 log.error_log.addHandler(applicationLogFileHandler)
1079 log.access_log.addHandler(accessLogFileHandler)
1083 cherrypy.engine.start()
1084 cherrypy.engine.block()