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 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 & 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 TestRecord.items():
722 for id, record in 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 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']:
792 cherrypy.log("*** Request released " + input_data['Id'])
794 cherrypy.log("Output: " + str(TestRecord[input_data['Id']]['Output']))
795 return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
796 elif 'GetOutputLog' in input_data['Type']:
797 cherrypy.log("GetOutputLog: processing.")
798 if glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id']):
799 id = input_data['Id']
800 cherrypy.log("Old directory found for ID: " + id)
801 run_dir = glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id'])
804 if os.path.exists(rdir + "/PlaybookExDir.txt"):
805 cherrypy.log("Found PlaybookExDir.txt file")
806 f = open(rdir + '/PlaybookExDir.txt', 'r')
807 playbookexdir = f.readline()
810 cherrypy.log("Id: " + id)
811 cherrypy.log("RunDir: " + rdir)
812 if os.path.exists(rdir + "/output.log"):
813 cherrypy.log("Found output.log file")
814 f = open(rdir + '/output.log', 'r')
815 output_log = f.readline()
819 cherrypy.log("Globglob failed:")
824 if TestRecord[input_data['Id']]['Log'] == '' and \
827 cherrypy.log("*** Request blocked " + input_data['Id'])
829 while TestRecord[input_data['Id']]['Log'] == '' \
830 or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
833 cherrypy.log("*** Request released " + input_data['Id'])
835 cherrypy.log("Log:" + str(TestRecord[input_data['Id']]['Log']))
836 return {"Log": TestRecord[input_data['Id']]['Log']}
838 # Not in memory check for a file
839 if glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id']):
840 id = input_data['Id']
841 cherrypy.log("Old directory found for ID: " + id)
842 run_dir = glob.glob(ANSIBLE_TEMP + '/*_' + input_data['Id'])
845 if os.path.exists(rdir + "/PlaybookExDir.txt"):
846 cherrypy.log("Found PlaybookExDir.txt file")
847 f = open(rdir + '/PlaybookExDir.txt', 'r')
848 playbookexdir = f.readline()
851 cherrypy.log("Id: " + id)
852 cherrypy.log("RunDir: " + rdir)
853 if 'GetLog' in input_data['Type']:
854 if os.path.exists(rdir + "/output.log"):
855 cherrypy.log("Found output.log file")
856 f = open(rdir + '/output.log', 'r')
857 output_log = f.readline()
860 elif 'GetOutputLog' in input_data['Type']:
861 if os.path.exists(rdir + "/output.log"):
862 cherrypy.log("Found output.log file")
863 f = open(rdir + '/output.log', 'r')
864 output_log = f.readline()
867 elif 'GetResult' in input_data['Type']:
868 if os.path.exists(rdir + "/PlaybookName.txt"):
869 cherrypy.log("Found PlaybookName.txt file")
870 f = open(rdir + '/PlaybookName.txt', 'r')
871 playbooknametxt = f.readline()
874 playbooknametxt = "NA"
876 # Add code to get other items not just output.log from files
877 if os.path.exists(rdir + "/log.file"):
878 cherrypy.log("Found log.file")
881 f = open(rdir + '/log.file', 'r')
885 out_results = out_results + line
886 elif "RECAP" in line:
887 out_results = out_results + line
888 recap_line = f.readline()
890 out_results = out_results + recap_line
891 recap_line = f.readline()
896 "StatusMessage": "FINISHED",
897 "PlaybookName": playbooknametxt,
898 "Version": "Version",
900 "Results": out_results
904 return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
906 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
908 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
911 def handle_delete_method(input_data):
912 cherrypy.log("***> in RestServer.DELETE")
913 cherrypy.log("Payload: " + str(input_data))
915 if input_data['Id'] in TestRecord:
916 if 'PENDING' not in TestRecord[input_data['Id']]['Result']:
917 cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
918 TestRecord.pop(input_data['Id'])
919 if input_data['Id'] in ActiveProcess:
920 ActiveProcess.pop(input_data['Id'])
921 return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
923 return {"StatusCode": 200, "StatusMessage": "PENDING"}
925 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
928 class TestManager(object):
930 @cherrypy.tools.json_out()
931 @cherrypy.tools.json_in()
932 @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
933 def Dispatch(self, **kwargs):
934 # Let cherrypy error handler deal with malformed requests
935 # No need for explicit error handler, we use default ones
937 time_now = datetime.datetime.utcnow()
939 # Erase old test results (2x timeout)
940 # Do cleanup too of ActiveProcess list and old Records - PAP
942 for key in TestRecord.copy():
943 cherrypy.log("LOOKING AT ALL TestRecords: " + str(key))
944 if key in ActiveProcess:
945 if not ActiveProcess[key].is_alive(): # Just to cleanup defunct processes
946 cherrypy.log("Not ActiveProcess for ID: " + str(key))
947 delta_time = (time_now - TestRecord[key]['Time']).seconds
948 if delta_time > 2 * TestRecord[key]['Timeout']:
949 cherrypy.log("DELETED HISTORY for ID: " + str(key))
950 if key in ActiveProcess:
951 if not ActiveProcess[key].is_alive():
952 ActiveProcess.pop(key)
953 cherrypy.log("DELETED ActiveProcess for ID: " + str(key))
954 # if os.path.exists(TestRecord[key]['Path']):
955 # don't remove run dirrectory
956 # shutil.rmtree (TestRecord[key]['Path'])
959 cherrypy.log("RestServer.Dispatch: " + cherrypy.request.method)
961 if 'POST' in cherrypy.request.method:
962 input_json = cherrypy.request.json
963 return handle_post_method(input_json, time_now)
964 elif 'GET' in cherrypy.request.method:
965 # Lets pause for a second just in case the request was just kicked off
968 input_data = parse_query_string(cherrypy.request.query_string)
969 return handle_get_method(input_data)
970 elif 'DELETE' in cherrypy.request.method:
971 input_data = parse_query_string(cherrypy.request.query_string)
972 return handle_delete_method(input_data)
975 if __name__ == '__main__':
979 config_file_path = "RestServer_config"
981 if not os.path.exists(config_file_path):
982 cherrypy.log('[INFO] The config file does not exist')
993 timeout_seconds = 'na'
998 getresults_block = False
1001 config_file = open(config_file_path, 'r')
1002 for config_line in config_file.readlines():
1003 if '#' not in config_line:
1004 if 'ip:' in config_line:
1005 ip = config_line.split(':')[1].strip()
1006 elif 'AnsibleServer:' in config_line:
1007 AnsibleServer = config_line.split(':')[1].strip()
1008 elif 'port:' in config_line:
1009 port = config_line.split(':')[1].strip()
1010 elif 'ksalt:' in config_line:
1011 salt = config_line.split(':')[1].strip()
1012 elif 'tls:' in config_line:
1013 tls = 'YES' in config_line.split(':')[1].strip().upper()
1014 elif 'auth:' in config_line:
1015 AUTH = 'YES' in config_line.split(':')[1].strip().upper()
1016 if tls and 'priv:' in config_line:
1017 priv = config_line.split(':')[1].strip()
1018 if tls and 'pub:' in config_line:
1019 pub = config_line.split(':')[1].strip()
1020 if tls and 'inter_cert:' in config_line:
1021 intermediate = config_line.split(':')[1].strip()
1022 if 'timeout_seconds' in config_line:
1023 timeout_seconds = int(config_line.split(':')[1].strip())
1024 if 'ansible_path' in config_line:
1025 ANSIBLE_PATH = config_line.split(':')[1].strip()
1026 if 'ansible_inv' in config_line:
1027 ANSIBLE_INV = config_line.split(':')[1].strip()
1028 if not os.path.exists(ANSIBLE_PATH + "/" + ANSIBLE_INV):
1029 print '[INFO] The ansible_inv file does not exist'
1031 if 'ansible_temp' in config_line:
1032 ANSIBLE_TEMP = config_line.split(':')[1].strip()
1033 if 'host' in config_line:
1034 host = config_line.split(':')[1].strip()
1035 if 'users' in config_line:
1036 users = config_line.split(':')[1].strip()
1037 if 'getresults_block' in config_line:
1038 getresults_block = 'YES' in config_line.split(':')[1].strip().upper()
1039 if 'from_files' in config_line:
1040 from_files = 'YES' in config_line.split(':')[1].strip().upper()
1048 'response.timeout': 5400,
1049 'server.socket_host': ip,
1050 'server.socket_port': int(port),
1051 'server.protocol_version': 'HTTP/1.1'
1056 # Use pythons built-in SSL
1057 cherrypy.server.ssl_module = 'builtin'
1059 # Point to certificate files
1061 if not os.path.exists(pub):
1062 cherrypy.log('[INFO] The public certificate does not exist')
1065 if not os.path.exists(priv):
1066 cherrypy.log('[INFO] The private key does not exist')
1069 if not os.path.exists(intermediate):
1070 cherrypy.log('[INFO] The intermediate certificate does not exist')
1073 cherrypy.server.ssl_certificate = pub
1074 cherrypy.server.ssl_certificate_chain = intermediate
1075 cherrypy.server.ssl_private_key = priv
1078 # Read in and build user dictionary
1079 if not os.path.exists(users):
1080 cherrypy.log('[INFO] The users file does not exist: ' + users)
1083 user_file = open(users, 'r')
1084 for config_line in user_file.readlines():
1085 if '#' not in config_line:
1086 uid = config_line.split(':')[0].strip()
1087 pw = config_line.split(':')[1].strip()
1088 userpassdict[uid] = pw
1092 {'tools.auth_basic.on': True,
1093 'tools.auth_basic.realm': 'earth',
1094 'tools.auth_basic.checkpassword': validate_password
1098 application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1100 application = cherrypy.tree.mount(TestManager(), '/')
1102 cherrypy.config.update({
1103 'log.access_file': "/var/log/RestServer.access"
1105 accessLogName = "/var/log/RestServer.access"
1106 applicationLogName = "/var/log/RestServer.log"
1107 cherrypy.config.update(global_conf)
1109 log = application.log
1111 log.access_file = ""
1112 from logging import handlers
1114 applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1115 accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1118 applicationLogFileHandler.setLevel(logging.DEBUG)
1119 log.error_log.addHandler(applicationLogFileHandler)
1120 log.access_log.addHandler(accessLogFileHandler)
1124 cherrypy.engine.start()
1125 cherrypy.engine.block()