alpine build, implement to follow VNF anisble Req
[ccsdk/distribution.git] / ansible-server / src / main / scripts / RestServer.py
1 '''
2 /*-
3 * ============LICENSE_START=======================================================
4 * ONAP : APPC
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
13 *
14 *      http://www.apache.org/licenses/LICENSE-2.0
15 *
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.
21
22 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 * ============LICENSE_END=========================================================
24 */
25 '''
26
27 import time, datetime, json, os, sys, subprocess, re
28 import uuid
29 import tarfile
30 import shutil
31 import glob
32 import crypt
33
34 import requests
35
36 import cherrypy
37 from cherrypy.lib.httputil import parse_query_string
38 from cherrypy.lib import auth_basic
39
40 from multiprocessing import Process, Manager
41
42 from AnsibleModule import ansibleSysCall
43 from BuildHostFile import buildHostsSysCall
44
45 from os import listdir
46 from os.path import isfile, join
47
48 TestRecord = Manager().dict()
49 ActiveProcess = {}
50
51 def validate_password(realm, username, password):
52     comp = crypt.crypt(password, salt)
53     if username in userpassdict and userpassdict[username] == comp:
54        return True
55     return False
56
57 def sys_call (cmd):
58     p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
59     output = p.stdout.readlines()
60     retval = p.wait()
61     if len (output) > 0:
62         for i in range(len(output)):
63             output[i] = output[i].strip()
64     return retval, output
65
66 def callback (Id, Result, Output, Log, returncode):
67     
68     print "***> in RestServer.callback"
69
70     if Id in TestRecord:
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']
75         if returncode == 137:
76             Result['StatusCode'] = 500
77             Result['StatusMessage'] = "TERMINATED"
78         else:
79             Result['StatusCode'] = 200
80             Result['StatusMessage'] = "FINISHED"
81
82         # Need to update the whole data structure for key=Id otherwise Manager is not updated
83         TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'],
84                           'Version': TestRecord[Id]['Version'],
85                           'NodeList': TestRecord[Id]['NodeList'],
86                           'HostGroupList': TestRecord[Id]['HostGroupList'],
87                           'HostNameList': TestRecord[Id]['HostNameList'],
88                           'Time': TestRecord[Id]['Time'],
89                           'Timeout': TestRecord[Id]['Timeout'],
90                           'Duration': str(delta_time),
91                           'EnvParameters': TestRecord[Id]['EnvParameters'],
92                           'LocalParameters': TestRecord[Id]['LocalParameters'],
93                           'FileParameters': TestRecord[Id]['FileParameters'],
94                           'CallBack': TestRecord[Id]['CallBack'],
95                           'Result': Result,
96                           'Log': Log, 
97                           'Output': Output, 
98                           'Path': TestRecord[Id]['Path'],
99                           'Mandatory': TestRecord[Id]['Path']}
100
101         if not TestRecord[Id]['CallBack'] == None:
102             
103             # Posting results to callback server
104
105             data = {"StatusCode": 200,
106                     "StatusMessage": "FINISHED",
107                     "PlaybookName": TestRecord[Id]["PlaybookName"],
108                     "Version": TestRecord[Id]["Version"],
109                     "Duration": TestRecord[Id]["Duration"],
110                     "Results": TestRecord[Id]['Result']['Results']}
111
112             cherrypy.log("CALLBACK: TestRecord[Id]['Output']['Output']:", str(TestRecord[Id]['Output']['Output']))
113             cherrypy.log("CALLBACK: Results:", str(data["Results"]))
114
115             if not TestRecord[Id]['Output']['Output'] == {}:
116                 for key in data["Results"]:
117                     if key in TestRecord[Id]['Output']['Output']:
118                         data["Results"][key]["Output"] = TestRecord[Id]['Output']['Output'][key]
119
120             print "     Posting to", TestRecord[Id]['CallBack']
121             
122             s = requests.Session()
123             r = s.post(TestRecord[Id]['CallBack'], data = json.dumps(data),
124                        headers = {'content-type': 'application/json'})
125             print  "     Response", r.status_code, r.text
126
127 def RunAnsible_Playbook (callback, Id, Inventory, Playbook, NodeList, TestRecord,
128                          Path, ArchiveFlag):
129
130     print "***> in RestServer.RunAnsible_Playbook"
131
132     # Run test in playbook for given target
133     Result = ''
134
135     retval, log, returncode = ansibleSysCall (Inventory, Playbook, NodeList,
136                                               TestRecord[Id]['Mandatory'],
137                                               TestRecord[Id]['EnvParameters'],
138                                               TestRecord[Id]['LocalParameters'],
139                                               TestRecord[Id]['Timeout'],
140                                               Path)
141
142
143     cherrypy.log("Return code:" + str(returncode))
144     cherrypy.log("Return val:" +  str(retval))
145     
146     Log = ''.join(log)
147     #Output = {'Output': {}}
148     Output = {}
149     
150     onlyfiles = [f for f in listdir(Path)
151                  if isfile(join(Path, f))]
152
153     cherrypy.log("Checking for results.txt files: ")
154     for file in onlyfiles:
155         if "results.txt" in file:
156 #       if file.endswith("results.txt"):
157             cherrypy.log("results file: " + file)
158             f = open(Path + "/" + file, "r")
159             resultsData =  f.read()  # Not to pass vnf instance name
160             OutputP = json.loads(resultsData)
161             Output['Output'] =  OutputP 
162             cherrypy.log("Output = " + str(Output['Output']))
163             #Output['Output'][key] = f.read() # To pass vnf instance name
164             f.close()
165
166     if Output == {}:
167       Output = {'Output': {}}
168
169     Result = {'Results': {}}
170     if 'could not be found' in Log:
171         Result['Results'] = {"StatusCode": 101,
172                              "StatusMessage": "PLAYBOOK NOT FOUND"}
173     if returncode == 137:
174         Result['Results'] = {"StatusCode": 500,
175                              "StatusMessage": "TERMINATED"}
176
177     elif TestRecord[Id]['NodeList'] == []:
178         
179         host_index = None
180         
181         if 'TargetNode' in TestRecord[Id]['EnvParameters']:
182             targetlist = TestRecord[Id]['EnvParameters']['TargetNode'].split(' ')
183         else:
184             targetlist = ["localhost"]
185             
186         for key in retval:
187             for i in range (len(targetlist)):
188                 if key in targetlist[i]:
189                     host_index = i
190     
191             if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
192                    int(retval[key][3]) == 0:
193
194                 if host_index:
195                     Result['Results'][targetlist[host_index]] = \
196                              {"GroupName": 'na', "StatusCode": 200, \
197                               "StatusMessage": "SUCCESS"}
198                 else:
199                     Result['Results'][key] = \
200                              {"GroupName": 'na', "StatusCode": 200, \
201                               "StatusMessage": "SUCCESS"}                    
202             elif int(retval[key][2]) > 0:
203                 if host_index:
204                     Result['Results'][targetlist[host_index]] = \
205                        {"GroupName": 'na', "StatusCode": 400, \
206                         "StatusMessage": "NOT REACHABLE"}
207                 else:
208                     Result['Results'][key] = \
209                        {"GroupName": 'na', "StatusCode": 400, \
210                         "StatusMessage": "NOT REACHABLE"}                    
211             elif int(retval[key][3]) > 0:
212                 if host_index:                
213                     Result['Results'][targetlist[host_index]] = \
214                        {"GroupName": 'na', "StatusCode": 400, \
215                         "StatusMessage": "FAILURE"}
216                 else:
217                     Result['Results'][key] = \
218                        {"GroupName": 'na', "StatusCode": 400, \
219                         "StatusMessage": "FAILURE"}                    
220     else:
221         
222         for key in retval:
223
224             if len(TestRecord[Id]['HostNameList']) > 0:
225
226                 host_index = []
227                 for i in range (len(TestRecord[Id]['HostNameList'])):
228                     if key in TestRecord[Id]['HostNameList'][i]:
229                         host_index.append(i)
230
231                 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
232                        int(retval[key][3]) == 0:
233
234                     if len(host_index) > 0:
235                         Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
236                           {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
237                            "StatusCode": 200, "StatusMessage": "SUCCESS"}
238                     
239                         for i in range (1, len(host_index)):
240                             Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
241                              "," + TestRecord[Id]['HostGroupList'][host_index[i]]
242                     else:
243                        Result['Results'][key] = \
244                           {"GroupName": key,
245                            "StatusCode": 200, "StatusMessage": "SUCCESS"}  
246
247                 elif int(retval[key][2]) > 0:
248
249                     if len(host_index) > 0:
250                         Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
251                           {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
252                            "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
253                     
254                         for i in range (1, len(host_index)):
255                             Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
256                              "," + TestRecord[Id]['HostGroupList'][host_index[i]]
257                     else:
258                        Result['Results'][key] = \
259                           {"GroupName": key,
260                            "StatusCode": 200, "StatusMessage": "NOT REACHABLE"}  
261                     
262                 elif int(retval[key][3]) > 0:
263
264                     if len(host_index) > 0:
265                         Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
266                           {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
267                            "StatusCode": 400, "StatusMessage": "FAILURE"}
268                     
269                         for i in range (1, len(host_index)):
270                             Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
271                              "," + TestRecord[Id]['HostGroupList'][host_index[i]]
272                     else:
273                        Result['Results'][key] = \
274                           {"GroupName": key,
275                            "StatusCode": 200, "StatusMessage": "FAILURE"}                          
276             else:
277                 host_index = None
278                 for i in range (len(TestRecord[Id]['NodeList'])):
279                     if key in TestRecord[Id]['NodeList'][i]:
280                         host_index = i
281     
282                 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
283                        int(retval[key][3]) == 0:
284                     Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
285                              {"GroupName": 'na', "StatusCode": 200, \
286                              "StatusMessage": "SUCCESS"}
287                 elif int(retval[key][2]) > 0:
288                     Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
289                        {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
290                 elif int(retval[key][3]) > 0:
291                     Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
292                        {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
293     
294     cherrypy.log("TESTRECORD: " + str(TestRecord[Id]))
295     #cherrypy.log("Output: " + str(Output))
296     callback (Id, Result, Output, Log, returncode)
297
298 class TestManager (object):
299
300     @cherrypy.expose
301     @cherrypy.tools.json_out()
302     @cherrypy.tools.json_in()
303     @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
304
305     def Dispatch(self, **kwargs):
306
307         # Let cherrypy error handler deal with malformed requests
308         # No need for explicit error handler, we use default ones
309
310         time_now = datetime.datetime.utcnow()
311
312         # Erase old test results (2x timeout)
313         # Do cleanup too of ActiveProcess list and old Records - PAP
314         if TestRecord:
315             for key in TestRecord.copy():
316                 cherrypy.log( "LOOKING AT ALL TestRecords: " + str(key))
317                 if key in ActiveProcess:
318                    if not ActiveProcess[key].is_alive(): # Just to cleanup defunct processes
319                       cherrypy.log( "Not ActiveProcess for ID: " + str(key))
320                 delta_time = (time_now - TestRecord[key]['Time']).seconds
321                 if delta_time > 2*TestRecord[key]['Timeout']:
322                     cherrypy.log( "DELETED HISTORY for ID: " + str(key))
323                     if key in ActiveProcess:
324                       if not ActiveProcess[key].is_alive():
325                           ActiveProcess.pop (key)
326                           cherrypy.log( "DELETED ActiveProcess for ID: " + str(key))
327                     #if os.path.exists(TestRecord[key]['Path']):
328                         # don't remove run dirrectory
329                         #shutil.rmtree (TestRecord[key]['Path'])
330                     del TestRecord[key]
331                     
332         cherrypy.log("RestServer.Dispatch: " + cherrypy.request.method)
333
334
335         if 'POST' in cherrypy.request.method:
336             
337             input_json = cherrypy.request.json
338             cherrypy.log("Payload: " + str(input_json))
339
340             if 'Id' in input_json and 'PlaybookName' in input_json and 'EnvParameters' in input_json:
341
342                 if True:
343
344                     if not input_json['Id'] in TestRecord:
345                         # check if Id exists in previous run dirctory
346                         # if so retun error
347                         s_cmd = 'ls ' + ansible_temp + '/*_' + input_json['Id']
348                         #if subprocess.check_output([s_cmd, ]):
349                         Id = input_json['Id']
350                         if glob.glob(  ansible_temp + '/*_' + input_json['Id']):
351                             cherrypy.log("Old directory found for ID: " + Id)
352                             return {"StatusCode": 101, "StatusMessage": "TEST ID FILE ALREADY DEFINED"}
353                     
354                         PlaybookName = input_json['PlaybookName']
355                         # if required it should be passed as an argument
356                         EnvParameters = input_json['EnvParameters']
357
358                         # The lines below are to test multiple EnvParameters being passed
359                         #for i in EnvParameters:
360                         #  cherrypy.log("EnvParameter object: " + i)
361                         #  cherrypy.log("  EnvParameter Value: " + EnvParameters[ i ])
362
363                         # Now get things out of EnvParameters
364                         VNF_instance   = None
365                         VNF_instance = EnvParameters.get('vnf_instance')
366
367                         # Get Version if present
368                         version = None
369                         if 'Version' in input_json:
370                             version = input_json['Version']
371
372                         # GetInventoryNames
373                         HaveNodeList         = False
374                         HaveInventoryNames   = False 
375                         inventory_names = None
376                         if 'InventoryNames' in input_json:
377                            inventory_names = input_json['InventoryNames']
378                            HaveInventoryNames   = True 
379                     
380                         #AnsibleInvFail = True
381                         AnsiblePlaybookFail = True
382
383                         LocalNodeList = None
384
385                         str_uuid = str (uuid.uuid4())
386                         
387
388                         VnfType= PlaybookName.split("/")[0] 
389                         if auth:
390                            cherrypy.log( "Request USER  :                  " + cherrypy.request.login)
391                         cherrypy.log( "Request Decode: ID               " + Id)
392                         cherrypy.log( "Request Decode: VnfType          " + VnfType)
393                         cherrypy.log( "Request Decode: EnvParameters    " + json.dumps(EnvParameters))
394                         
395                         # Verify VNF_instance was passed in EnvParameters
396                         if VNF_instance != None:
397                           cherrypy.log( "Request Decode: VnfInstance      " + VNF_instance)
398                         else:
399                           cherrypy.log( "StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED" )
400                           return {"StatusCode": 107,
401                                     "StatusMessage": "VNF_instance NOT PROVIDED"}
402
403                         if inventory_names != None:
404                           cherrypy.log( "Request Decode: Inventory Names  " + inventory_names)
405                         else:
406                           cherrypy.log( "Request Decode: Inventory Names  " + "Not provided")
407
408                         cherrypy.log( "Request Decode: PlaybookName     " + PlaybookName)
409                         PlayBookFunction = PlaybookName.rsplit("/",2)[1]
410                         PlayBookFile = PlayBookFunction + "/site.yml"
411                         cherrypy.log( "Request Decode: PlaybookFunction " + PlayBookFunction)
412                         cherrypy.log( "Request Decode: Playbook file    " + PlayBookFile)
413                         
414                         BaseDir = ansible_path + "/" + PlaybookName.rsplit("/",1)[0]
415                         CopyDir = ansible_path + "/" + PlaybookName.rsplit("/",2)[0]
416                         cherrypy.log( "Request Decode: Basedir          " + BaseDir)
417                         cherrypy.log( "Request Decode: Copydir          " + CopyDir)
418                         
419
420                         PlaybookDir = ansible_temp + "/" + \
421                                       VNF_instance + "_" + str_uuid + "_" + str(Id)
422
423                         # AnsibleInv is the directory where the host file to be run exsists
424                         AnsibleInv = ansible_path + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance
425                         ArchiveFlag = False
426
427                         # Create base run directory if it doesn't exist
428                         if not os.path.exists(ansible_temp):
429                             cherrypy.log( "Creating Base Run Directory: "  + ansible_temp)
430                             os.makedirs(ansible_temp)
431
432                         if not os.path.exists( CopyDir ):
433                             cherrypy.log("Playbook Not Found")
434                             return {"StatusCode": 101,
435                                     "StatusMessage": "PLAYBOOK NOT FOUND"}
436
437                         # copy static playbook dir to run dir
438                         cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir)
439                         shutil.copytree(CopyDir, PlaybookDir)
440                         cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;"
441                         cmd="/usr/bin/find " + PlaybookDir + " -exec chmod +rx  {} \;"
442                         sys_call(cmd)
443                         cherrypy.log(cmd)
444
445                         cherrypy.log( "PlaybookDir:    " + PlaybookDir)
446                         cherrypy.log( "AnsibleInv:     " + AnsibleInv)
447
448                         # Process inventory file for target
449                     
450                         hostgrouplist = []
451                         hostnamelist = []
452
453                         NodeList = []
454                         if 'NodeList' in input_json:
455                             NodeList = input_json['NodeList']
456
457                         cherrypy.log("NodeList: " + str(NodeList));
458
459                         # if NodeList empty 
460                         if NodeList == []:
461                                 cherrypy.log( "*** NodeList - Empty ***")
462
463                         else:
464                                 HaveNodeList = True
465
466                         ###############################################################################
467                         ##### Host file processing                          ###########################
468                         ##### 1. Use file delivered with playbook           ###########################
469                         ##### 2. If HostNames + NodeList generate and use   ###########################
470                         ###############################################################################
471
472                         #Verify inventory directory exists
473                         path = PlaybookDir + "/inventory/"
474                         if not os.path.isdir(path):
475                             cherrypy.log ("Inventory directory %s does not exist - create it" % path)
476                             try:
477                                 os.mkdir(path)
478                             except OSError:
479                                 cherrypy.log ("Creation of the directory %s failed" % path)
480                             else:
481                                 cherrypy.log ("Successfully created the directory %s " % path)
482
483                         #location of host file - Default
484                         HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
485                         cherrypy.log("HostFile: " +  HostFile)
486
487                         # if NodeList and InventoryNames need to build host file
488                         if HaveInventoryNames & HaveNodeList:
489                            cherrypy.log("Build host file from NodeList")
490                            ret = buildHostsSysCall (input_json, PlaybookDir, inventory_names)
491                            if (ret < 0):
492                               cherrypy.log("Returning Error: Not running Playbook")
493                               return {"StatusCode": 105,
494                                     "StatusMessage": "NodeList: Missing vnfc-type field"}
495
496                            # Having been built now copy new file to correct file
497                            shutil.copy(PlaybookDir + "/host_file.txt", HostFile)
498                            cherrypy.log("Copying Generated host file to: " + HostFile)
499
500                         timeout = timeout_seconds
501                         if 'Timeout' in input_json:
502                             timeout = int (input_json['Timeout'])
503                             cherrypy.log("Timeout from API: " + str(timeout))
504
505                         else:
506                             cherrypy.log("Timeout not passed from API using default: " + str(timeout))
507
508                         EnvParam = {}
509                         if 'EnvParameters' in input_json:
510                             EnvParam = input_json['EnvParameters']
511
512                         LocalParam = {}
513                         if 'LocalParameters' in input_json:
514                             LocalParam = input_json['LocalParameters']
515
516                         FileParam = {}
517                         if 'FileParameters' in input_json:
518                             FileParam = input_json['FileParameters']
519                     
520                         callback_flag = None
521                         if 'CallBack' in input_json:
522                             callback_flag = input_json['CallBack']
523
524                         # if AnsibleServer is not set to 'na'  don't send AnsibleServer in PENDING responce.
525                         if AnsibleServer != 'na':
526                                 TestRecord[Id] = {'PlaybookName': PlaybookName,
527                                           'Version': version,
528                                           'NodeList': NodeList,
529                                           'HostGroupList': hostgrouplist,
530                                           'HostNameList': hostnamelist,
531                                           'Time': time_now,
532                                           'Duration': timeout,
533                                           'Timeout': timeout,
534                                           'EnvParameters': EnvParam,
535                                           'LocalParameters': LocalParam,
536                                           'FileParameters': FileParam,
537                                           'CallBack': callback_flag,
538                                           'Result': {"StatusCode": 100,
539                                                      "StatusMessage": 'PENDING',
540                                                      "AnsibleServer": str(AnsibleServer),
541                                                      "ExpectedDuration": str(timeout) + "sec"},
542                                           'Log': '',
543                                           'Output': {},
544                                           'Path': PlaybookDir,
545                                           'Mandatory': None}
546                         else:
547                                 TestRecord[Id] = {'PlaybookName': PlaybookName,
548                                           'Version': version,
549                                           'NodeList': NodeList,
550                                           'HostGroupList': hostgrouplist,
551                                           'HostNameList': hostnamelist,
552                                           'Time': time_now,
553                                           'Duration': timeout,
554                                           'Timeout': timeout,
555                                           'EnvParameters': EnvParam,
556                                           'LocalParameters': LocalParam,
557                                           'FileParameters': FileParam,
558                                           'CallBack': callback_flag,
559                                           'Result': {"StatusCode": 100,
560                                                      "StatusMessage": 'PENDING',
561                                                      "ExpectedDuration": str(timeout) + "sec"},
562                                           'Log': '',
563                                           'Output': {},
564                                           'Path': PlaybookDir,
565                                           'Mandatory': None}
566
567                         cherrypy.log("Test_Record: " +  str(TestRecord[Id]))
568                         # Write files
569                         
570                         if not TestRecord[Id]['FileParameters'] == {}:
571                             for key in TestRecord[Id]['FileParameters']:
572                                 filename = key
573                                 filecontent = TestRecord[Id]['FileParameters'][key]
574                                 f = open(PlaybookDir + "/" + filename, "w")
575                                 f.write(filecontent)
576                                 f.close()
577                                 
578                         
579                         # Process playbook
580                         if os.path.exists( ansible_path + '/' + PlaybookName):
581                             AnsiblePlaybookFail = False
582                                     
583                         if AnsiblePlaybookFail:
584                             #if os.path.exists(PlaybookDir):
585                                 #shutil.rmtree (PlaybookDir)
586                             del TestRecord[Id]
587                             return {"StatusCode": 101,
588                                     "StatusMessage": "PLAYBOOK NOT FOUND"}
589                         else:
590
591                             # Test EnvParameters
592                             playbook_path = PlaybookDir
593
594                             # Store local vars
595                             if not os.path.exists(playbook_path + "/vars"):
596                                 os.mkdir(playbook_path + "/vars")
597                             if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
598                                 os.mknod(playbook_path + "/vars/defaults.yml")
599
600                             ###################################################
601                             # PAP
602                             #write local parameters passed into defaults.yml
603                             # PAP
604                             f = open(playbook_path + "/vars/defaults.yml","a")
605                             #for id, record in TestRecord.items():
606                             print TestRecord[Id]['LocalParameters']
607                             local_parms = TestRecord[Id]['LocalParameters']
608                             for key, value in local_parms.items():
609                                 f.write(key +"=" + value + "\n");
610                             f.close()
611                             ###################################################
612                                  
613                             for key in TestRecord[Id]['LocalParameters']:
614                                 host_index = []
615                                 for i in range(len(TestRecord[Id]['HostNameList'])):
616                                     if key in TestRecord[Id]['HostNameList'][i]:
617                                         host_index.append(i)
618                                 if len(host_index) == 0:
619                                     for i in range(len(TestRecord[Id]['HostGroupList'])):
620                                         if key in TestRecord[Id]['HostGroupList'][i]:
621                                             host_index.append(i)
622                                 if len(host_index) > 0:
623                                     for i in range(len(host_index)):
624                                         f = open(playbook_path + "/vars/" +
625                                                  TestRecord[Id]['HostNameList'][host_index[i]] +
626                                                  ".yml", "a")
627                                         for param in TestRecord[Id]['LocalParameters'][key]:
628                                             f.write(param + ": " +
629                                              str (TestRecord[Id]['LocalParameters'][key][param]) +
630                                                   "\n")
631                                         f.close()
632
633     
634                             # write some info out to files before running
635                             if auth:
636                                f = open(playbook_path + "/User.txt", "a")
637                                f.write(cherrypy.request.login)
638                                f.close()
639                             f = open(playbook_path + "/PlaybookName.txt", "a")
640                             f.write(PlaybookName)
641                             f.close()
642                             f = open(playbook_path + "/PlaybookExDir.txt", "a")
643                             f.write(PlaybookDir + "/" + PlayBookFunction)
644                             f.close()
645                             f = open(playbook_path + "/JsonRequest.txt", "w")
646                             #f.write(str(input_json))
647                             #print( json.dumps(input_json, indent=4, sort_keys=True))
648                             f.write( json.dumps(input_json, indent=4, sort_keys=True))
649                             f.close()
650
651
652                             # Check that HostFile exists
653                             if not os.path.isfile(HostFile):
654                               cherrypy.log("Inventory file Not Found: " + HostFile)
655                               return {"StatusCode": 101,
656                                    "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"}
657    
658                             # Cannot use thread because ansible module uses
659                             # signals which are only supported in main thread.
660                             # So use multiprocess with shared object
661                                        # args = (callback, Id,  PlaybookDir + "/" + AnsibleInv,
662
663                             p = Process(target = RunAnsible_Playbook,
664                                         args = (callback, Id,  HostFile,
665                                                 PlaybookDir + '/' + PlayBookFile,
666                                                 NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction,
667                                                 ArchiveFlag))
668                             p.start()
669                             ActiveProcess[Id] = p
670                             return TestRecord[Id]['Result']
671                     else:
672                         cherrypy.log("TEST ID ALREADY DEFINED")
673                         return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
674
675                 else:
676                     return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
677                 
678             else:
679                 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"}
680
681         elif 'GET' in cherrypy.request.method:
682             
683             # Lets pause for a second just incase the resquest was just kicked off
684             time.sleep(1)
685
686             input_data = parse_query_string(cherrypy.request.query_string)
687              
688             # Verify we have a Type passed in GET request
689             if not ( 'Type' in input_data):
690                 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
691
692             if auth:
693                cherrypy.log( "Request USER:             " + cherrypy.request.login)
694             cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type'])
695
696             if 'LogRest' in input_data['Type']:
697                 sys.stdout.close()
698                 sys.stdout = open("/var/log/RestServer.log", "w")
699
700             # Just a debug to dump any records
701             if 'GetStatus' in input_data['Type']:
702                 cherrypy.log( "******** Dump Records **********")
703                 if TestRecord.items():
704                   for id, record in TestRecord.items():
705                     cherrypy.log( "    Id: " + id)
706                     cherrypy.log( "Record: " + str(record))
707                 else:
708                   cherrypy.log(" No Records to dump")
709             
710             if 'Id' in input_data and 'Type' in input_data:
711                 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']):
712                     return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
713                 if input_data['Id'] in TestRecord:
714                     
715                     if 'GetResult' in input_data['Type']:
716                         
717                         cherrypy.log( " ** GetResult for: " + str (input_data['Id']))
718
719                         if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
720
721
722                             #check if playbook is still running
723                             while ActiveProcess[input_data['Id']].is_alive():
724                                 cherrypy.log( "*** Playbook running returning PENDING for " + str(input_data['Id']))
725                                 ##
726                                 ## If still running return PENDING response
727                                 ##
728                                 if AnsibleServer != 'na':
729                                    return {"StatusCode": 100,
730                                                      "StatusMessage": 'PENDING',
731                                                      "AnsibleServer": str(AnsibleServer)}
732                                 else: 
733                                    return {"StatusCode": 100,
734                                                      "StatusMessage": 'PENDING'}
735                                 #time.sleep(5)
736
737                             #cherrypy.log( "*** Request released " + input_data['Id'])
738
739                         cherrypy.log(str( TestRecord[input_data['Id']]['Result']))
740                         cherrypy.log("Output: " + str( TestRecord[input_data['Id']]['Output']))
741                         cherrypy.log("StatusCode: " + str( TestRecord[input_data['Id']]['Result']['StatusCode']))
742                         cherrypy.log("StatusMessage: " + str( TestRecord[input_data['Id']]['Result']['StatusMessage']))
743
744                         #out_obj gets returned to GET request
745                         if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
746                             out_obj = TestRecord[input_data['Id']]['Result']['Results']
747                         else:
748                             out_obj = {"StatusCode": 200,
749                                    "StatusMessage": "FINISHED",
750                                    "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
751                                    "Version": TestRecord[input_data['Id']]["Version"],
752                                    "Duration": TestRecord[input_data['Id']]["Duration"],
753                                    "Output": TestRecord[input_data['Id']]["Output"]["Output"],
754                                    "Results": TestRecord[input_data['Id']]['Result']['Results']}
755                         if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
756                             cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output']))
757                             # PAP                
758                             for key in out_obj["Results"]:
759                                 cherrypy.log("Output key: " + str(key))
760                                 if key in TestRecord[input_data['Id']]['Output']['Output']:
761                                     out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
762
763                         cherrypy.log("***** GET RETURNING RESULTS Back ****")
764                         cherrypy.log(str(out_obj))
765                         return out_obj
766
767                     elif 'GetStatus' in input_data['Type']:
768                         print " Dump Records"
769                         for id, record in TestRecord,items():
770                            print " id: " + id
771                            print "   Record:" + str(reecord)
772
773                     elif 'GetTheOutput' in input_data['Type']:
774
775                         if TestRecord[input_data['Id']]['Output'] == {} and \
776                                getresults_block:
777
778                             cherrypy.log( "*** Request blocked " + input_data['Id'])
779                             
780                             while TestRecord[input_data['Id']]['Output'] == {} \
781                                       or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
782                                 time.sleep(5)
783
784                             cherrypy.log( "*** Request released " + input_data['Id'])
785                         
786                         cherrypy.log( "Output: " + str(TestRecord[input_data['Id']]['Output']))
787                         return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
788
789                     elif 'GetOutputLog' in input_data['Type']:
790                        cherrypy.log("GetOutputLog: processing.")
791                        if glob.glob(  ansible_temp + '/*_' + input_data['Id']):
792                           id = input_data['Id']
793                           cherrypy.log("Old directory found for ID: " + id)
794                           run_dir = glob.glob(  ansible_temp + '/*_' + input_data['Id'])
795                           for dir in run_dir:
796                               rdir=dir
797                           if os.path.exists (rdir + "/PlaybookExDir.txt"):
798                                cherrypy.log("Found PlaybookExDir.txt file")
799                                f = open( rdir + '/PlaybookExDir.txt', 'r')
800                                playbookexdir =  f.readline()
801                                rdir = playbookexdir
802                                f.close()
803                           cherrypy.log("Id:     " + id)
804                           cherrypy.log("RunDir: " + rdir)
805                           if os.path.exists( rdir + "/output.log"):
806                                cherrypy.log("Found output.log file")
807                                f = open( rdir + '/output.log', 'r')
808                                output_log =  f.readline()
809                                f.close()
810                                return output_log
811                        else:
812                          cherrypy.log("Globglob failed:")
813                          return
814
815                     else:
816                         # GetLog
817
818                         if TestRecord[input_data['Id']]['Log'] == '' and \
819                                getresults_block:
820
821                             cherrypy.log( "*** Request blocked " + input_data['Id'])
822                             
823                             while TestRecord[input_data['Id']]['Log'] == '' \
824                                       or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
825                                 time.sleep(5)
826
827                             cherrypy.log( "*** Request released " + input_data['Id'])
828                             
829                         cherrypy.log( "Log:" + str(TestRecord[input_data['Id']]['Log']))
830                         return {"Log": TestRecord[input_data['Id']]['Log']}
831                 else:
832                    # Not in memory check for a file 
833                    if glob.glob(  ansible_temp + '/*_' + input_data['Id']):
834                        id = input_data['Id']
835                        cherrypy.log("Old directory found for ID: " + id)
836                        run_dir = glob.glob(  ansible_temp + '/*_' + input_data['Id'])
837                        for dir in run_dir:
838                            rdir=dir
839                        if os.path.exists (rdir + "/PlaybookExDir.txt"):
840                             cherrypy.log("Found PlaybookExDir.txt file")
841                             f = open( rdir + '/PlaybookExDir.txt', 'r')
842                             playbookexdir =  f.readline()
843                             rdir = playbookexdir
844                             f.close()
845                        cherrypy.log("Id:     " + id)
846                        cherrypy.log("RunDir: " + rdir)
847                        if 'GetLog' in input_data['Type']:
848                          if os.path.exists( rdir + "/output.log"):
849                             cherrypy.log("Found output.log file")
850                             f = open( rdir + '/output.log', 'r')
851                             output_log =  f.readline()
852                             f.close()
853                             return output_log
854                        elif 'GetOutputLog' 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()
859                                f.close()
860                                return output_log
861                        elif 'GetResult' in input_data['Type']:
862                             if os.path.exists (rdir + "/PlaybookName.txt"):
863                                cherrypy.log("Found PlaybookName.txt file")
864                                f = open( rdir + '/PlaybookName.txt', 'r')
865                                playbooknametxt =  f.readline()
866                                f.close()
867                             else:
868                                playbooknametxt = "NA"
869
870                             # Add code to get other items not just output.log from files
871                             if os.path.exists( rdir + "/log.file"):
872                                cherrypy.log("Found log.file")
873                                out_results = "NA:"
874                                f = open( rdir + '/log.file', 'r')
875                                
876                                line =  f.readline()
877                                while line :
878                                  if "fatal" in line:
879                                    out_results = out_results +  line
880                                  elif "RECAP" in line:
881                                    out_results = out_results +  line
882                                    recap_line =  f.readline()
883                                    while recap_line :
884                                      out_results = out_results +  recap_line
885                                      recap_line =  f.readline()
886                                  line = f.readline()
887                                f.close()
888                             out_obj = {"StatusCode": 200,
889                                      "StatusMessage": "FINISHED",
890                                      "PlaybookName": playbooknametxt,
891                                      "Version": "Version",
892                                      "Duration": 200,
893                                      "Results": out_results}
894                             return out_obj
895                        else:
896                           return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
897                            
898
899                    return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
900             else:
901                 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
902         elif 'DELETE' in cherrypy.request.method:
903             input_data = parse_query_string(cherrypy.request.query_string)
904             
905             cherrypy.log( "***> in RestServer.DELETE")
906             cherrypy.log("Payload: " + str(input_data))
907             
908             if input_data['Id'] in TestRecord:
909                 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
910                     cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
911                     TestRecord.pop (input_data['Id'])
912                     if input_data['Id'] in ActiveProcess:
913                         ActiveProcess.pop (input_data['Id'])
914
915                     return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
916                 else:
917                     return {"StatusCode": 200, "StatusMessage": "PENDING"}
918             else:
919                 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
920
921
922 if __name__ == '__main__':
923
924     # Read configuration
925
926     config_file_path = "RestServer_config"
927
928     if not os.path.exists(config_file_path):
929         print '[INFO] The config file does not exist'
930         sys.exit(0)
931
932     ip = 'na'
933     AnsibleServer = 'na'
934     port = 'na'
935     tls = False
936     auth = False
937     pub = 'na'
938     priv = 'na'
939     timeout_seconds = 'na'
940     ansible_path = 'na'
941     ansible_temp = 'na'    
942     host = 'na'
943     users= 'na'
944     getresults_block = False
945     from_files = False
946     
947     file = open(config_file_path, 'r')
948     for line in file.readlines():
949         if '#' not in line:
950             if 'ip:' in line:
951                 ip = line.split(':')[1].strip()
952             elif 'AnsibleServer:' in line:
953                 AnsibleServer = line.split(':')[1].strip()
954             elif 'port:' in line:
955                 port = line.split(':')[1].strip()
956             elif 'ksalt:' in line:
957                 salt = line.split(':')[1].strip()
958             elif 'tls:' in line:
959                 tls = 'YES' in line.split(':')[1].strip().upper()
960             elif 'auth:' in line:
961                 auth = 'YES' in line.split(':')[1].strip().upper()
962             if tls and 'priv:' in line:
963                 priv = line.split(':')[1].strip()
964             if tls and 'pub:' in line:
965                 pub = line.split(':')[1].strip()
966             if tls and 'inter_cert:' in line:
967                 intermediate = line.split(':')[1].strip()
968             if 'timeout_seconds' in line:
969                 timeout_seconds = int (line.split(':')[1].strip())
970             if 'ansible_path' in line:
971                 ansible_path = line.split(':')[1].strip()
972             if 'ansible_temp' in line:
973                 ansible_temp = line.split(':')[1].strip()
974             if 'host' in line:
975                 host = line.split(':')[1].strip()
976             if 'users' in line:
977                 users = line.split(':')[1].strip()
978             if 'getresults_block' in line:
979                 getresults_block = 'YES' in line.split(':')[1].strip().upper()
980             if 'from_files' in line:
981                 from_files = 'YES' in line.split(':')[1].strip().upper()
982     file.close()
983
984     # Initialization
985     
986     global_conf = {
987         'global': {
988             'log.screen': True,
989             'response.timeout': 5400,
990             'server.socket_host': ip,
991             'server.socket_port': int(port),
992             'server.protocol_version': 'HTTP/1.1'
993             }
994         }
995
996     if tls:
997         # Use pythons built-in SSL
998         cherrypy.server.ssl_module = 'builtin'
999
1000         # Point to certificate files
1001
1002         if not os.path.exists(pub):
1003             print '[INFO] The public certificate does not exist'
1004             sys.exit(0)
1005
1006         if not os.path.exists(priv):
1007             print '[INFO] The private key does not exist'
1008             sys.exit(0)            
1009
1010         if not os.path.exists(intermediate):
1011             print '[INFO] The intermediate certificate does not exist'
1012             sys.exit(0)
1013
1014         
1015         cherrypy.server.ssl_certificate = pub
1016         cherrypy.server.ssl_certificate_chain = intermediate
1017         cherrypy.server.ssl_private_key = priv
1018
1019     if auth:
1020         # Read in and build user dictionary
1021         if not os.path.exists(users):
1022           print '[INFO] The users file does not exist: ' + users
1023           sys.exit(0)
1024         userpassdict = {}
1025         user_file = open(users, 'r')
1026         for line in user_file.readlines():
1027           if '#' not in line:
1028             id = line.split(':')[0].strip()
1029             pw = line.split(':')[1].strip()
1030             userpassdict[id] = pw
1031             #print str(userpassdict)
1032
1033         app_conf = {'/':
1034                     {'tools.auth_basic.on': True,
1035                      'tools.auth_basic.realm': 'earth',
1036                      'tools.auth_basic.checkpassword': validate_password,
1037                      }
1038                     }
1039
1040         application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1041     else:
1042         application = cherrypy.tree.mount(TestManager(), '/')
1043         
1044     cherrypy.config.update({
1045         'log.access_file': "/var/log/RestServer.access"
1046     })
1047     accessLogName = "/var/log/RestServer.access"
1048     applicationLogName = "/var/log/RestServer.log"
1049     cherrypy.config.update(global_conf)
1050
1051     log = application.log
1052     log.error_file = ""
1053     log.access_file = ""
1054     from logging import handlers
1055     applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1056     accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1057     import logging
1058     applicationLogFileHandler.setLevel(logging.DEBUG)
1059     log.error_log.addHandler(applicationLogFileHandler)
1060     log.access_log.addHandler(accessLogFileHandler)
1061
1062     # Start server
1063     
1064     cherrypy.engine.start()
1065     cherrypy.engine.block()