| ''' |
| /*- |
| * ============LICENSE_START======================================================= |
| * ONAP : APPC |
| * ================================================================================ |
| * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. |
| * ================================================================================ |
| * Copyright (C) 2017 Amdocs |
| * ============================================================================= |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * ECOMP is a trademark and service mark of AT&T Intellectual Property. |
| * ============LICENSE_END========================================================= |
| */ |
| ''' |
| |
| import time, datetime, json, os, sys, subprocess, re |
| import uuid |
| import tarfile |
| import shutil |
| import glob |
| import crypt |
| |
| import requests |
| |
| import cherrypy |
| from cherrypy.lib.httputil import parse_query_string |
| from cherrypy.lib import auth_basic |
| |
| from multiprocessing import Process, Manager |
| |
| from AnsibleModule import ansibleSysCall |
| from BuildHostFile import buildHostsSysCall |
| |
| from os import listdir |
| from os.path import isfile, join |
| |
| TestRecord = Manager().dict() |
| ActiveProcess = {} |
| |
| def validate_password(realm, username, password): |
| comp = crypt.crypt(password, salt) |
| if username in userpassdict and userpassdict[username] == comp: |
| return True |
| return False |
| |
| def sys_call (cmd): |
| p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
| output = p.stdout.readlines() |
| retval = p.wait() |
| if len (output) > 0: |
| for i in range(len(output)): |
| output[i] = output[i].strip() |
| return retval, output |
| |
| def callback (Id, Result, Output, Log, returncode): |
| |
| print "***> in RestServer.callback" |
| |
| if Id in TestRecord: |
| time_now = datetime.datetime.utcnow() |
| delta_time = (time_now - TestRecord[Id]['Time']).total_seconds() |
| Result['PlaybookName'] = TestRecord[Id]['PlaybookName'] |
| Result['Version'] = TestRecord[Id]['Version'] |
| if returncode == 137: |
| Result['StatusCode'] = 500 |
| Result['StatusMessage'] = "TERMINATED" |
| else: |
| Result['StatusCode'] = 200 |
| Result['StatusMessage'] = "FINISHED" |
| |
| # Need to update the whole data structure for key=Id otherwise Manager is not updated |
| TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'], |
| 'Version': TestRecord[Id]['Version'], |
| 'NodeList': TestRecord[Id]['NodeList'], |
| 'HostGroupList': TestRecord[Id]['HostGroupList'], |
| 'HostNameList': TestRecord[Id]['HostNameList'], |
| 'Time': TestRecord[Id]['Time'], |
| 'Timeout': TestRecord[Id]['Timeout'], |
| 'Duration': str(delta_time), |
| 'EnvParameters': TestRecord[Id]['EnvParameters'], |
| 'LocalParameters': TestRecord[Id]['LocalParameters'], |
| 'FileParameters': TestRecord[Id]['FileParameters'], |
| 'CallBack': TestRecord[Id]['CallBack'], |
| 'Result': Result, |
| 'Log': Log, |
| 'Output': Output, |
| 'Path': TestRecord[Id]['Path'], |
| 'Mandatory': TestRecord[Id]['Path']} |
| |
| if not TestRecord[Id]['CallBack'] == None: |
| |
| # Posting results to callback server |
| |
| data = {"StatusCode": 200, |
| "StatusMessage": "FINISHED", |
| "PlaybookName": TestRecord[Id]["PlaybookName"], |
| "Version": TestRecord[Id]["Version"], |
| "Duration": TestRecord[Id]["Duration"], |
| "Results": TestRecord[Id]['Result']['Results']} |
| |
| cherrypy.log("CALLBACK: TestRecord[Id]['Output']['Output']:", str(TestRecord[Id]['Output']['Output'])) |
| cherrypy.log("CALLBACK: Results:", str(data["Results"])) |
| |
| if not TestRecord[Id]['Output']['Output'] == {}: |
| for key in data["Results"]: |
| if key in TestRecord[Id]['Output']['Output']: |
| data["Results"][key]["Output"] = TestRecord[Id]['Output']['Output'][key] |
| |
| print " Posting to", TestRecord[Id]['CallBack'] |
| |
| s = requests.Session() |
| r = s.post(TestRecord[Id]['CallBack'], data = json.dumps(data), |
| headers = {'content-type': 'application/json'}) |
| print " Response", r.status_code, r.text |
| |
| def RunAnsible_Playbook (callback, Id, Inventory, Playbook, NodeList, TestRecord, |
| Path, ArchiveFlag): |
| |
| print "***> in RestServer.RunAnsible_Playbook" |
| |
| # Run test in playbook for given target |
| Result = '' |
| |
| retval, log, returncode = ansibleSysCall (Inventory, Playbook, NodeList, |
| TestRecord[Id]['Mandatory'], |
| TestRecord[Id]['EnvParameters'], |
| TestRecord[Id]['LocalParameters'], |
| TestRecord[Id]['Timeout'], |
| Path) |
| |
| |
| cherrypy.log("Return code:" + str(returncode)) |
| cherrypy.log("Return val:" + str(retval)) |
| |
| Log = ''.join(log) |
| #Output = {'Output': {}} |
| Output = {} |
| |
| onlyfiles = [f for f in listdir(Path) |
| if isfile(join(Path, f))] |
| |
| cherrypy.log("Checking for results.txt files: ") |
| for file in onlyfiles: |
| if "results.txt" in file: |
| # if file.endswith("results.txt"): |
| cherrypy.log("results file: " + file) |
| f = open(Path + "/" + file, "r") |
| resultsData = f.read() # Not to pass vnf instance name |
| OutputP = json.loads(resultsData) |
| Output['Output'] = OutputP |
| cherrypy.log("Output = " + str(Output['Output'])) |
| #Output['Output'][key] = f.read() # To pass vnf instance name |
| f.close() |
| |
| if Output == {}: |
| Output = {'Output': {}} |
| |
| Result = {'Results': {}} |
| if 'could not be found' in Log: |
| Result['Results'] = {"StatusCode": 101, |
| "StatusMessage": "PLAYBOOK NOT FOUND"} |
| if returncode == 137: |
| Result['Results'] = {"StatusCode": 500, |
| "StatusMessage": "TERMINATED"} |
| |
| elif TestRecord[Id]['NodeList'] == []: |
| |
| host_index = None |
| |
| if 'TargetNode' in TestRecord[Id]['EnvParameters']: |
| targetlist = TestRecord[Id]['EnvParameters']['TargetNode'].split(' ') |
| else: |
| targetlist = ["localhost"] |
| |
| for key in retval: |
| for i in range (len(targetlist)): |
| if key in targetlist[i]: |
| host_index = i |
| |
| if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \ |
| int(retval[key][3]) == 0: |
| |
| if host_index: |
| Result['Results'][targetlist[host_index]] = \ |
| {"GroupName": 'na', "StatusCode": 200, \ |
| "StatusMessage": "SUCCESS"} |
| else: |
| Result['Results'][key] = \ |
| {"GroupName": 'na', "StatusCode": 200, \ |
| "StatusMessage": "SUCCESS"} |
| elif int(retval[key][2]) > 0: |
| if host_index: |
| Result['Results'][targetlist[host_index]] = \ |
| {"GroupName": 'na', "StatusCode": 400, \ |
| "StatusMessage": "NOT REACHABLE"} |
| else: |
| Result['Results'][key] = \ |
| {"GroupName": 'na', "StatusCode": 400, \ |
| "StatusMessage": "NOT REACHABLE"} |
| elif int(retval[key][3]) > 0: |
| if host_index: |
| Result['Results'][targetlist[host_index]] = \ |
| {"GroupName": 'na', "StatusCode": 400, \ |
| "StatusMessage": "FAILURE"} |
| else: |
| Result['Results'][key] = \ |
| {"GroupName": 'na', "StatusCode": 400, \ |
| "StatusMessage": "FAILURE"} |
| else: |
| |
| for key in retval: |
| |
| if len(TestRecord[Id]['HostNameList']) > 0: |
| |
| host_index = [] |
| for i in range (len(TestRecord[Id]['HostNameList'])): |
| if key in TestRecord[Id]['HostNameList'][i]: |
| host_index.append(i) |
| |
| if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \ |
| int(retval[key][3]) == 0: |
| |
| if len(host_index) > 0: |
| Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \ |
| {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]], |
| "StatusCode": 200, "StatusMessage": "SUCCESS"} |
| |
| for i in range (1, len(host_index)): |
| Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\ |
| "," + TestRecord[Id]['HostGroupList'][host_index[i]] |
| else: |
| Result['Results'][key] = \ |
| {"GroupName": key, |
| "StatusCode": 200, "StatusMessage": "SUCCESS"} |
| |
| elif int(retval[key][2]) > 0: |
| |
| if len(host_index) > 0: |
| Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \ |
| {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]], |
| "StatusCode": 400, "StatusMessage": "NOT REACHABLE"} |
| |
| for i in range (1, len(host_index)): |
| Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\ |
| "," + TestRecord[Id]['HostGroupList'][host_index[i]] |
| else: |
| Result['Results'][key] = \ |
| {"GroupName": key, |
| "StatusCode": 200, "StatusMessage": "NOT REACHABLE"} |
| |
| elif int(retval[key][3]) > 0: |
| |
| if len(host_index) > 0: |
| Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \ |
| {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]], |
| "StatusCode": 400, "StatusMessage": "FAILURE"} |
| |
| for i in range (1, len(host_index)): |
| Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\ |
| "," + TestRecord[Id]['HostGroupList'][host_index[i]] |
| else: |
| Result['Results'][key] = \ |
| {"GroupName": key, |
| "StatusCode": 200, "StatusMessage": "FAILURE"} |
| else: |
| host_index = None |
| for i in range (len(TestRecord[Id]['NodeList'])): |
| if key in TestRecord[Id]['NodeList'][i]: |
| host_index = i |
| |
| if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \ |
| int(retval[key][3]) == 0: |
| Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \ |
| {"GroupName": 'na', "StatusCode": 200, \ |
| "StatusMessage": "SUCCESS"} |
| elif int(retval[key][2]) > 0: |
| Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \ |
| {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"} |
| elif int(retval[key][3]) > 0: |
| Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \ |
| {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"} |
| |
| cherrypy.log("TESTRECORD: " + str(TestRecord[Id])) |
| #cherrypy.log("Output: " + str(Output)) |
| callback (Id, Result, Output, Log, returncode) |
| |
| class TestManager (object): |
| |
| @cherrypy.expose |
| @cherrypy.tools.json_out() |
| @cherrypy.tools.json_in() |
| @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE']) |
| |
| def Dispatch(self, **kwargs): |
| |
| # Let cherrypy error handler deal with malformed requests |
| # No need for explicit error handler, we use default ones |
| |
| time_now = datetime.datetime.utcnow() |
| |
| # Erase old test results (2x timeout) |
| # Do cleanup too of ActiveProcess list and old Records - PAP |
| if TestRecord: |
| for key in TestRecord.copy(): |
| cherrypy.log( "LOOKING AT ALL TestRecords: " + str(key)) |
| if key in ActiveProcess: |
| if not ActiveProcess[key].is_alive(): # Just to cleanup defunct processes |
| cherrypy.log( "Not ActiveProcess for ID: " + str(key)) |
| delta_time = (time_now - TestRecord[key]['Time']).seconds |
| if delta_time > 2*TestRecord[key]['Timeout']: |
| cherrypy.log( "DELETED HISTORY for ID: " + str(key)) |
| if key in ActiveProcess: |
| if not ActiveProcess[key].is_alive(): |
| ActiveProcess.pop (key) |
| cherrypy.log( "DELETED ActiveProcess for ID: " + str(key)) |
| #if os.path.exists(TestRecord[key]['Path']): |
| # don't remove run dirrectory |
| #shutil.rmtree (TestRecord[key]['Path']) |
| del TestRecord[key] |
| |
| cherrypy.log("RestServer.Dispatch: " + cherrypy.request.method) |
| |
| |
| if 'POST' in cherrypy.request.method: |
| |
| input_json = cherrypy.request.json |
| cherrypy.log("Payload: " + str(input_json)) |
| |
| if 'Id' in input_json and 'PlaybookName' in input_json and 'EnvParameters' in input_json: |
| |
| if True: |
| |
| if not input_json['Id'] in TestRecord: |
| # check if Id exists in previous run dirctory |
| # if so retun error |
| s_cmd = 'ls ' + ansible_temp + '/*_' + input_json['Id'] |
| #if subprocess.check_output([s_cmd, ]): |
| Id = input_json['Id'] |
| if glob.glob( ansible_temp + '/*_' + input_json['Id']): |
| cherrypy.log("Old directory found for ID: " + Id) |
| return {"StatusCode": 101, "StatusMessage": "TEST ID FILE ALREADY DEFINED"} |
| |
| PlaybookName = input_json['PlaybookName'] |
| # if required it should be passed as an argument |
| EnvParameters = input_json['EnvParameters'] |
| |
| # The lines below are to test multiple EnvParameters being passed |
| #for i in EnvParameters: |
| # cherrypy.log("EnvParameter object: " + i) |
| # cherrypy.log(" EnvParameter Value: " + EnvParameters[ i ]) |
| |
| # Now get things out of EnvParameters |
| VNF_instance = None |
| VNF_instance = EnvParameters.get('vnf_instance') |
| |
| # Get Version if present |
| version = None |
| if 'Version' in input_json: |
| version = input_json['Version'] |
| |
| # GetInventoryNames |
| HaveNodeList = False |
| HaveInventoryNames = False |
| inventory_names = None |
| if 'InventoryNames' in input_json: |
| inventory_names = input_json['InventoryNames'] |
| HaveInventoryNames = True |
| |
| #AnsibleInvFail = True |
| AnsiblePlaybookFail = True |
| |
| LocalNodeList = None |
| |
| str_uuid = str (uuid.uuid4()) |
| |
| |
| VnfType= PlaybookName.split("/")[0] |
| if auth: |
| cherrypy.log( "Request USER : " + cherrypy.request.login) |
| cherrypy.log( "Request Decode: ID " + Id) |
| cherrypy.log( "Request Decode: VnfType " + VnfType) |
| cherrypy.log( "Request Decode: EnvParameters " + json.dumps(EnvParameters)) |
| |
| # Verify VNF_instance was passed in EnvParameters |
| if VNF_instance != None: |
| cherrypy.log( "Request Decode: VnfInstance " + VNF_instance) |
| else: |
| cherrypy.log( "StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED" ) |
| return {"StatusCode": 107, |
| "StatusMessage": "VNF_instance NOT PROVIDED"} |
| |
| if inventory_names != None: |
| cherrypy.log( "Request Decode: Inventory Names " + inventory_names) |
| else: |
| cherrypy.log( "Request Decode: Inventory Names " + "Not provided") |
| |
| cherrypy.log( "Request Decode: PlaybookName " + PlaybookName) |
| PlayBookFunction = PlaybookName.rsplit("/",2)[1] |
| PlayBookFile = PlayBookFunction + "/site.yml" |
| cherrypy.log( "Request Decode: PlaybookFunction " + PlayBookFunction) |
| cherrypy.log( "Request Decode: Playbook file " + PlayBookFile) |
| |
| BaseDir = ansible_path + "/" + PlaybookName.rsplit("/",1)[0] |
| CopyDir = ansible_path + "/" + PlaybookName.rsplit("/",2)[0] |
| cherrypy.log( "Request Decode: Basedir " + BaseDir) |
| cherrypy.log( "Request Decode: Copydir " + CopyDir) |
| |
| |
| PlaybookDir = ansible_temp + "/" + \ |
| VNF_instance + "_" + str_uuid + "_" + str(Id) |
| |
| # AnsibleInv is the directory where the host file to be run exsists |
| AnsibleInv = ansible_path + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance |
| ArchiveFlag = False |
| |
| # Create base run directory if it doesn't exist |
| if not os.path.exists(ansible_temp): |
| cherrypy.log( "Creating Base Run Directory: " + ansible_temp) |
| os.makedirs(ansible_temp) |
| |
| if not os.path.exists( CopyDir ): |
| cherrypy.log("Playbook Not Found") |
| return {"StatusCode": 101, |
| "StatusMessage": "PLAYBOOK NOT FOUND"} |
| |
| # copy static playbook dir to run dir |
| cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir) |
| shutil.copytree(CopyDir, PlaybookDir) |
| cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;" |
| cmd="/usr/bin/find " + PlaybookDir + " -exec chmod +rx {} \;" |
| sys_call(cmd) |
| cherrypy.log(cmd) |
| |
| cherrypy.log( "PlaybookDir: " + PlaybookDir) |
| cherrypy.log( "AnsibleInv: " + AnsibleInv) |
| |
| # Process inventory file for target |
| |
| hostgrouplist = [] |
| hostnamelist = [] |
| |
| NodeList = [] |
| if 'NodeList' in input_json: |
| NodeList = input_json['NodeList'] |
| |
| cherrypy.log("NodeList: " + str(NodeList)); |
| |
| # if NodeList empty |
| if NodeList == []: |
| cherrypy.log( "*** NodeList - Empty ***") |
| |
| else: |
| HaveNodeList = True |
| |
| ############################################################################### |
| ##### Host file processing ########################### |
| ##### 1. Use file delivered with playbook ########################### |
| ##### 2. If HostNames + NodeList generate and use ########################### |
| ############################################################################### |
| |
| #Verify inventory directory exists |
| path = PlaybookDir + "/inventory/" |
| if not os.path.isdir(path): |
| cherrypy.log ("Inventory directory %s does not exist - create it" % path) |
| try: |
| os.mkdir(path) |
| except OSError: |
| cherrypy.log ("Creation of the directory %s failed" % path) |
| else: |
| cherrypy.log ("Successfully created the directory %s " % path) |
| |
| #location of host file - Default |
| HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts" |
| cherrypy.log("HostFile: " + HostFile) |
| |
| # if NodeList and InventoryNames need to build host file |
| if HaveInventoryNames & HaveNodeList: |
| cherrypy.log("Build host file from NodeList") |
| ret = buildHostsSysCall (input_json, PlaybookDir, inventory_names) |
| if (ret < 0): |
| cherrypy.log("Returning Error: Not running Playbook") |
| return {"StatusCode": 105, |
| "StatusMessage": "NodeList: Missing vnfc-type field"} |
| |
| # Having been built now copy new file to correct file |
| shutil.copy(PlaybookDir + "/host_file.txt", HostFile) |
| cherrypy.log("Copying Generated host file to: " + HostFile) |
| |
| timeout = timeout_seconds |
| if 'Timeout' in input_json: |
| timeout = int (input_json['Timeout']) |
| cherrypy.log("Timeout from API: " + str(timeout)) |
| |
| else: |
| cherrypy.log("Timeout not passed from API using default: " + str(timeout)) |
| |
| EnvParam = {} |
| if 'EnvParameters' in input_json: |
| EnvParam = input_json['EnvParameters'] |
| |
| LocalParam = {} |
| if 'LocalParameters' in input_json: |
| LocalParam = input_json['LocalParameters'] |
| |
| FileParam = {} |
| if 'FileParameters' in input_json: |
| FileParam = input_json['FileParameters'] |
| |
| callback_flag = None |
| if 'CallBack' in input_json: |
| callback_flag = input_json['CallBack'] |
| |
| # if AnsibleServer is not set to 'na' don't send AnsibleServer in PENDING responce. |
| if AnsibleServer != 'na': |
| TestRecord[Id] = {'PlaybookName': PlaybookName, |
| 'Version': version, |
| 'NodeList': NodeList, |
| 'HostGroupList': hostgrouplist, |
| 'HostNameList': hostnamelist, |
| 'Time': time_now, |
| 'Duration': timeout, |
| 'Timeout': timeout, |
| 'EnvParameters': EnvParam, |
| 'LocalParameters': LocalParam, |
| 'FileParameters': FileParam, |
| 'CallBack': callback_flag, |
| 'Result': {"StatusCode": 100, |
| "StatusMessage": 'PENDING', |
| "AnsibleServer": str(AnsibleServer), |
| "ExpectedDuration": str(timeout) + "sec"}, |
| 'Log': '', |
| 'Output': {}, |
| 'Path': PlaybookDir, |
| 'Mandatory': None} |
| else: |
| TestRecord[Id] = {'PlaybookName': PlaybookName, |
| 'Version': version, |
| 'NodeList': NodeList, |
| 'HostGroupList': hostgrouplist, |
| 'HostNameList': hostnamelist, |
| 'Time': time_now, |
| 'Duration': timeout, |
| 'Timeout': timeout, |
| 'EnvParameters': EnvParam, |
| 'LocalParameters': LocalParam, |
| 'FileParameters': FileParam, |
| 'CallBack': callback_flag, |
| 'Result': {"StatusCode": 100, |
| "StatusMessage": 'PENDING', |
| "ExpectedDuration": str(timeout) + "sec"}, |
| 'Log': '', |
| 'Output': {}, |
| 'Path': PlaybookDir, |
| 'Mandatory': None} |
| |
| cherrypy.log("Test_Record: " + str(TestRecord[Id])) |
| # Write files |
| |
| if not TestRecord[Id]['FileParameters'] == {}: |
| for key in TestRecord[Id]['FileParameters']: |
| filename = key |
| filecontent = TestRecord[Id]['FileParameters'][key] |
| f = open(PlaybookDir + "/" + filename, "w") |
| f.write(filecontent) |
| f.close() |
| |
| |
| # Process playbook |
| if os.path.exists( ansible_path + '/' + PlaybookName): |
| AnsiblePlaybookFail = False |
| |
| if AnsiblePlaybookFail: |
| #if os.path.exists(PlaybookDir): |
| #shutil.rmtree (PlaybookDir) |
| del TestRecord[Id] |
| return {"StatusCode": 101, |
| "StatusMessage": "PLAYBOOK NOT FOUND"} |
| else: |
| |
| # Test EnvParameters |
| playbook_path = PlaybookDir |
| |
| # Store local vars |
| if not os.path.exists(playbook_path + "/vars"): |
| os.mkdir(playbook_path + "/vars") |
| if not os.path.isfile(playbook_path + "/vars/defaults.yml"): |
| os.mknod(playbook_path + "/vars/defaults.yml") |
| |
| ################################################### |
| # PAP |
| #write local parameters passed into defaults.yml |
| # PAP |
| f = open(playbook_path + "/vars/defaults.yml","a") |
| #for id, record in TestRecord.items(): |
| print TestRecord[Id]['LocalParameters'] |
| local_parms = TestRecord[Id]['LocalParameters'] |
| for key, value in local_parms.items(): |
| f.write(key +"=" + value + "\n"); |
| f.close() |
| ################################################### |
| |
| for key in TestRecord[Id]['LocalParameters']: |
| host_index = [] |
| for i in range(len(TestRecord[Id]['HostNameList'])): |
| if key in TestRecord[Id]['HostNameList'][i]: |
| host_index.append(i) |
| if len(host_index) == 0: |
| for i in range(len(TestRecord[Id]['HostGroupList'])): |
| if key in TestRecord[Id]['HostGroupList'][i]: |
| host_index.append(i) |
| if len(host_index) > 0: |
| for i in range(len(host_index)): |
| f = open(playbook_path + "/vars/" + |
| TestRecord[Id]['HostNameList'][host_index[i]] + |
| ".yml", "a") |
| for param in TestRecord[Id]['LocalParameters'][key]: |
| f.write(param + ": " + |
| str (TestRecord[Id]['LocalParameters'][key][param]) + |
| "\n") |
| f.close() |
| |
| |
| # write some info out to files before running |
| if auth: |
| f = open(playbook_path + "/User.txt", "a") |
| f.write(cherrypy.request.login) |
| f.close() |
| f = open(playbook_path + "/PlaybookName.txt", "a") |
| f.write(PlaybookName) |
| f.close() |
| f = open(playbook_path + "/PlaybookExDir.txt", "a") |
| f.write(PlaybookDir + "/" + PlayBookFunction) |
| f.close() |
| f = open(playbook_path + "/JsonRequest.txt", "w") |
| #f.write(str(input_json)) |
| #print( json.dumps(input_json, indent=4, sort_keys=True)) |
| f.write( json.dumps(input_json, indent=4, sort_keys=True)) |
| f.close() |
| |
| |
| # Check that HostFile exists |
| if not os.path.isfile(HostFile): |
| cherrypy.log("Inventory file Not Found: " + HostFile) |
| return {"StatusCode": 101, |
| "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"} |
| |
| # Cannot use thread because ansible module uses |
| # signals which are only supported in main thread. |
| # So use multiprocess with shared object |
| # args = (callback, Id, PlaybookDir + "/" + AnsibleInv, |
| |
| p = Process(target = RunAnsible_Playbook, |
| args = (callback, Id, HostFile, |
| PlaybookDir + '/' + PlayBookFile, |
| NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction, |
| ArchiveFlag)) |
| p.start() |
| ActiveProcess[Id] = p |
| return TestRecord[Id]['Result'] |
| else: |
| cherrypy.log("TEST ID ALREADY DEFINED") |
| return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"} |
| |
| else: |
| return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"} |
| |
| else: |
| return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"} |
| |
| elif 'GET' in cherrypy.request.method: |
| |
| # Lets pause for a second just incase the resquest was just kicked off |
| time.sleep(1) |
| |
| input_data = parse_query_string(cherrypy.request.query_string) |
| |
| # Verify we have a Type passed in GET request |
| if not ( 'Type' in input_data): |
| return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"} |
| |
| if auth: |
| cherrypy.log( "Request USER: " + cherrypy.request.login) |
| cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type']) |
| |
| if 'LogRest' in input_data['Type']: |
| sys.stdout.close() |
| sys.stdout = open("/var/log/RestServer.log", "w") |
| |
| # Just a debug to dump any records |
| if 'GetStatus' in input_data['Type']: |
| cherrypy.log( "******** Dump Records **********") |
| if TestRecord.items(): |
| for id, record in TestRecord.items(): |
| cherrypy.log( " Id: " + id) |
| cherrypy.log( "Record: " + str(record)) |
| else: |
| cherrypy.log(" No Records to dump") |
| |
| if 'Id' in input_data and 'Type' in input_data: |
| if not ('GetResult' in input_data['Type'] or 'GetOutputLog' in input_data['Type'] or'GetTheOutput' in input_data['Type'] or 'GetLog' in input_data['Type']): |
| return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"} |
| if input_data['Id'] in TestRecord: |
| |
| if 'GetResult' in input_data['Type']: |
| |
| cherrypy.log( " ** GetResult for: " + str (input_data['Id'])) |
| |
| if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block: |
| |
| |
| #check if playbook is still running |
| while ActiveProcess[input_data['Id']].is_alive(): |
| cherrypy.log( "*** Playbook running returning PENDING for " + str(input_data['Id'])) |
| ## |
| ## If still running return PENDING response |
| ## |
| if AnsibleServer != 'na': |
| return {"StatusCode": 100, |
| "StatusMessage": 'PENDING', |
| "AnsibleServer": str(AnsibleServer)} |
| else: |
| return {"StatusCode": 100, |
| "StatusMessage": 'PENDING'} |
| #time.sleep(5) |
| |
| #cherrypy.log( "*** Request released " + input_data['Id']) |
| |
| cherrypy.log(str( TestRecord[input_data['Id']]['Result'])) |
| cherrypy.log("Output: " + str( TestRecord[input_data['Id']]['Output'])) |
| cherrypy.log("StatusCode: " + str( TestRecord[input_data['Id']]['Result']['StatusCode'])) |
| cherrypy.log("StatusMessage: " + str( TestRecord[input_data['Id']]['Result']['StatusMessage'])) |
| |
| #out_obj gets returned to GET request |
| if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500: |
| out_obj = TestRecord[input_data['Id']]['Result']['Results'] |
| else: |
| out_obj = {"StatusCode": 200, |
| "StatusMessage": "FINISHED", |
| "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"], |
| "Version": TestRecord[input_data['Id']]["Version"], |
| "Duration": TestRecord[input_data['Id']]["Duration"], |
| "Output": TestRecord[input_data['Id']]["Output"]["Output"], |
| "Results": TestRecord[input_data['Id']]['Result']['Results']} |
| if not TestRecord[input_data['Id']]['Output']['Output'] == {}: |
| cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output'])) |
| # PAP |
| for key in out_obj["Results"]: |
| cherrypy.log("Output key: " + str(key)) |
| if key in TestRecord[input_data['Id']]['Output']['Output']: |
| out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key] |
| |
| cherrypy.log("***** GET RETURNING RESULTS Back ****") |
| cherrypy.log(str(out_obj)) |
| return out_obj |
| |
| elif 'GetStatus' in input_data['Type']: |
| print " Dump Records" |
| for id, record in TestRecord,items(): |
| print " id: " + id |
| print " Record:" + str(reecord) |
| |
| elif 'GetTheOutput' in input_data['Type']: |
| |
| if TestRecord[input_data['Id']]['Output'] == {} and \ |
| getresults_block: |
| |
| cherrypy.log( "*** Request blocked " + input_data['Id']) |
| |
| while TestRecord[input_data['Id']]['Output'] == {} \ |
| or 'StatusMessage' in TestRecord[input_data['Id']]['Result']: |
| time.sleep(5) |
| |
| cherrypy.log( "*** Request released " + input_data['Id']) |
| |
| cherrypy.log( "Output: " + str(TestRecord[input_data['Id']]['Output'])) |
| return {"Output": TestRecord[input_data['Id']]['Output']['Output']} |
| |
| elif 'GetOutputLog' in input_data['Type']: |
| cherrypy.log("GetOutputLog: processing.") |
| if glob.glob( ansible_temp + '/*_' + input_data['Id']): |
| id = input_data['Id'] |
| cherrypy.log("Old directory found for ID: " + id) |
| run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id']) |
| for dir in run_dir: |
| rdir=dir |
| if os.path.exists (rdir + "/PlaybookExDir.txt"): |
| cherrypy.log("Found PlaybookExDir.txt file") |
| f = open( rdir + '/PlaybookExDir.txt', 'r') |
| playbookexdir = f.readline() |
| rdir = playbookexdir |
| f.close() |
| cherrypy.log("Id: " + id) |
| cherrypy.log("RunDir: " + rdir) |
| if os.path.exists( rdir + "/output.log"): |
| cherrypy.log("Found output.log file") |
| f = open( rdir + '/output.log', 'r') |
| output_log = f.readline() |
| f.close() |
| return output_log |
| else: |
| cherrypy.log("Globglob failed:") |
| return |
| |
| else: |
| # GetLog |
| |
| if TestRecord[input_data['Id']]['Log'] == '' and \ |
| getresults_block: |
| |
| cherrypy.log( "*** Request blocked " + input_data['Id']) |
| |
| while TestRecord[input_data['Id']]['Log'] == '' \ |
| or 'StatusMessage' in TestRecord[input_data['Id']]['Result']: |
| time.sleep(5) |
| |
| cherrypy.log( "*** Request released " + input_data['Id']) |
| |
| cherrypy.log( "Log:" + str(TestRecord[input_data['Id']]['Log'])) |
| return {"Log": TestRecord[input_data['Id']]['Log']} |
| else: |
| # Not in memory check for a file |
| if glob.glob( ansible_temp + '/*_' + input_data['Id']): |
| id = input_data['Id'] |
| cherrypy.log("Old directory found for ID: " + id) |
| run_dir = glob.glob( ansible_temp + '/*_' + input_data['Id']) |
| for dir in run_dir: |
| rdir=dir |
| if os.path.exists (rdir + "/PlaybookExDir.txt"): |
| cherrypy.log("Found PlaybookExDir.txt file") |
| f = open( rdir + '/PlaybookExDir.txt', 'r') |
| playbookexdir = f.readline() |
| rdir = playbookexdir |
| f.close() |
| cherrypy.log("Id: " + id) |
| cherrypy.log("RunDir: " + rdir) |
| if 'GetLog' in input_data['Type']: |
| if os.path.exists( rdir + "/output.log"): |
| cherrypy.log("Found output.log file") |
| f = open( rdir + '/output.log', 'r') |
| output_log = f.readline() |
| f.close() |
| return output_log |
| elif 'GetOutputLog' in input_data['Type']: |
| if os.path.exists( rdir + "/output.log"): |
| cherrypy.log("Found output.log file") |
| f = open( rdir + '/output.log', 'r') |
| output_log = f.readline() |
| f.close() |
| return output_log |
| elif 'GetResult' in input_data['Type']: |
| if os.path.exists (rdir + "/PlaybookName.txt"): |
| cherrypy.log("Found PlaybookName.txt file") |
| f = open( rdir + '/PlaybookName.txt', 'r') |
| playbooknametxt = f.readline() |
| f.close() |
| else: |
| playbooknametxt = "NA" |
| |
| # Add code to get other items not just output.log from files |
| if os.path.exists( rdir + "/log.file"): |
| cherrypy.log("Found log.file") |
| out_results = "NA:" |
| f = open( rdir + '/log.file', 'r') |
| |
| line = f.readline() |
| while line : |
| if "fatal" in line: |
| out_results = out_results + line |
| elif "RECAP" in line: |
| out_results = out_results + line |
| recap_line = f.readline() |
| while recap_line : |
| out_results = out_results + recap_line |
| recap_line = f.readline() |
| line = f.readline() |
| f.close() |
| out_obj = {"StatusCode": 200, |
| "StatusMessage": "FINISHED", |
| "PlaybookName": playbooknametxt, |
| "Version": "Version", |
| "Duration": 200, |
| "Results": out_results} |
| return out_obj |
| else: |
| return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "} |
| |
| |
| return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"} |
| else: |
| return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"} |
| elif 'DELETE' in cherrypy.request.method: |
| input_data = parse_query_string(cherrypy.request.query_string) |
| |
| cherrypy.log( "***> in RestServer.DELETE") |
| cherrypy.log("Payload: " + str(input_data)) |
| |
| if input_data['Id'] in TestRecord: |
| if not 'PENDING' in TestRecord[input_data['Id']]['Result']: |
| cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path'])) |
| TestRecord.pop (input_data['Id']) |
| if input_data['Id'] in ActiveProcess: |
| ActiveProcess.pop (input_data['Id']) |
| |
| return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"} |
| else: |
| return {"StatusCode": 200, "StatusMessage": "PENDING"} |
| else: |
| return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"} |
| |
| |
| if __name__ == '__main__': |
| |
| # Read configuration |
| |
| config_file_path = "RestServer_config" |
| |
| if not os.path.exists(config_file_path): |
| print '[INFO] The config file does not exist' |
| sys.exit(0) |
| |
| ip = 'na' |
| AnsibleServer = 'na' |
| port = 'na' |
| tls = False |
| auth = False |
| pub = 'na' |
| priv = 'na' |
| timeout_seconds = 'na' |
| ansible_path = 'na' |
| ansible_temp = 'na' |
| host = 'na' |
| users= 'na' |
| getresults_block = False |
| from_files = False |
| |
| file = open(config_file_path, 'r') |
| for line in file.readlines(): |
| if '#' not in line: |
| if 'ip:' in line: |
| ip = line.split(':')[1].strip() |
| elif 'AnsibleServer:' in line: |
| AnsibleServer = line.split(':')[1].strip() |
| elif 'port:' in line: |
| port = line.split(':')[1].strip() |
| elif 'ksalt:' in line: |
| salt = line.split(':')[1].strip() |
| elif 'tls:' in line: |
| tls = 'YES' in line.split(':')[1].strip().upper() |
| elif 'auth:' in line: |
| auth = 'YES' in line.split(':')[1].strip().upper() |
| if tls and 'priv:' in line: |
| priv = line.split(':')[1].strip() |
| if tls and 'pub:' in line: |
| pub = line.split(':')[1].strip() |
| if tls and 'inter_cert:' in line: |
| intermediate = line.split(':')[1].strip() |
| if 'timeout_seconds' in line: |
| timeout_seconds = int (line.split(':')[1].strip()) |
| if 'ansible_path' in line: |
| ansible_path = line.split(':')[1].strip() |
| if 'ansible_temp' in line: |
| ansible_temp = line.split(':')[1].strip() |
| if 'host' in line: |
| host = line.split(':')[1].strip() |
| if 'users' in line: |
| users = line.split(':')[1].strip() |
| if 'getresults_block' in line: |
| getresults_block = 'YES' in line.split(':')[1].strip().upper() |
| if 'from_files' in line: |
| from_files = 'YES' in line.split(':')[1].strip().upper() |
| file.close() |
| |
| # Initialization |
| |
| global_conf = { |
| 'global': { |
| 'log.screen': True, |
| 'response.timeout': 5400, |
| 'server.socket_host': ip, |
| 'server.socket_port': int(port), |
| 'server.protocol_version': 'HTTP/1.1' |
| } |
| } |
| |
| if tls: |
| # Use pythons built-in SSL |
| cherrypy.server.ssl_module = 'builtin' |
| |
| # Point to certificate files |
| |
| if not os.path.exists(pub): |
| print '[INFO] The public certificate does not exist' |
| sys.exit(0) |
| |
| if not os.path.exists(priv): |
| print '[INFO] The private key does not exist' |
| sys.exit(0) |
| |
| if not os.path.exists(intermediate): |
| print '[INFO] The intermediate certificate does not exist' |
| sys.exit(0) |
| |
| |
| cherrypy.server.ssl_certificate = pub |
| cherrypy.server.ssl_certificate_chain = intermediate |
| cherrypy.server.ssl_private_key = priv |
| |
| if auth: |
| # Read in and build user dictionary |
| if not os.path.exists(users): |
| print '[INFO] The users file does not exist: ' + users |
| sys.exit(0) |
| userpassdict = {} |
| user_file = open(users, 'r') |
| for line in user_file.readlines(): |
| if '#' not in line: |
| id = line.split(':')[0].strip() |
| pw = line.split(':')[1].strip() |
| userpassdict[id] = pw |
| #print str(userpassdict) |
| |
| app_conf = {'/': |
| {'tools.auth_basic.on': True, |
| 'tools.auth_basic.realm': 'earth', |
| 'tools.auth_basic.checkpassword': validate_password, |
| } |
| } |
| |
| application = cherrypy.tree.mount(TestManager(), '/', app_conf) |
| else: |
| application = cherrypy.tree.mount(TestManager(), '/') |
| |
| cherrypy.config.update({ |
| 'log.access_file': "/var/log/RestServer.access" |
| }) |
| accessLogName = "/var/log/RestServer.access" |
| applicationLogName = "/var/log/RestServer.log" |
| cherrypy.config.update(global_conf) |
| |
| log = application.log |
| log.error_file = "" |
| log.access_file = "" |
| from logging import handlers |
| applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000) |
| accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000) |
| import logging |
| applicationLogFileHandler.setLevel(logging.DEBUG) |
| log.error_log.addHandler(applicationLogFileHandler) |
| log.access_log.addHandler(accessLogFileHandler) |
| |
| # Start server |
| |
| cherrypy.engine.start() |
| cherrypy.engine.block() |