9da6fb9197f8d94d7601f19d659b10b6d0378fb5
[ccsdk/distribution.git] / ansible-server / src / main / scripts / UsersRestServer.py
1 '''
2 /*-
3 * ============LICENSE_START=======================================================
4 * ONAP : APPC
5 * ================================================================================
6 * Copyright (C) 2019 AT&T Intellectual Property.  All rights reserved.
7 * ================================================================================
8 * Copyright (C) 2019 Amdocs
9 * =============================================================================
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
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                         cherrypy.log( "Request USER  :                  " + cherrypy.request.login)
390                         cherrypy.log( "Request Decode: ID               " + Id)
391                         cherrypy.log( "Request Decode: VnfType          " + VnfType)
392                         cherrypy.log( "Request Decode: EnvParameters    " + json.dumps(EnvParameters))
393                         
394                         # Verify VNF_instance was passed in EnvParameters
395                         if VNF_instance != None:
396                           cherrypy.log( "Request Decode: VnfInstance      " + VNF_instance)
397                         else:
398                           cherrypy.log( "StatusCode: 107, StatusMessage: VNF_instance NOT PROVIDED" )
399                           return {"StatusCode": 107,
400                                     "StatusMessage": "VNF_instance NOT PROVIDED"}
401
402                         if inventory_names != None:
403                           cherrypy.log( "Request Decode: Inventory Names  " + inventory_names)
404                         else:
405                           cherrypy.log( "Request Decode: Inventory Names  " + "Not provided")
406
407                         cherrypy.log( "Request Decode: PlaybookName     " + PlaybookName)
408                         PlayBookFunction = PlaybookName.rsplit("/",2)[1]
409                         PlayBookFile = PlayBookFunction + "/site.yml"
410                         cherrypy.log( "Request Decode: PlaybookFunction " + PlayBookFunction)
411                         cherrypy.log( "Request Decode: Playbook file    " + PlayBookFile)
412                         
413                         BaseDir = ansible_path + "/" + PlaybookName.rsplit("/",1)[0]
414                         CopyDir = ansible_path + "/" + PlaybookName.rsplit("/",2)[0]
415                         cherrypy.log( "Request Decode: Basedir          " + BaseDir)
416                         cherrypy.log( "Request Decode: Copydir          " + CopyDir)
417                         
418
419                         PlaybookDir = ansible_temp + "/" + \
420                                       VNF_instance + "_" + str_uuid + "_" + str(Id)
421
422                         # AnsibleInv is the directory where the host file to be run exsists
423                         AnsibleInv = ansible_path + "/" + VnfType + "/latest/ansible/inventory/" + VNF_instance
424                         ArchiveFlag = False
425
426                         # Create base run directory if it doesn't exist
427                         if not os.path.exists(ansible_temp):
428                             cherrypy.log( "Creating Base Run Directory: "  + ansible_temp)
429                             os.makedirs(ansible_temp)
430
431                         if not os.path.exists( CopyDir ):
432                             cherrypy.log("Playbook Not Found")
433                             return {"StatusCode": 101,
434                                     "StatusMessage": "PLAYBOOK NOT FOUND"}
435
436                         # copy static playbook dir to run dir
437                         cherrypy.log("Copying from " + CopyDir + " to " + PlaybookDir)
438                         shutil.copytree(CopyDir, PlaybookDir)
439                         cmd="/usr/bin/find " + PlaybookDir + " -exec /usr/bin/touch {} \;"
440                         cmd="/usr/bin/find " + PlaybookDir + " -exec chmod +rx  {} \;"
441                         sys_call(cmd)
442                         cherrypy.log(cmd)
443
444                         cherrypy.log( "PlaybookDir:    " + PlaybookDir)
445                         cherrypy.log( "AnsibleInv:     " + AnsibleInv)
446
447                         #location of host file
448                         #HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
449                         #cherrypy.log("HostFile: " +  HostFile)
450
451                         # Process inventory file for target
452                     
453                         hostgrouplist = []
454                         hostnamelist = []
455
456                         NodeList = []
457                         if 'NodeList' in input_json:
458                             NodeList = input_json['NodeList']
459
460                         cherrypy.log("NodeList: " + str(NodeList));
461
462                         # if NodeList empty 
463                         if NodeList == []:
464                                 cherrypy.log( "*** NodeList - Empty ***")
465                                 #AnsibleInvFail = False
466
467                         else:
468                                 #AnsibleInvFail = False # ???
469                                 HaveNodeList = True
470
471                         ###############################################################################
472                         ##### Host file processing                          ###########################
473                         ##### 1. Use file delivered with playbook           ###########################
474                         ##### 2. If HostNames + NodeList generate and use   ###########################
475                         ##### 3. If HostNames = VM or NVF copy and use.     ###########################
476                         ###############################################################################
477
478                         #location of host file - Default
479                         HostFile = PlaybookDir + "/inventory/" + VNF_instance + "hosts"
480                         cherrypy.log("HostFile: " +  HostFile)
481
482                         # if NodeList and InventoryNames need to build host file
483                         if HaveInventoryNames & HaveNodeList:
484                            cherrypy.log("Build host file from NodeList")
485                            ret = buildHostsSysCall (input_json, PlaybookDir, inventory_names)
486                            if (ret < 0):
487                               cherrypy.log("Returning Error: Not running Playbook")
488                               return {"StatusCode": 105,
489                                     "StatusMessage": "NodeList: Missing vnfc-type field"}
490
491                            # Having been built now copy new file to correct file
492                            shutil.copy(PlaybookDir + "/host_file.txt", HostFile)
493                            cherrypy.log("Copying Generated host file to: " + HostFile)
494                         elif HaveInventoryNames & (not HaveNodeList):
495                            ### Copy Instar based Hostfile
496                            if inventory_names == "VNFC":
497                               #test if file
498                               host_file_path = "/storage/inventory/VNFC/" + VNF_instance + "hosts"
499                               if os.path.exists(host_file_path):
500                                 #Copy file
501                                 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
502                                 shutil.copy(host_file_path, HostFile)
503                               else:
504                                 cherrypy.log("Inventory file not found: " + host_file_path)
505                            elif inventory_names == "None":
506                               #test if file
507                               host_file_path = "/storage/inventory/None/" + VNF_instance + "hosts"
508                               if os.path.exists(host_file_path):
509                                 #Copy file
510                                 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
511                                 shutil.copy(host_file_path, HostFile)
512                               else:
513                                 cherrypy.log("Inventory file not found: " + host_file_path)
514                            elif inventory_names == "VM":
515                               #test if file
516                               host_file_path = "/storage/inventory/VM/" + VNF_instance + "hosts"
517                               if os.path.exists(host_file_path):
518                                 #Copy file
519                                 cherrypy.log("Copying Instar hostfile: " + host_file_path + " -> " + HostFile)
520                                 shutil.copy(host_file_path, HostFile)
521                               else:
522                                 cherrypy.log("Inventory file not found: " + host_file_path)
523
524
525                         timeout = timeout_seconds
526                         if 'Timeout' in input_json:
527                             timeout = int (input_json['Timeout'])
528                             cherrypy.log("Timeout from API: " + str(timeout))
529
530                         else:
531                             cherrypy.log("Timeout not passed from API using default: " + str(timeout))
532
533                         EnvParam = {}
534                         if 'EnvParameters' in input_json:
535                             EnvParam = input_json['EnvParameters']
536
537                         LocalParam = {}
538                         if 'LocalParameters' in input_json:
539                             LocalParam = input_json['LocalParameters']
540
541                         FileParam = {}
542                         if 'FileParameters' in input_json:
543                             FileParam = input_json['FileParameters']
544                     
545                         callback_flag = None
546                         if 'CallBack' in input_json:
547                             callback_flag = input_json['CallBack']
548
549                         # if AnsibleServer is not set to 'na'  don't send AnsibleServer in PENDING responce.
550                         if AnsibleServer != 'na':
551                                 TestRecord[Id] = {'PlaybookName': PlaybookName,
552                                           'Version': version,
553                                           'NodeList': NodeList,
554                                           'HostGroupList': hostgrouplist,
555                                           'HostNameList': hostnamelist,
556                                           'Time': time_now,
557                                           'Duration': timeout,
558                                           'Timeout': timeout,
559                                           'EnvParameters': EnvParam,
560                                           'LocalParameters': LocalParam,
561                                           'FileParameters': FileParam,
562                                           'CallBack': callback_flag,
563                                           'Result': {"StatusCode": 100,
564                                                      "StatusMessage": 'PENDING',
565                                                      "AnsibleServer": str(AnsibleServer),
566                                                      "ExpectedDuration": str(timeout) + "sec"},
567                                           'Log': '',
568                                           'Output': {},
569                                           'Path': PlaybookDir,
570                                           'Mandatory': None}
571                         else:
572                                 TestRecord[Id] = {'PlaybookName': PlaybookName,
573                                           'Version': version,
574                                           'NodeList': NodeList,
575                                           'HostGroupList': hostgrouplist,
576                                           'HostNameList': hostnamelist,
577                                           'Time': time_now,
578                                           'Duration': timeout,
579                                           'Timeout': timeout,
580                                           'EnvParameters': EnvParam,
581                                           'LocalParameters': LocalParam,
582                                           'FileParameters': FileParam,
583                                           'CallBack': callback_flag,
584                                           'Result': {"StatusCode": 100,
585                                                      "StatusMessage": 'PENDING',
586                                                      "ExpectedDuration": str(timeout) + "sec"},
587                                           'Log': '',
588                                           'Output': {},
589                                           'Path': PlaybookDir,
590                                           'Mandatory': None}
591
592                         cherrypy.log("Test_Record: " +  str(TestRecord[Id]))
593                         # Write files
594                         
595                         if not TestRecord[Id]['FileParameters'] == {}:
596                             for key in TestRecord[Id]['FileParameters']:
597                                 filename = key
598                                 filecontent = TestRecord[Id]['FileParameters'][key]
599                                 f = open(PlaybookDir + "/" + filename, "w")
600                                 f.write(filecontent)
601                                 f.close()
602                                 
603                         
604                         # Process playbook
605                         if os.path.exists( ansible_path + '/' + PlaybookName):
606                             AnsiblePlaybookFail = False
607                                     
608                         if AnsiblePlaybookFail:
609                             #if os.path.exists(PlaybookDir):
610                                 #shutil.rmtree (PlaybookDir)
611                             del TestRecord[Id]
612                             return {"StatusCode": 101,
613                                     "StatusMessage": "PLAYBOOK NOT FOUND"}
614                         else:
615
616                             # Test EnvParameters
617                             playbook_path = PlaybookDir
618
619                             # Store local vars
620                             if not os.path.exists(playbook_path + "/vars"):
621                                 os.mkdir(playbook_path + "/vars")
622                             if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
623                                 os.mknod(playbook_path + "/vars/defaults.yml")
624
625                             ###################################################
626                             # PAP
627                             #write local parameters passed into defaults.yml
628                             # PAP
629                             f = open(playbook_path + "/vars/defaults.yml","a")
630                             #for id, record in TestRecord.items():
631                             print TestRecord[Id]['LocalParameters']
632                             local_parms = TestRecord[Id]['LocalParameters']
633                             for key, value in local_parms.items():
634                                 f.write(key +"=" + value + "\n");
635                             f.close()
636                             ###################################################
637                                  
638                             for key in TestRecord[Id]['LocalParameters']:
639                                 host_index = []
640                                 for i in range(len(TestRecord[Id]['HostNameList'])):
641                                     if key in TestRecord[Id]['HostNameList'][i]:
642                                         host_index.append(i)
643                                 if len(host_index) == 0:
644                                     for i in range(len(TestRecord[Id]['HostGroupList'])):
645                                         if key in TestRecord[Id]['HostGroupList'][i]:
646                                             host_index.append(i)
647                                 if len(host_index) > 0:
648                                     for i in range(len(host_index)):
649                                         f = open(playbook_path + "/vars/" +
650                                                  TestRecord[Id]['HostNameList'][host_index[i]] +
651                                                  ".yml", "a")
652                                         for param in TestRecord[Id]['LocalParameters'][key]:
653                                             f.write(param + ": " +
654                                              str (TestRecord[Id]['LocalParameters'][key][param]) +
655                                                   "\n")
656                                         f.close()
657
658     
659                             # write some info out to files before running
660                             f = open(playbook_path + "/PlaybookName.txt", "a")
661                             f.write(PlaybookName)
662                             f.close()
663                             f = open(playbook_path + "/PlaybookExDir.txt", "a")
664                             f.write(PlaybookDir + "/" + PlayBookFunction)
665                             f.close()
666                             f = open(playbook_path + "/JsonRequest.txt", "w")
667                             #f.write(str(input_json))
668                             print( json.dumps(input_json, indent=4, sort_keys=True))
669                             f.write( json.dumps(input_json, indent=4, sort_keys=True))
670                             f.close()
671
672
673                             # Check that HostFile exists
674                             if not os.path.isfile(HostFile):
675                               cherrypy.log("Inventory file Not Found: " + HostFile)
676                               return {"StatusCode": 101,
677                                    "StatusMessage": "PLAYBOOK INVENTORY FILE NOT FOUND"}
678    
679                             # Cannot use thread because ansible module uses
680                             # signals which are only supported in main thread.
681                             # So use multiprocess with shared object
682                                        # args = (callback, Id,  PlaybookDir + "/" + AnsibleInv,
683
684                             p = Process(target = RunAnsible_Playbook,
685                                         args = (callback, Id,  HostFile,
686                                                 PlaybookDir + '/' + PlayBookFile,
687                                                 NodeList, TestRecord, PlaybookDir + "/" + PlayBookFunction,
688                                                 ArchiveFlag))
689                             p.start()
690                             ActiveProcess[Id] = p
691                             return TestRecord[Id]['Result']
692                     else:
693                         cherrypy.log("TEST ID ALREADY DEFINED")
694                         return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
695
696                 else:
697                     return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
698                 
699             else:
700                 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME, EnvParameters"}
701
702         elif 'GET' in cherrypy.request.method:
703             
704             # Lets pause for a second just incase the resquest was just kicked off
705             time.sleep(1)
706
707             input_data = parse_query_string(cherrypy.request.query_string)
708              
709             # Verify we have a Type passed in GET request
710             if not ( 'Type' in input_data):
711                 return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
712
713             cherrypy.log( "Request USER:             " + cherrypy.request.login)
714             cherrypy.log("Payload: " + str(input_data) + " Type " + input_data['Type'])
715
716             if 'LogRest' in input_data['Type']:
717                 sys.stdout.close()
718                 sys.stdout = open("/var/log/RestServer.log", "w")
719
720             # Just a debug to dump any records
721             if 'GetStatus' in input_data['Type']:
722                 cherrypy.log( "******** Dump Records **********")
723                 if TestRecord.items():
724                   for id, record in TestRecord.items():
725                     cherrypy.log( "    Id: " + id)
726                     cherrypy.log( "Record: " + str(record))
727                 else:
728                   cherrypy.log(" No Records to dump")
729             
730             if 'Id' in input_data and 'Type' in input_data:
731                 if not ('GetResult' in input_data['Type'] or 'GetOutputLog' in input_data['Type'] or'GetOutput' in input_data['Type'] or 'GetLog' in input_data['Type']):
732                     return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
733                 if input_data['Id'] in TestRecord:
734                     
735                     if 'GetResult' in input_data['Type']:
736                         
737                         cherrypy.log( " ** GetResult for: " + str (input_data['Id']))
738
739                         if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
740
741
742                             #check if playbook is still running
743                             while ActiveProcess[input_data['Id']].is_alive():
744                                 cherrypy.log( "*** Playbook running returning PENDING for " + str(input_data['Id']))
745                                 ##
746                                 ## If still running return PENDING response
747                                 ##
748                                 if AnsibleServer != 'na':
749                                    return {"StatusCode": 100,
750                                                      "StatusMessage": 'PENDING',
751                                                      "AnsibleServer": str(AnsibleServer)}
752                                 else: 
753                                    return {"StatusCode": 100,
754                                                      "StatusMessage": 'PENDING'}
755                                 #time.sleep(5)
756
757                             #cherrypy.log( "*** Request released " + input_data['Id'])
758
759                         cherrypy.log(str( TestRecord[input_data['Id']]['Result']))
760                         cherrypy.log("Output: " + str( TestRecord[input_data['Id']]['Output']))
761                         cherrypy.log("StatusCode: " + str( TestRecord[input_data['Id']]['Result']['StatusCode']))
762                         cherrypy.log("StatusMessage: " + str( TestRecord[input_data['Id']]['Result']['StatusMessage']))
763
764                         #out_obj gets returned to GET request
765                         if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
766                             out_obj = TestRecord[input_data['Id']]['Result']['Results']
767                         else:
768                             out_obj = {"StatusCode": 200,
769                                    "StatusMessage": "FINISHED",
770                                    "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
771                                    "Version": TestRecord[input_data['Id']]["Version"],
772                                    "Duration": TestRecord[input_data['Id']]["Duration"],
773                                    "Output": TestRecord[input_data['Id']]["Output"]["Output"],
774                                    "Results": TestRecord[input_data['Id']]['Result']['Results']}
775                         if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
776                             cherrypy.log("TestRecord has Output:" + str(TestRecord[input_data['Id']]['Output']['Output']))
777                             # PAP                
778                             for key in out_obj["Results"]:
779                                 cherrypy.log("Output key: " + str(key))
780                                 if key in TestRecord[input_data['Id']]['Output']['Output']:
781                                     out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
782
783                         cherrypy.log("***** GET RETURNING RESULTS Back ****")
784                         cherrypy.log(str(out_obj))
785                         return out_obj
786
787                     elif 'GetStatus' in input_data['Type']:
788                         print " Dump Records"
789                         for id, record in TestRecord,items():
790                            print " id: " + id
791                            print "   Record:" + str(reecord)
792
793                     elif 'GetOutput' in input_data['Type']:
794
795                         if TestRecord[input_data['Id']]['Output'] == {} and \
796                                getresults_block:
797
798                             cherrypy.log( "*** Request blocked " + input_data['Id'])
799                             
800                             while TestRecord[input_data['Id']]['Output'] == {} \
801                                       or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
802                                 time.sleep(5)
803
804                             cherrypy.log( "*** Request released " + input_data['Id'])
805                         
806                         cherrypy.log( "Output: " + str(TestRecord[input_data['Id']]['Output']))
807                         return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
808                     elif 'GetOutputLog' in input_data['Type']:
809 #XXXXXXXXXXX
810                        if glob.glob(  ansible_temp + '/*_' + input_data['Id']):
811                           id = input_data['Id']
812                           cherrypy.log("Old directory found for ID: " + id)
813                           run_dir = glob.glob(  ansible_temp + '/*_' + input_data['Id'])
814                           for dir in run_dir:
815                               rdir=dir
816                           if os.path.exists (rdir + "/PlaybookExDir.txt"):
817                                cherrypy.log("Found PlaybookExDir.txt file")
818                                f = open( rdir + '/PlaybookExDir.txt', 'r')
819                                playbookexdir =  f.readline()
820                                rdir = playbookexdir
821                                f.close()
822                           cherrypy.log("Id:     " + id)
823                           cherrypy.log("RunDir: " + rdir)
824                           if os.path.exists( rdir + "/output.log"):
825                                cherrypy.log("Found output.log file")
826                                f = open( rdir + '/output.log', 'r')
827                                output_log =  f.readline()
828                                f.close()
829                                return output_log
830                        else:
831                          return
832
833 #XXXXXXXXXXX
834                     else:
835                         # GetLog
836
837                         if TestRecord[input_data['Id']]['Log'] == '' and \
838                                getresults_block:
839
840                             cherrypy.log( "*** Request blocked " + input_data['Id'])
841                             
842                             while TestRecord[input_data['Id']]['Log'] == '' \
843                                       or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
844                                 time.sleep(5)
845
846                             cherrypy.log( "*** Request released " + input_data['Id'])
847                             
848                         cherrypy.log( "Log:" + str(TestRecord[input_data['Id']]['Log']))
849                         return {"Log": TestRecord[input_data['Id']]['Log']}
850                 else:
851                    # Not in memory check for a file 
852                    if glob.glob(  ansible_temp + '/*_' + input_data['Id']):
853                        id = input_data['Id']
854                        cherrypy.log("Old directory found for ID: " + id)
855                        run_dir = glob.glob(  ansible_temp + '/*_' + input_data['Id'])
856                        for dir in run_dir:
857                            rdir=dir
858                        if os.path.exists (rdir + "/PlaybookExDir.txt"):
859                             cherrypy.log("Found PlaybookExDir.txt file")
860                             f = open( rdir + '/PlaybookExDir.txt', 'r')
861                             playbookexdir =  f.readline()
862                             rdir = playbookexdir
863                             f.close()
864                        cherrypy.log("Id:     " + id)
865                        cherrypy.log("RunDir: " + rdir)
866                        if 'GetLog' in input_data['Type']:
867                          if os.path.exists( rdir + "/output.log"):
868                             cherrypy.log("Found output.log file")
869                             f = open( rdir + '/output.log', 'r')
870                             output_log =  f.readline()
871                             f.close()
872                             return output_log
873                        elif 'GetOutputLog' in input_data['Type']:
874                             if os.path.exists( rdir + "/output.log"):
875                                cherrypy.log("Found output.log file")
876                                f = open( rdir + '/output.log', 'r')
877                                output_log =  f.readline()
878                                f.close()
879                                return output_log
880                        elif 'GetResult' in input_data['Type']:
881                             if os.path.exists (rdir + "/PlaybookName.txt"):
882                                cherrypy.log("Found PlaybookName.txt file")
883                                f = open( rdir + '/PlaybookName.txt', 'r')
884                                playbooknametxt =  f.readline()
885                                f.close()
886                             else:
887                                playbooknametxt = "NA"
888
889                             # Add code to get other items not just output.log from files
890                             if os.path.exists( rdir + "/log.file"):
891                                cherrypy.log("Found log.file")
892                                out_results = "NA:"
893                                f = open( rdir + '/log.file', 'r')
894                                
895                                line =  f.readline()
896                                while line :
897                                  if "fatal" in line:
898                                    out_results = out_results +  line
899                                  elif "RECAP" in line:
900                                    out_results = out_results +  line
901                                    recap_line =  f.readline()
902                                    while recap_line :
903                                      out_results = out_results +  recap_line
904                                      recap_line =  f.readline()
905                                  line = f.readline()
906                                f.close()
907                             out_obj = {"StatusCode": 200,
908                                      "StatusMessage": "FINISHED",
909                                      "PlaybookName": playbooknametxt,
910                                      "Version": "Version",
911                                      "Duration": 200,
912                                      "Results": out_results}
913                             return out_obj
914                        else:
915                           return {"StatusCode": 500, "StatusMessage": "PLAYBOOK FAILED "}
916                            
917
918                    return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
919             else:
920                 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
921         elif 'DELETE' in cherrypy.request.method:
922             input_data = parse_query_string(cherrypy.request.query_string)
923             
924             cherrypy.log( "***> in RestServer.DELETE")
925             cherrypy.log("Payload: " + str(input_data))
926             
927             if input_data['Id'] in TestRecord:
928                 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
929                     cherrypy.log(" Path: " + str(TestRecord[input_data['Id']]['Path']))
930                     TestRecord.pop (input_data['Id'])
931                     if input_data['Id'] in ActiveProcess:
932                         ActiveProcess.pop (input_data['Id'])
933
934                     return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
935                 else:
936                     return {"StatusCode": 200, "StatusMessage": "PENDING"}
937             else:
938                 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
939
940
941 if __name__ == '__main__':
942
943     # Read configuration
944
945     config_file_path = "RestServer_config"
946
947     if not os.path.exists(config_file_path):
948         print '[INFO] The config file does not exist'
949         sys.exit(0)
950
951     ip = 'na'
952     AnsibleServer = 'na'
953     port = 'na'
954     tls = False
955     auth = False
956     pub = 'na'
957     priv = 'na'
958     timeout_seconds = 'na'
959     ansible_path = 'na'
960     ansible_temp = 'na'    
961     host = 'na'
962     users= 'na'
963     getresults_block = False
964     from_files = False
965     
966     file = open(config_file_path, 'r')
967     for line in file.readlines():
968         if '#' not in line:
969             if 'ip:' in line:
970                 ip = line.split(':')[1].strip()
971             elif 'AnsibleServer:' in line:
972                 AnsibleServer = line.split(':')[1].strip()
973             elif 'port:' in line:
974                 port = line.split(':')[1].strip()
975             elif 'ksalt:' in line:
976                 salt = line.split(':')[1].strip()
977             elif 'tls:' in line:
978                 tls = 'YES' in line.split(':')[1].strip().upper()
979             elif 'auth:' in line:
980                 auth = 'YES' in line.split(':')[1].strip().upper()
981             if tls and 'priv:' in line:
982                 priv = line.split(':')[1].strip()
983             if tls and 'pub:' in line:
984                 pub = line.split(':')[1].strip()
985             if tls and 'inter_cert:' in line:
986                 intermediate = line.split(':')[1].strip()
987             if 'timeout_seconds' in line:
988                 timeout_seconds = int (line.split(':')[1].strip())
989             if 'ansible_path' in line:
990                 ansible_path = line.split(':')[1].strip()
991             if 'ansible_temp' in line:
992                 ansible_temp = line.split(':')[1].strip()
993             if 'host' in line:
994                 host = line.split(':')[1].strip()
995             if 'users' in line:
996                 users = line.split(':')[1].strip()
997             if 'getresults_block' in line:
998                 getresults_block = 'YES' in line.split(':')[1].strip().upper()
999             if 'from_files' in line:
1000                 from_files = 'YES' in line.split(':')[1].strip().upper()
1001     file.close()
1002
1003     # Initialization
1004     
1005     global_conf = {
1006         'global': {
1007             'log.screen': True,
1008             'response.timeout': 5400,
1009             'server.socket_host': ip,
1010             'server.socket_port': int(port),
1011             'server.protocol_version': 'HTTP/1.1'
1012             }
1013         }
1014
1015     if tls:
1016         # Use pythons built-in SSL
1017         cherrypy.server.ssl_module = 'builtin'
1018
1019         # Point to certificate files
1020
1021         if not os.path.exists(pub):
1022             print '[INFO] The public certificate does not exist'
1023             sys.exit(0)
1024
1025         if not os.path.exists(priv):
1026             print '[INFO] The private key does not exist'
1027             sys.exit(0)            
1028
1029         if not os.path.exists(intermediate):
1030             print '[INFO] The intermediate certificate does not exist'
1031             sys.exit(0)
1032
1033         
1034         cherrypy.server.ssl_certificate = pub
1035         cherrypy.server.ssl_certificate_chain = intermediate
1036         cherrypy.server.ssl_private_key = priv
1037
1038     if auth:
1039         # Read in and build user dictionary
1040         if not os.path.exists(users):
1041           print '[INFO] The users file does not exist: ' + users
1042           sys.exit(0)
1043         userpassdict = {}
1044         user_file = open(users, 'r')
1045         for line in user_file.readlines():
1046           if '#' not in line:
1047             id = line.split(':')[0].strip()
1048             pw = line.split(':')[1].strip()
1049             userpassdict[id] = pw
1050             #print str(userpassdict)
1051
1052         app_conf = {'/':
1053                     {'tools.auth_basic.on': True,
1054                      'tools.auth_basic.realm': 'earth',
1055                      'tools.auth_basic.checkpassword': validate_password,
1056                      }
1057                     }
1058
1059         application = cherrypy.tree.mount(TestManager(), '/', app_conf)
1060     else:
1061         application = cherrypy.tree.mount(TestManager(), '/')
1062         
1063     cherrypy.config.update({
1064         'log.access_file': "/var/log/RestServer.access"
1065     })
1066     accessLogName = "/var/log/RestServer.access"
1067     applicationLogName = "/var/log/RestServer.log"
1068     cherrypy.config.update(global_conf)
1069
1070     log = application.log
1071     log.error_file = ""
1072     log.access_file = ""
1073     from logging import handlers
1074     applicationLogFileHandler = handlers.RotatingFileHandler(applicationLogName, 'a', 1000000, 5000)
1075     accessLogFileHandler = handlers.RotatingFileHandler(accessLogName, 'a', 1000000, 5000)
1076     import logging
1077     applicationLogFileHandler.setLevel(logging.DEBUG)
1078     log.error_log.addHandler(applicationLogFileHandler)
1079     log.access_log.addHandler(accessLogFileHandler)
1080
1081     # Start server
1082     
1083     cherrypy.engine.start()
1084     cherrypy.engine.block()