3 * ============LICENSE_START=======================================================
5 * ================================================================================
6 * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
7 * ================================================================================
8 * Copyright (C) 2017 Amdocs
9 * =============================================================================
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
22 * ============LICENSE_END=========================================================
26 import time, datetime, json, os, sys, subprocess
35 from cherrypy.lib.httputil import parse_query_string
37 from multiprocessing import Process, Manager
39 from AnsibleModule import ansibleSysCall
40 from BuildHostFile import buildHostsSysCall
41 from BuildPlaybookParams import buildInventorySysCall, getPlaybookFile
43 from os import listdir
44 from os.path import isfile, join
46 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:
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()
67 def callback(Id, Result, Output, Log, returncode):
68 cherrypy.log("***> 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
84 'PlaybookName': TestRecord[Id]['PlaybookName'],
85 'Version': TestRecord[Id]['Version'],
86 'NodeList': TestRecord[Id]['NodeList'],
87 'HostGroupList': TestRecord[Id]['HostGroupList'],
88 'HostNameList': TestRecord[Id]['HostNameList'],
89 'Time': TestRecord[Id]['Time'],
90 'Timeout': TestRecord[Id]['Timeout'],
91 'Duration': str(delta_time),
92 'EnvParameters': TestRecord[Id]['EnvParameters'],
93 'LocalParameters': TestRecord[Id]['LocalParameters'],
94 'FileParameters': TestRecord[Id]['FileParameters'],
95 'CallBack': TestRecord[Id]['CallBack'],
99 'Path': TestRecord[Id]['Path'],
100 'Mandatory': TestRecord[Id]['Path']
103 if TestRecord[Id]['CallBack'] is not None:
105 # Posting results to callback server
108 "StatusMessage": "FINISHED",
109 "PlaybookName": TestRecord[Id]["PlaybookName"],
110 "Version": TestRecord[Id]["Version"],
111 "Duration": TestRecord[Id]["Duration"],
112 "Results": TestRecord[Id]['Result']['Results']
115 cherrypy.log("CALLBACK: TestRecord[Id]['Output']['Output']:", str(TestRecord[Id]['Output']['Output']))
116 cherrypy.log("CALLBACK: Results:", str(data["Results"]))
118 if not TestRecord[Id]['Output']['Output'] == {}:
119 for key in data["Results"]:
120 if key in TestRecord[Id]['Output']['Output']:
121 data["Results"][key]["Output"] = TestRecord[Id]['Output']['Output'][key]
123 cherrypy.log(" Posting to", TestRecord[Id]['CallBack'])
125 s = requests.Session()
126 r = s.post(TestRecord[Id]['CallBack'], data=json.dumps(data),
127 headers={'content-type': 'application/json'})
128 cherrypy.log(" Response", r.status_code, r.text)
131 def RunAnsible_Playbook(callback, Id, Inventory, Playbook, NodeList, TestRecord, Path, ArchiveFlag, pnf_flag=False):
132 cherrypy.log("***> in RestServer.RunAnsible_Playbook")
134 # 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'],
142 cherrypy.log("Return code:" + str(returncode))
143 cherrypy.log("Return value:" + str(retval))
147 Output = {'Output': {}}
151 onlyfiles = [f for f in listdir(Path)
152 if isfile(join(Path, f))]
154 cherrypy.log("Checking for results.txt files: ")
155 for file in onlyfiles:
156 if "results.txt" in file:
157 # if file.endswith("results.txt"):
158 cherrypy.log("results file: " + file)
159 f = open(Path + "/" + file, "r")
161 key = file.split("_")[0]
162 Output['Output'][key] = f.read()
164 resultsData = f.read() # Not to pass vnf instance name
165 OutputP = json.loads(resultsData)
166 Output['Output'] = OutputP
167 cherrypy.log("Output = " + str(Output['Output']))
168 # Output['Output'][key] = f.read() # To pass vnf instance name
172 Output = {'Output': {}}
174 Result = {'Results': {}}
175 if 'could not be found' in Log:
176 Result['Results'] = {"StatusCode": 101, "StatusMessage": "PLAYBOOK NOT FOUND"}
178 if returncode == 137:
179 Result['Results'] = {"StatusCode": 500, "StatusMessage": "TERMINATED"}
180 elif TestRecord[Id]['NodeList'] == []:
182 if 'TargetNode' in TestRecord[Id]['EnvParameters']:
183 targetlist = TestRecord[Id]['EnvParameters']['TargetNode'].split(' ')
185 targetlist = ["localhost"]
188 for i in range(len(targetlist)):
189 if key in targetlist[i]:
192 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and int(retval[key][3]) == 0:
194 Result['Results'][targetlist[host_index]] = \
195 {"GroupName": 'na', "StatusCode": 200, "StatusMessage": "SUCCESS"}
197 Result['Results'][key] = {"GroupName": 'na', "StatusCode": 200, "StatusMessage": "SUCCESS"}
198 elif int(retval[key][2]) > 0:
200 Result['Results'][targetlist[host_index]] = \
201 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
203 Result['Results'][key] = \
204 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
205 elif int(retval[key][3]) > 0:
207 Result['Results'][targetlist[host_index]] = \
208 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
210 Result['Results'][key] = \
211 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
214 if len(TestRecord[Id]['HostNameList']) > 0:
216 for i in range(len(TestRecord[Id]['HostNameList'])):
217 if key in TestRecord[Id]['HostNameList'][i]:
220 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
221 int(retval[key][3]) == 0:
222 if len(host_index) > 0:
223 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
225 "GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
226 "StatusCode": 200, "StatusMessage": "SUCCESS"
229 for i in range(1, len(host_index)):
230 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"] += \
231 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
233 Result['Results'][key] = {"GroupName": key, "StatusCode": 200, "StatusMessage": "SUCCESS"}
235 elif int(retval[key][2]) > 0:
236 if len(host_index) > 0:
237 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
239 "GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
240 "StatusCode": 400, "StatusMessage": "NOT REACHABLE"
243 for i in range(1, len(host_index)):
244 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"] += \
245 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
247 Result['Results'][key] = \
248 {"GroupName": key, "StatusCode": 200, "StatusMessage": "NOT REACHABLE"}
249 elif int(retval[key][3]) > 0:
250 if len(host_index) > 0:
251 Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
253 "GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
254 "StatusCode": 400, "StatusMessage": "FAILURE"
257 for i in range(1, len(host_index)):
258 Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"] += \
259 "," + TestRecord[Id]['HostGroupList'][host_index[i]]
261 Result['Results'][key] = \
262 {"GroupName": key, "StatusCode": 200, "StatusMessage": "FAILURE"}
265 for i in range(len(TestRecord[Id]['NodeList'])):
266 if key in TestRecord[Id]['NodeList'][i]:
269 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
270 int(retval[key][3]) == 0:
271 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
272 {"GroupName": 'na', "StatusCode": 200, "StatusMessage": "SUCCESS"}
273 elif int(retval[key][2]) > 0:
274 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
275 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
276 elif int(retval[key][3]) > 0:
277 Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
278 {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
280 callback(Id, Result, Output, Log, returncode)
283 def store_local_vars(playbook_path, Id):
284 if not os.path.exists(playbook_path + "/vars"):
285 os.mkdir(playbook_path + "/vars")
287 if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
288 os.mknod(playbook_path + "/vars/defaults.yml")
290 # ##################################################
292 # write local parameters passed into defaults.yml
294 local_parms = TestRecord[Id]['LocalParameters']
295 cherrypy.log("LocalParameters: " + str(local_parms))
297 f = open(playbook_path + "/vars/defaults.yml", "a")
298 for key, value in list(local_parms.items()):
299 f.write(key + "=" + value + "\n")
301 # ##################################################
303 for key in TestRecord[Id]['LocalParameters']:
305 for i in range(len(TestRecord[Id]['HostNameList'])):
306 if key in TestRecord[Id]['HostNameList'][i]:
308 if len(host_index) == 0:
309 for i in range(len(TestRecord[Id]['HostGroupList'])):
310 if key in TestRecord[Id]['HostGroupList'][i]:
312 if len(host_index) > 0:
313 for i in range(len(host_index)):
314 f = open(playbook_path + "/vars/" + TestRecord[Id]['HostNameList'][host_index[i]] + ".yml", "a")
315 for param in TestRecord[Id]['LocalParameters'][key]:
316 f.write(param + ": " + str(TestRecord[Id]['LocalParameters'][key][param]) + "\n")
320 def process_pnf_playbook(input_json, Id, EnvParameters, time_now):
321 cherrypy.log("Processing playbook for PNF...")
323 PlaybookName = input_json['PlaybookName']
324 version = input_json.get('Version', None)
327 cherrypy.log("Request USER : " + cherrypy.request.login)
328 cherrypy.log("Request Decode: ID " + Id)
329 cherrypy.log("Request Decode: EnvParameters " + json.dumps(EnvParameters))
330 cherrypy.log("Request Decode: PlaybookName " + PlaybookName)
332 str_uuid = str(uuid.uuid4())
334 HomeDir = os.path.dirname(os.path.realpath("~/"))
336 PlaybookType = PlaybookName.split(".")[0].split('_')[-1]
337 PlaybookDir = HomeDir + '/' + ANSIBLE_TEMP + "/" + PlaybookName.split(".")[0] + "_" + str_uuid
338 AnsibleInv = PlaybookType + "_" + "inventory"
341 cherrypy.log("Request Decode: PlaybookType " + PlaybookType)
342 cherrypy.log("Request Decode: PlaybookDir " + PlaybookDir)
343 cherrypy.log("Request Decode: AnsibleInv " + AnsibleInv)
345 NodeList = input_json.get('NodeList', [])
346 cherrypy.log("Request Decode: NodeList: " + str(NodeList))
348 # Create base run directory if it doesn't exist
349 if not os.path.exists(ANSIBLE_TEMP):
350 cherrypy.log("Creating Base Run Directory: " + ANSIBLE_TEMP)
351 os.makedirs(ANSIBLE_TEMP)
353 os.mkdir(PlaybookDir)
355 # Process inventory file for target
359 buildInventorySysCall(ANSIBLE_PATH, ANSIBLE_INV, NodeList, PlaybookDir, AnsibleInv, hostgrouplist, hostnamelist)
361 version_target = getPlaybookFile(ANSIBLE_PATH, PlaybookName, PlaybookType, PlaybookDir)
362 if not version_target:
363 return {"StatusCode": 101, "StatusMessage": "PLAYBOOK NOT FOUND"}
366 version = version_target
368 if 'Timeout' in input_json:
369 timeout = int(input_json['Timeout'])
370 cherrypy.log("Timeout from API: " + str(timeout))
372 timeout = timeout_seconds
373 cherrypy.log("Timeout not passed from API using default: " + str(timeout))
375 EnvParam = input_json.get('EnvParameters', {})
376 LocalParam = input_json.get('LocalParameters', {})
377 FileParam = input_json.get('FileParameters', {})
378 callback_flag = input_json.get('CallBack', None)
380 # if AnsibleServer is not set to 'na' don't send AnsibleServer in PENDING response.
382 'PlaybookName': PlaybookName,
384 'NodeList': NodeList,
385 'HostGroupList': hostgrouplist,
386 'HostNameList': hostnamelist,
390 'EnvParameters': EnvParam,
391 'LocalParameters': LocalParam,
392 'FileParameters': FileParam,
393 'CallBack': callback_flag,
396 "StatusMessage": 'PENDING',
397 "ExpectedDuration": str(timeout) + "sec"
404 if AnsibleServer != 'na':
405 TestRecord[Id]['Result']["AnsibleServer"] = str(AnsibleServer),
407 cherrypy.log("Test_Record: " + str(TestRecord[Id]))
410 if TestRecord[Id]['FileParameters']:
411 for key in TestRecord[Id]['FileParameters']:
413 filecontent = TestRecord[Id]['FileParameters'][key]
414 f = open(PlaybookDir + "/" + filename, "w")
418 playbook_path = PlaybookDir
421 store_local_vars(playbook_path, Id)
423 # write some info out to files before running
425 f = open(playbook_path + "/User.txt", "a")
426 f.write(cherrypy.request.login)
429 f = open(playbook_path + "/PlaybookName.txt", "a")
430 f.write(PlaybookName)
433 f = open(playbook_path + "/JsonRequest.txt", "w")
434 f.write(json.dumps(input_json, indent=4, sort_keys=True))
437 # Cannot use thread because ansible module uses signals which are only supported in main thread.
438 # So use multiprocess with shared object
439 p = Process(target=RunAnsible_Playbook,
440 args=(callback, Id, PlaybookDir + '/' + AnsibleInv, PlaybookDir + '/' + PlaybookType + '.yml',
441 NodeList, TestRecord, PlaybookDir, ArchiveFlag, True))
443 ActiveProcess[Id] = p
444 return TestRecord[Id]['Result']
447 def process_vnf_playbook(input_json, Id, EnvParameters, time_now):
448 cherrypy.log("Processing playbook for VNF...")
450 PlaybookName = input_json['PlaybookName']
451 VNF_instance = EnvParameters.get('vnf_instance')
452 version = input_json.get('Version', None)
456 HaveInventoryNames = False
457 inventory_names = None
458 if 'InventoryNames' in input_json:
459 inventory_names = input_json['InventoryNames']
460 HaveInventoryNames = True
462 AnsiblePlaybookFail = True
464 str_uuid = str(uuid.uuid4())
466 # VnfType = PlaybookName.split("/")[0]
469 cherrypy.log("Request USER : " + cherrypy.request.login)
470 cherrypy.log("Request Decode: ID " + Id)
471 # cherrypy.log("Request Decode: VnfType " + VnfType)
472 cherrypy.log("Request Decode: EnvParameters " + json.dumps(EnvParameters))
474 # Verify VNF_instance was passed in EnvParameters
475 if VNF_instance is not None:
476 cherrypy.log("Request Decode: VnfInstance " + VNF_instance)
478 cherrypy.log("StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED")
479 return {"StatusCode": 107, "StatusMessage": "VNF_instance NOT PROVIDED"}
481 if inventory_names is not None:
482 cherrypy.log("Request Decode: Inventory Names " + inventory_names)
484 cherrypy.log("Request Decode: Inventory Names " + "Not provided")
486 cherrypy.log("Request Decode: PlaybookName " + PlaybookName)
488 PlayBookFunction = PlaybookName.rsplit("/", 2)[1]
489 PlayBookFile = PlayBookFunction + "/site.yml"
491 cherrypy.log("Request Decode: PlaybookFunction " + PlayBookFunction)
492 cherrypy.log("Request Decode: PlaybookFile " + PlayBookFile)
494 BaseDir = ANSIBLE_PATH + "/" + PlaybookName.rsplit("/", 1)[0]
495 CopyDir = ANSIBLE_PATH + "/" + PlaybookName.rsplit("/", 2)[0]
496 cherrypy.log("Request Decode: Basedir " + BaseDir)
497 cherrypy.log("Request Decode: Copydir " + CopyDir)
499 PlaybookDir = ANSIBLE_TEMP + "/" + VNF_instance + "_" + str_uuid + "_" + str(Id)
500 cherrypy.log("Request Decode: PlaybookDir " + PlaybookDir)
502 # AnsibleInv is the directory where the host file to be run exists
503 # AnsibleInv = ANSIBLE_PATH + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance
506 # Create base run directory if it doesn't exist
507 if not os.path.exists(ANSIBLE_TEMP):
508 cherrypy.log("Creating Base Run Directory: " + ANSIBLE_TEMP)
509 os.makedirs(ANSIBLE_TEMP)
511 if not os.path.exists(CopyDir):
512 cherrypy.log("Playbook Not Found")
513 return {"StatusCode": 101, "StatusMessage": "PLAYBOOK NOT FOUND"}
515 # copy static playbook dir to run dir
516 cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir)
517 shutil.copytree(CopyDir, PlaybookDir)
518 # cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;"
519 cmd = "/usr/bin/find " + PlaybookDir + " -exec chmod +rx {} \;"
523 cherrypy.log("PlaybookDir: " + PlaybookDir)
524 # cherrypy.log("AnsibleInv: " + AnsibleInv)
526 # Process inventory file for target
530 NodeList = input_json.get('NodeList', [])
532 cherrypy.log("NodeList: " + str(NodeList))
536 cherrypy.log("*** NodeList - Empty ***")
540 # ##############################################################################
541 # #### Host file processing ###########################
542 # #### 1. Use file delivered with playbook ###########################
543 # #### 2. If HostNames + NodeList generate and use ###########################
544 # ##############################################################################
546 # Verify inventory directory exists
547 path = PlaybookDir + "/inventory/"
548 if not os.path.isdir(path):
549 cherrypy.log("Inventory directory %s does not exist - create it" % path)
553 cherrypy.log("Creation of the directory %s failed" % path)
555 cherrypy.log("Successfully created the directory %s " % path)
557 # location of host file - Default
558 HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
559 cherrypy.log("HostFile: " + HostFile)
561 # if NodeList and InventoryNames need to build host file
562 if HaveInventoryNames and HaveNodeList:
563 cherrypy.log("Build host file from NodeList")
564 ret = buildHostsSysCall(input_json, PlaybookDir, inventory_names)
566 cherrypy.log("Returning Error: Not running Playbook")
567 return {"StatusCode": 105,
568 "StatusMessage": "NodeList: Missing vnfc-type field"}
570 # Having been built now copy new file to correct file
571 shutil.copy(PlaybookDir + "/host_file.txt", HostFile)
572 cherrypy.log("Copying Generated host file to: " + HostFile)
574 if 'Timeout' in input_json:
575 timeout = int(input_json['Timeout'])
576 cherrypy.log("Timeout from API: " + str(timeout))
578 timeout = timeout_seconds
579 cherrypy.log("Timeout not passed from API using default: " + str(timeout))
581 EnvParam = input_json.get('EnvParameters', {})
582 LocalParam = input_json.get('LocalParameters', {})
583 FileParam = input_json.get('FileParameters', {})
584 callback_flag = input_json.get('CallBack', None)
586 # if AnsibleServer is not set to 'na' don't send AnsibleServer in PENDING response.
588 'PlaybookName': PlaybookName,
590 'NodeList': NodeList,
591 'HostGroupList': hostgrouplist,
592 'HostNameList': hostnamelist,
596 'EnvParameters': EnvParam,
597 'LocalParameters': LocalParam,
598 'FileParameters': FileParam,
599 'CallBack': callback_flag,
602 "StatusMessage": 'PENDING',
603 "ExpectedDuration": str(timeout) + "sec"
610 if AnsibleServer != 'na':
611 TestRecord[Id]['Result']["AnsibleServer"] = str(AnsibleServer),
613 cherrypy.log("Test_Record: " + str(TestRecord[Id]))
616 if TestRecord[Id]['FileParameters']:
617 for key in TestRecord[Id]['FileParameters']:
619 filecontent = TestRecord[Id]['FileParameters'][key]
620 f = open(PlaybookDir + "/" + filename, "w")
625 if os.path.exists(ANSIBLE_PATH + '/' + PlaybookName):
626 AnsiblePlaybookFail = False
628 if AnsiblePlaybookFail:
629 # if os.path.exists(PlaybookDir):
630 # shutil.rmtree (PlaybookDir)
631 cherrypy.log("AnsiblePlaybookFail")
633 return {"StatusCode": 101, "StatusMessage": "PLAYBOOK NOT FOUND"}
636 playbook_path = PlaybookDir
639 store_local_vars(playbook_path, Id)
641 # write some info out to files before running
643 f = open(playbook_path + "/User.txt", "a")
644 f.write(cherrypy.request.login)
647 f = open(playbook_path + "/PlaybookName.txt", "a")
648 f.write(PlaybookName)
651 f = open(playbook_path + "/PlaybookExDir.txt", "a")
652 f.write(PlaybookDir + "/" + PlayBookFunction)
655 f = open(playbook_path + "/JsonRequest.txt", "w")
656 f.write(json.dumps(input_json, indent=4, sort_keys=True))
659 # Check that HostFile exists
660 if not os.path.isfile(HostFile):
661 cherrypy.log("Inventory file Not Found: " + HostFile)
662 return {"StatusCode": 101, "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"}
664 # Cannot use thread because ansible module uses signals which are only supported in main thread.
665 # So use multiprocess with shared object
666 p = Process(target=RunAnsible_Playbook,
667 args=(callback, Id, HostFile, PlaybookDir + '/' + PlayBookFile,
668 NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction, ArchiveFlag))
670 ActiveProcess[Id] = p
671 return TestRecord[Id]['Result']
674 def handle_post_method(input_json, time_now):
675 cherrypy.log("Payload: " + str(input_json))
677 if 'Id' in input_json and 'PlaybookName' in input_json and 'EnvParameters' in input_json:
678 if input_json['Id'] not in TestRecord:
679 # check if Id exists in previous run directory, if so return error
680 Id = input_json['Id']
681 if glob.glob(ANSIBLE_TEMP + '/*_' + input_json['Id']):
682 cherrypy.log("Old directory found for ID: " + Id)
683 return {"StatusCode": 101, "StatusMessage": "TEST ID FILE ALREADY DEFINED"}
685 # if required it should be passed as an argument
686 EnvParameters = input_json.get('EnvParameters', {})
688 # The lines below are to test multiple EnvParameters being passed
689 # for i in EnvParameters:
690 # cherrypy.log("EnvParameter object: " + i)
691 # cherrypy.log(" EnvParameter Value: " + EnvParameters[ i ])
693 pnf_flag = EnvParameters.get("pnf-flag", "")
694 if pnf_flag == "true":
695 return process_pnf_playbook(input_json, Id, EnvParameters, time_now)
697 return process_vnf_playbook(input_json, Id, EnvParameters, time_now)
699 cherrypy.log("TEST ID ALREADY DEFINED")
700 return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
702 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"}
705 def handle_get_method(input_data):
706 # Verify we have a Type passed in GET request
707 if 'Type' not in input_data:
708 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
711 cherrypy.log("Request USER: " + cherrypy.request.login)
712 cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type'])
714 if 'LogRest' in input_data['Type']:
716 sys.stdout = open("/var/log/RestServer.log", "w")
718 # Just a debug to dump any records
719 if 'GetStatus' in input_data['Type']:
720 cherrypy.log("******** Dump Records **********")
721 if list(TestRecord.items()):
722 for id, record in list(TestRecord.items()):
723 cherrypy.log(" Id: " + id)
724 cherrypy.log("Record: " + str(record))
726 cherrypy.log(" No Records to dump")
728 if 'Id' in input_data and 'Type' in input_data:
729 if not ('GetResult' in input_data['Type'] or 'GetOutputLog' in input_data['Type'] or
730 'GetTheOutput' in input_data['Type'] or 'GetOutput' in input_data['Type'] or
731 'GetLog' in input_data['Type']):
732 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
734 if input_data['Id'] in TestRecord:
735 if 'GetResult' in input_data['Type']:
736 cherrypy.log(" ** GetResult for: " + str(input_data['Id']))
737 if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
738 # check if playbook is still running
739 while ActiveProcess[input_data['Id']].is_alive():
740 cherrypy.log("*** Playbook running returning PENDING for " + str(input_data['Id']))
741 # If still running return PENDING response
742 # if AnsibleServer != 'na':
743 # return {"StatusCode": 100, "StatusMessage": 'PENDING', "AnsibleServer": str(AnsibleServer)}
745 # return {"StatusCode": 100, "StatusMessage": 'PENDING'}
748 # cherrypy.log( "*** Request released " + input_data['Id'])
750 cherrypy.log(str(TestRecord[input_data['Id']]['Result']))
751 cherrypy.log("Output: " + str(TestRecord[input_data['Id']]['Output']))
752 cherrypy.log("StatusCode: " + str(TestRecord[input_data['Id']]['Result']['StatusCode']))
753 cherrypy.log("StatusMessage: " + str(TestRecord[input_data['Id']]['Result']['StatusMessage']))
755 # out_obj gets returned to GET request
756 if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
757 out_obj = TestRecord[input_data['Id']]['Result']['Results']
761 "StatusMessage": "FINISHED",
762 "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
763 "Version": TestRecord[input_data['Id']]["Version"],
764 "Duration": TestRecord[input_data['Id']]["Duration"],
765 "Output": TestRecord[input_data['Id']]["Output"]["Output"],
766 "Results": TestRecord[input_data['Id']]['Result']['Results']
768 if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
769 cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output']))
771 for key in out_obj["Results"]:
772 cherrypy.log("Output key: " + str(key))
773 if key in TestRecord[input_data['Id']]['Output']['Output']:
774 out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
776 cherrypy.log("***** GET RETURNING RESULTS Back ****")
777 cherrypy.log(str(out_obj))
779 elif 'GetStatus' in input_data['Type']:
780 cherrypy.log(" Dump Records")
781 for id, record in list(TestRecord.items()):
782 cherrypy.log(" id: " + id)
783 cherrypy.log(" Record:" + str(record))
784 elif 'GetTheOutput' in input_data['Type'] or 'GetOutput' in input_data['Type']:
785 if TestRecord[input_data['Id']]['Output'] == {} and getresults_block:
786 cherrypy.log("*** Request blocked " + input_data['Id'])
788 # while TestRecord[input_data['Id']]['Output'] == {} \
789 # or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
790 while ActiveProcess[input_data['Id']].is_alive():
793 cherrypy.log("*** Request released " + input_data['Id'])
795 cherrypy.log("Output: " + str(TestRecord[input_data['Id']]['Output']))
796 return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
797 elif 'GetOutputLog' in input_data['Type']:
798 cherrypy.log("GetOutputLog: processing.")
799 if glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id']):
800 id = input_data['Id']
801 cherrypy.log("Old directory found for ID: " + id)
802 run_dir = glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id'])
805 if os.path.exists(rdir + "/PlaybookExDir.txt"):
806 cherrypy.log("Found PlaybookExDir.txt file")
807 f = open(rdir + '/PlaybookExDir.txt', 'r')
808 playbookexdir = f.readline()
811 cherrypy.log("Id: " + id)
812 cherrypy.log("RunDir: " + rdir)
813 if os.path.exists(rdir + "/output.log"):
814 cherrypy.log("Found output.log file")
815 f = open(rdir + '/output.log', 'r')
816 output_log = f.readline()
820 cherrypy.log("Globglob failed:")
825 if TestRecord[input_data['Id']]['Log'] == '' and \
828 cherrypy.log("*** Request blocked " + input_data['Id'])
830 while TestRecord[input_data['Id']]['Log'] == '' \
831 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
834 cherrypy.log("*** Request released " + input_data['Id'])
836 cherrypy.log("Log:" + str(TestRecord[input_data['Id']]['Log']))
837 return {"Log": TestRecord[input_data['Id']]['Log']}
839 # Not in memory check for a file
840 if glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id']):
841 id = input_data['Id']
842 cherrypy.log("Old directory found for ID: " + id)
843 run_dir = glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id'])
846 if os.path.exists(rdir + "/PlaybookExDir.txt"):
847 cherrypy.log("Found PlaybookExDir.txt file")
848 f = open(rdir + '/PlaybookExDir.txt', 'r')
849 playbookexdir = f.readline()
852 cherrypy.log("Id: " + id)
853 cherrypy.log("RunDir: " + rdir)
854 if 'GetLog' 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 'GetOutputLog' in input_data['Type']:
862 if os.path.exists(rdir + "/output.log"):
863 cherrypy.log("Found output.log file")
864 f = open(rdir + '/output.log', 'r')
865 output_log = f.readline()
868 elif 'GetResult' in input_data['Type']:
869 if os.path.exists(rdir + "/PlaybookName.txt"):
870 cherrypy.log("Found PlaybookName.txt file")
871 f = open(rdir + '/PlaybookName.txt', 'r')
872 playbooknametxt = f.readline()
875 playbooknametxt = "NA"
877 # Add code to get other items not just output.log from files
878 if os.path.exists(rdir + "/log.file"):
879 cherrypy.log("Found log.file")
882 f = open(rdir + '/log.file', 'r')
886 out_results = out_results + line
887 elif "RECAP" in line:
888 out_results = out_results + line
889 recap_line = f.readline()
891 out_results = out_results + recap_line
892 recap_line = f.readline()
897 "StatusMessage": "FINISHED",
898 "PlaybookName": playbooknametxt,
899 "Version": "Version",
901 "Results": out_results
905 return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
907 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
909 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
912 def handle_delete_method(input_data):
913 cherrypy.log("***> in RestServer.DELETE")
914 cherrypy.log("Payload: " + str(input_data))
916 if input_data['Id'] in TestRecord:
917 if 'PENDING' not in TestRecord[input_data['Id']]['Result']:
918 cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
919 TestRecord.pop(input_data['Id'])
920 if input_data['Id'] in ActiveProcess:
921 ActiveProcess.pop(input_data['Id'])
922 return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
924 return {"StatusCode": 200, "StatusMessage": "PENDING"}
926 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
929 class TestManager(object):
931 @cherrypy.tools.json_out()
932 @cherrypy.tools.json_in()
933 @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
934 def Dispatch(self, **kwargs):
935 # Let cherrypy error handler deal with malformed requests
936 # No need for explicit error handler, we use default ones
938 time_now = datetime.datetime.utcnow()
940 # Erase old test results (2x timeout)
941 # Do cleanup too of ActiveProcess list and old Records - PAP
943 for key in TestRecord.copy():
944 cherrypy.log("LOOKING AT ALL TestRecords: " + str(key))
945 if key in ActiveProcess:
946 if not ActiveProcess[key].is_alive(): # Just to cleanup defunct processes
947 cherrypy.log("Not ActiveProcess for ID: " + str(key))
948 delta_time = (time_now - TestRecord[key]['Time']).seconds
949 if delta_time > 2 * TestRecord[key]['Timeout']:
950 cherrypy.log("DELETED HISTORY for ID: " + str(key))
951 if key in ActiveProcess:
952 if not ActiveProcess[key].is_alive():
953 ActiveProcess.pop(key)
954 cherrypy.log("DELETED ActiveProcess for ID: " + str(key))
955 # if os.path.exists(TestRecord[key]['Path']):
956 # don't remove run dirrectory
957 # shutil.rmtree (TestRecord[key]['Path'])
960 cherrypy.log("RestServer.Dispatch: " + cherrypy.request.method)
962 if 'POST' in cherrypy.request.method:
963 input_json = cherrypy.request.json
964 return handle_post_method(input_json, time_now)
965 elif 'GET' in cherrypy.request.method:
966 # Lets pause for a second just in case the request was just kicked off
969 input_data = parse_query_string(cherrypy.request.query_string)
970 return handle_get_method(input_data)
971 elif 'DELETE' in cherrypy.request.method:
972 input_data = parse_query_string(cherrypy.request.query_string)
973 return handle_delete_method(input_data)
976 if __name__ == '__main__':
980 config_file_path = "RestServer_config"
982 if not os.path.exists(config_file_path):
983 cherrypy.log('[INFO] The config file does not exist')
994 timeout_seconds = 'na'
999 getresults_block = False
1002 config_file = open(config_file_path, 'r')
1003 for config_line in config_file.readlines():
1004 if '#' not in config_line:
1005 if 'ip:' in config_line:
1006 ip = config_line.split(':')[1].strip()
1007 elif 'AnsibleServer:' in config_line:
1008 AnsibleServer = config_line.split(':')[1].strip()
1009 elif 'port:' in config_line:
1010 port = config_line.split(':')[1].strip()
1011 elif 'ksalt:' in config_line:
1012 salt = config_line.split(':')[1].strip()
1013 elif 'tls:' in config_line:
1014 tls = 'YES' in config_line.split(':')[1].strip().upper()
1015 elif 'auth:' in config_line:
1016 AUTH = 'YES' in config_line.split(':')[1].strip().upper()
1017 if tls and 'priv:' in config_line:
1018 priv = config_line.split(':')[1].strip()
1019 if tls and 'pub:' in config_line:
1020 pub = config_line.split(':')[1].strip()
1021 if tls and 'inter_cert:' in config_line:
1022 intermediate = config_line.split(':')[1].strip()
1023 if 'timeout_seconds' in config_line:
1024 timeout_seconds = int(config_line.split(':')[1].strip())
1025 if 'ansible_path' in config_line:
1026 ANSIBLE_PATH = config_line.split(':')[1].strip()
1027 if 'ansible_inv' in config_line:
1028 ANSIBLE_INV = config_line.split(':')[1].strip()
1029 if not os.path.exists(ANSIBLE_PATH + "/" + ANSIBLE_INV):
1030 print('[INFO] The ansible_inv file does not exist')
1032 if 'ansible_temp' in config_line:
1033 ANSIBLE_TEMP = config_line.split(':')[1].strip()
1034 if 'host' in config_line:
1035 host = config_line.split(':')[1].strip()
1036 if 'users' in config_line:
1037 users = config_line.split(':')[1].strip()
1038 if 'getresults_block' in config_line:
1039 getresults_block = 'YES' in config_line.split(':')[1].strip().upper()
1040 if 'from_files' in config_line:
1041 from_files = 'YES' in config_line.split(':')[1].strip().upper()
1049 'response.timeout': 5400,
1050 'server.socket_host': ip,
1051 'server.socket_port': int(port),
1052 'server.protocol_version': 'HTTP/1.1'
1057 # Use pythons built-in SSL
1058 cherrypy.server.ssl_module = 'builtin'
1060 # Point to certificate files
1062 if not os.path.exists(pub):
1063 cherrypy.log('[INFO] The public certificate does not exist')
1066 if not os.path.exists(priv):
1067 cherrypy.log('[INFO] The private key does not exist')
1070 if not os.path.exists(intermediate):
1071 cherrypy.log('[INFO] The intermediate certificate does not exist')
1074 cherrypy.server.ssl_certificate = pub
1075 cherrypy.server.ssl_certificate_chain = intermediate
1076 cherrypy.server.ssl_private_key = priv
1079 # Read in and build user dictionary
1080 if not os.path.exists(users):
1081 cherrypy.log('[INFO] The users file does not exist: ' + users)
1084 user_file = open(users, 'r')
1085 for config_line in user_file.readlines():
1086 if '#' not in config_line:
1087 uid = config_line.split(':')[0].strip()
1088 pw = config_line.split(':')[1].strip()
1089 userpassdict[uid] = pw
1093 {'tools.auth_basic.on': True,
1094 'tools.auth_basic.realm': 'earth',
1095 'tools.auth_basic.checkpassword': validate_password
1099 application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1101 application = cherrypy.tree.mount(TestManager(), '/')
1103 cherrypy.config.update({
1104 'log.access_file': "/var/log/RestServer.access"
1106 accessLogName = "/var/log/RestServer.access"
1107 applicationLogName = "/var/log/RestServer.log"
1108 cherrypy.config.update(global_conf)
1110 log = application.log
1112 log.access_file = ""
1113 from logging import handlers
1115 applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1116 accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1119 applicationLogFileHandler.setLevel(logging.DEBUG)
1120 log.error_log.addHandler(applicationLogFileHandler)
1121 log.access_log.addHandler(accessLogFileHandler)
1125 cherrypy.engine.start()
1126 cherrypy.engine.block()