4758a9b9b0a0774368947b8b99c4b99ac1408c26
[ccsdk/distribution.git] / ansible-server / src / main / scripts / RestServer.py
1 '''
2 /*-
3 * ============LICENSE_START=======================================================
4 * ONAP : APPC
5 * ================================================================================
6 * Copyright (C) 2017 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
32 import requests
33
34 import cherrypy
35 from cherrypy.lib.httputil import parse_query_string
36 from cherrypy.lib import auth_basic
37
38 from multiprocessing import Process, Manager
39
40 from AnsibleModule import ansibleSysCall
41
42 import AnsibleSql
43 from AnsibleSql import readPlaybook, readCredentials
44
45 from os import listdir
46 from os.path import isfile, join
47
48 TestRecord = Manager().dict()
49 ActiveProcess = {}
50
51 def sys_call (cmd):
52     p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
53     output = p.stdout.readlines()
54     retval = p.wait()
55     if len (output) > 0:
56         for i in range(len(output)):
57             output[i] = output[i].strip()
58     return retval, output
59
60 def callback (Id, Result, Output, Log, returncode):
61     
62     print "***> in RestServer.callback"
63
64     if Id in TestRecord:
65         time_now = datetime.datetime.utcnow()
66         delta_time = (time_now - TestRecord[Id]['Time']).total_seconds()
67         Result['PlaybookName'] = TestRecord[Id]['PlaybookName']
68         Result['Version'] = TestRecord[Id]['Version']
69         if returncode == 137:
70             Result['StatusCode'] = 500
71             Result['StatusMessage'] = "TERMINATED"
72         else:
73             Result['StatusCode'] = 200
74             Result['StatusMessage'] = "FINISHED"
75
76         # Need to update the whole data structure for key=Id otherwise Manager is not updated
77         TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'],
78                           'LCM': TestRecord[Id]['LCM'],
79                           'Version': TestRecord[Id]['Version'],
80                           'NodeList': TestRecord[Id]['NodeList'],
81                           'HostGroupList': TestRecord[Id]['HostGroupList'],
82                           'HostNameList': TestRecord[Id]['HostNameList'],
83                           'Time': TestRecord[Id]['Time'],
84                           'Timeout': TestRecord[Id]['Timeout'],
85                           'Duration': str(delta_time),
86                           'EnvParameters': TestRecord[Id]['EnvParameters'],
87                           'LocalParameters': TestRecord[Id]['LocalParameters'],
88                           'FileParameters': TestRecord[Id]['FileParameters'],
89                           'CallBack': TestRecord[Id]['CallBack'],
90                           'Result': Result,
91                           'Log': Log, 
92                           'Output': Output, 
93                           'Path': TestRecord[Id]['Path'],
94                           'Mandatory': TestRecord[Id]['Path']}
95
96         if not TestRecord[Id]['CallBack'] == None:
97             
98             # Posting results to callback server
99
100             data = {"StatusCode": 200,
101                     "StatusMessage": "FINISHED",
102                     "PlaybookName": TestRecord[Id]["PlaybookName"],
103                     "Version": TestRecord[Id]["Version"],
104                     "Duration": TestRecord[Id]["Duration"],
105                     "Results": TestRecord[Id]['Result']['Results']}
106
107             if not TestRecord[Id]['Output']['Output'] == {}:
108                 for key in data["Results"]:
109                     if key in TestRecord[Id]['Output']['Output']:
110                         data["Results"][key]["Output"] = TestRecord[Id]['Output']['Output'][key]
111
112             print "     Posting to", TestRecord[Id]['CallBack']
113             
114             s = requests.Session()
115             r = s.post(TestRecord[Id]['CallBack'], data = json.dumps(data),
116                        headers = {'content-type': 'application/json'})
117             print  "     Response", r.status_code, r.text
118
119 def RunAnsible_Playbook (callback, Id, Inventory, Playbook, NodeList, TestRecord,
120                          Path, ArchiveFlag):
121
122     print "***> in RestServer.RunAnsible_Playbook"
123
124     # Run test in playbook for given target
125     Result = ''
126
127     retval, log, returncode = ansibleSysCall (Inventory, Playbook, NodeList,
128                                               TestRecord[Id]['Mandatory'],
129                                               TestRecord[Id]['EnvParameters'],
130                                               TestRecord[Id]['LocalParameters'],
131                                               TestRecord[Id]['LCM'],
132                                               TestRecord[Id]['Timeout'])
133
134
135     print "   returncode:", returncode
136     print "   retval:    ", retval
137     print "   log:       ", log
138     
139     Log = ''.join(log)
140     Output = {'Output': {}}
141     
142     onlyfiles = [f for f in listdir(Path)
143                  if isfile(join(Path, f))]
144
145     for file in onlyfiles:
146         if "results.txt" in file:
147             f = open(Path + "/" + file, "r")
148             key = file.split("_")[0]
149             Output['Output'][key] = f.read()
150             f.close()
151
152     Result = {'Results': {}}
153     if 'could not be found' in Log:
154         Result['Results'] = {"StatusCode": 101,
155                              "StatusMessage": "PLAYBOOK NOT FOUND"}
156     if returncode == 137:
157         Result['Results'] = {"StatusCode": 500,
158                              "StatusMessage": "TERMINATED"}
159
160     elif TestRecord[Id]['NodeList'] == []:
161         
162         host_index = None
163         
164         if 'TargetNode' in TestRecord[Id]['EnvParameters']:
165             targetlist = TestRecord[Id]['EnvParameters']['TargetNode'].split(' ')
166         else:
167             targetlist = ["localhost"]
168             
169         for key in retval:
170             for i in range (len(targetlist)):
171                 if key in targetlist[i]:
172                     host_index = i
173     
174             if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
175                    int(retval[key][3]) == 0:
176
177                 if host_index:
178                     Result['Results'][targetlist[host_index]] = \
179                              {"GroupName": 'na', "StatusCode": 200, \
180                               "StatusMessage": "SUCCESS"}
181                 else:
182                     Result['Results'][key] = \
183                              {"GroupName": 'na', "StatusCode": 200, \
184                               "StatusMessage": "SUCCESS"}                    
185             elif int(retval[key][2]) > 0:
186                 if host_index:
187                     Result['Results'][targetlist[host_index]] = \
188                        {"GroupName": 'na', "StatusCode": 400, \
189                         "StatusMessage": "NOT REACHABLE"}
190                 else:
191                     Result['Results'][key] = \
192                        {"GroupName": 'na', "StatusCode": 400, \
193                         "StatusMessage": "NOT REACHABLE"}                    
194             elif int(retval[key][3]) > 0:
195                 if host_index:                
196                     Result['Results'][targetlist[host_index]] = \
197                        {"GroupName": 'na', "StatusCode": 400, \
198                         "StatusMessage": "FAILURE"}
199                 else:
200                     Result['Results'][key] = \
201                        {"GroupName": 'na', "StatusCode": 400, \
202                         "StatusMessage": "FAILURE"}                    
203     else:
204         
205         for key in retval:
206
207             if len(TestRecord[Id]['HostNameList']) > 0:
208
209                 host_index = []
210                 for i in range (len(TestRecord[Id]['HostNameList'])):
211                     if key in TestRecord[Id]['HostNameList'][i]:
212                         host_index.append(i)
213
214                 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
215                        int(retval[key][3]) == 0:
216
217                     if len(host_index) > 0:
218                         Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
219                           {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
220                            "StatusCode": 200, "StatusMessage": "SUCCESS"}
221                     
222                         for i in range (1, len(host_index)):
223                             Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
224                              "," + TestRecord[Id]['HostGroupList'][host_index[i]]
225                     else:
226                        Result['Results'][key] = \
227                           {"GroupName": key,
228                            "StatusCode": 200, "StatusMessage": "SUCCESS"}  
229
230                 elif int(retval[key][2]) > 0:
231
232                     if len(host_index) > 0:
233                         Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
234                           {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
235                            "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
236                     
237                         for i in range (1, len(host_index)):
238                             Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
239                              "," + TestRecord[Id]['HostGroupList'][host_index[i]]
240                     else:
241                        Result['Results'][key] = \
242                           {"GroupName": key,
243                            "StatusCode": 200, "StatusMessage": "NOT REACHABLE"}  
244                     
245                 elif int(retval[key][3]) > 0:
246
247                     if len(host_index) > 0:
248                         Result['Results'][TestRecord[Id]['HostNameList'][host_index[0]]] = \
249                           {"GroupName": TestRecord[Id]['HostGroupList'][host_index[0]],
250                            "StatusCode": 400, "StatusMessage": "FAILURE"}
251                     
252                         for i in range (1, len(host_index)):
253                             Result['Results'][TestRecord[Id]['HostNameList'][host_index[i]]]["GroupName"]+=\
254                              "," + TestRecord[Id]['HostGroupList'][host_index[i]]
255                     else:
256                        Result['Results'][key] = \
257                           {"GroupName": key,
258                            "StatusCode": 200, "StatusMessage": "FAILURE"}                          
259             else:
260                 host_index = None
261                 for i in range (len(TestRecord[Id]['NodeList'])):
262                     if key in TestRecord[Id]['NodeList'][i]:
263                         host_index = i
264     
265                 if int(retval[key][0]) > 0 and int(retval[key][2]) == 0 and \
266                        int(retval[key][3]) == 0:
267                     Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
268                              {"GroupName": 'na', "StatusCode": 200, \
269                              "StatusMessage": "SUCCESS"}
270                 elif int(retval[key][2]) > 0:
271                     Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
272                        {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "NOT REACHABLE"}
273                 elif int(retval[key][3]) > 0:
274                     Result['Results'][TestRecord[Id]['NodeList'][host_index]] = \
275                        {"GroupName": 'na', "StatusCode": 400, "StatusMessage": "FAILURE"}
276     
277     callback (Id, Result, Output, Log, returncode)
278
279 class TestManager (object):
280
281     @cherrypy.expose
282     @cherrypy.tools.json_out()
283     @cherrypy.tools.json_in()
284     @cherrypy.tools.allow(methods=['POST', 'GET', 'DELETE'])
285
286     def Dispatch(self, **kwargs):
287
288         # Let cherrypy error handler deal with malformed requests
289         # No need for explicit error handler, we use default ones
290
291         time_now = datetime.datetime.utcnow()
292
293         # Erase old test results (2x timeout)
294         if TestRecord:
295             for key in TestRecord.copy():
296                 delta_time = (time_now - TestRecord[key]['Time']).seconds
297                 if delta_time > 2*TestRecord[key]['Timeout']:
298                     print "Deleted history for test", key
299                     if os.path.exists(TestRecord[key]['Path']):
300                         shutil.rmtree (TestRecord[key]['Path'])
301                     del TestRecord[key]
302                     
303         print "***> in RestServer.Dispatch:", cherrypy.request.method
304
305         HomeDir = os.path.dirname(os.path.realpath("~/"))
306
307         if 'POST' in cherrypy.request.method:
308             
309             input_json = cherrypy.request.json
310             print "   Payload:      ", input_json
311
312             if 'Id' in input_json and 'PlaybookName' in input_json:
313
314                 if True:
315
316                     if not input_json['Id'] in TestRecord:
317                     
318                         Id = input_json['Id']
319                         PlaybookName = input_json['PlaybookName']
320
321                         version = None
322                         if 'Version' in input_json:
323                             version = input_json['Version']
324                     
325                         AnsibleInvFail = True
326                         AnsiblePlaybookFail = True
327
328                         MySqlConFail = True
329                         MySqlCause = ''
330
331                         LocalNodeList = None
332
333                         str_uuid = str (uuid.uuid4())
334                         
335                         LCM = PlaybookName.split(".")[0].split('_')[-1]
336                         PlaybookDir = HomeDir + "/" + ansible_temp + "/" + \
337                                       PlaybookName.split(".")[0] + "_" + str_uuid
338                         AnsibleInv = LCM + "_" + "inventory"
339                         ArchiveFlag = False
340
341                         print "   LCM:          ", LCM
342                         print "   PlaybookDir:  ", ansible_temp + PlaybookDir.split(ansible_temp)[1]
343                         print "   AnsibleInv:   ", AnsibleInv
344                         print "   ansible_temp: ", ansible_temp
345
346                         if not os.path.exists(HomeDir + "/" + ansible_temp):
347                             os.makedirs(HomeDir + "/" + ansible_temp)
348
349                         os.mkdir(PlaybookDir)
350
351                         # Process inventory file for target
352                     
353                         hostgrouplist = []
354                         hostnamelist = []
355
356                         NodeList = []
357                         if 'NodeList' in input_json:
358                             NodeList = input_json['NodeList']
359
360                         print "   NodeList:     ", NodeList
361
362                         if NodeList == []:
363                                 # By default set to local host
364                                 AnsibleInvFail = False
365
366                                 LocalNodeList = "host"
367                                 LocalCredentials = "localhost   ansible_connection=local"
368                                 f = open(PlaybookDir + "/" + AnsibleInv, "w")
369                                 f.write("[" + LocalNodeList + "]\n")
370                                 f.write(LocalCredentials)
371                                 f.close()
372
373                         else:
374
375                                 if from_files:
376                                     
377                                     # Get credentials from file
378
379                                     data_inventory_orig = {}
380                                     data_inventory_target = {}
381                                     curr_group = None
382
383                                     print "***>", ansible_path + "/" + ansible_inv
384                                     f = open(ansible_path + "/" + ansible_inv, "r")
385                                     for line in f:
386                                         line = line.rstrip()
387                                         
388                                         if len(line)> 0:
389                                             if '#' not in line:
390                                                 if "[" in line and "]" in line:
391                                                     data_inventory_orig[line] = []
392                                                     curr_group = line
393                                                 else:
394                                                     data_inventory_orig[curr_group].append(line)
395                                     f.close()
396
397                                     for node in NodeList:
398                                         Fail = True
399                                         if "[" + node + "]" in data_inventory_orig:
400                                             if not "[" + node + "]" in data_inventory_target:
401
402                                                 print "RESET", "[" + node + "]"
403                                                 data_inventory_target["[" + node + "]"] = []
404                                             else:
405                                                 print "OK", "[" + node + "]"
406                                             Fail = False
407                                             for cred in data_inventory_orig["[" + node + "]"]:
408                                                 data_inventory_target["[" + node + "]"].append(cred)
409                                                 
410                                         else:
411                                             for key in data_inventory_orig:
412                                                 if node in " ".join(data_inventory_orig[key]):
413                                                     if not key in data_inventory_target:
414                                                         data_inventory_target[key] = []
415                                                     for cred in data_inventory_orig[key]:
416                                                         if node in cred:
417                                                             data_inventory_target[key].append(cred)
418                                                             Fail = False
419
420                                         if Fail:
421                                             data_inventory_target["["+node+"]"] = \
422                                                    [node + " ansible_connection=ssh ansible_ssh_user=na ansible_ssh_private_key_file=na"]
423                                     
424                                     AnsibleInvFail = False
425
426                                     f = open(PlaybookDir + "/" + AnsibleInv, "w")
427                                     for key in data_inventory_target:
428                                         f.write(key + "\n")
429                                         for rec in data_inventory_target[key]:
430                                             hostgrouplist.append(key.replace("[", '').replace("]", ''))
431                                             hostnamelist.append(rec.split(' ')[0])
432                                             f.write(rec + "\n")
433                                     f.close()
434
435                                 else:
436                                     
437                                     # Get credentials from mySQL
438                             
439                                     sqlintf = AnsibleSql.mySql (host, user, passwd,
440                                                                 db)
441                                     if sqlintf.con:
442                                         MySqlConFail = False
443                                         errorCode, diag = readCredentials (sqlintf,
444                                                                            NodeList)
445                                     
446                                         print errorCode, diag 
447                                         if len (diag) > 0:
448                                             f = open(PlaybookDir + "/" + AnsibleInv,
449                                                      "w")
450                                             AnsibleInvFail = False
451                                             # [hostgroup, hostname, credentials]
452                                             for i in range(len(diag)):
453                                                 f.write('[' +  diag[i][0] + ']' + "\n")
454                                                 f.write(diag[i][1]+ " " +  diag[i][2] + "\n\n")
455                                                 hostgrouplist.append(diag[i][0])
456                                                 hostnamelist.append(diag[i][1])
457                                             f.close()
458                                         else:
459                                             MySqlConFailCause = sqlintf.error
460                                     sqlintf.Close()
461
462                         timeout = timeout_seconds
463                         if 'Timeout' in input_json:
464                             timeout = int (input_json['Timeout'])
465
466                         EnvParam = {}
467                         if 'EnvParameters' in input_json:
468                             EnvParam = input_json['EnvParameters']
469
470                         LocalParam = {}
471                         if 'LocalParameters' in input_json:
472                             LocalParam = input_json['LocalParameters']
473
474                         FileParam = {}
475                         if 'FileParameters' in input_json:
476                             FileParam = input_json['FileParameters']
477                     
478                         callback_flag = None
479                         if 'CallBack' in input_json:
480                             callback_flag = input_json['CallBack']
481
482                         TestRecord[Id] = {'PlaybookName': PlaybookName,
483                                           'LCM': LCM,
484                                           'Version': version,
485                                           'NodeList': NodeList,
486                                           'HostGroupList': hostgrouplist,
487                                           'HostNameList': hostnamelist,
488                                           'Time': time_now,
489                                           'Duration': timeout,
490                                           'Timeout': timeout,
491                                           'EnvParameters': EnvParam,
492                                           'LocalParameters': LocalParam,
493                                           'FileParameters': FileParam,
494                                           'CallBack': callback_flag,
495                                           'Result': {"StatusCode": 100,
496                                                      "StatusMessage": 'PENDING',
497                                                      "ExpectedDuration": str(timeout) + "sec"},
498                                           'Log': '',
499                                           'Output': {},
500                                           'Path': PlaybookDir,
501                                           'Mandatory': None}
502
503                         # Write files
504                         
505                         if not TestRecord[Id]['FileParameters'] == {}:
506                             for key in TestRecord[Id]['FileParameters']:
507                                 filename = key
508                                 filecontent = TestRecord[Id]['FileParameters'][key]
509                                 f = open(PlaybookDir + "/" + filename, "w")
510                                 f.write(filecontent)
511                                 f.close()
512                                 
513                         
514                         # Process playbook
515
516                         if from_files:
517                             
518                             # Get playbooks from files
519
520                             MySqlConFail = False
521                             
522                             version = None
523                             target_PlaybookName = None
524                             
525                             if '@' in PlaybookName:
526                                 version = PlaybookName.split("@")[1]
527                                 version = version.replace('.yml','')
528                                 version = version.replace('.tar.gz','')
529
530                             onlyfiles = [f for f in listdir(ansible_path)
531                                          if isfile(join(ansible_path, f))]
532
533                             version_max = '0.00'
534                             version_target = ''
535                             
536                             for file in onlyfiles:
537                                 if LCM in file:
538                                     temp_version = file.split("@")[1]
539                                     temp_version = temp_version.replace('.yml','')
540                                     temp_version = temp_version.replace('.tar.gz','')
541                                     if version_max < temp_version:
542                                         version_max = temp_version
543                                     
544                                     if not version == None:
545                                         if version in PlaybookName:
546                                             version_target = version
547                                             target_PlaybookName = file
548                                             
549                             if target_PlaybookName == None:
550                                 for file in onlyfiles:
551                                     if LCM in file and version_max in file:
552                                         target_PlaybookName = file
553                                         version_target = version_max
554                                         
555                             if target_PlaybookName:
556                                 AnsiblePlaybookFail = False
557                                 readversion = version_target
558                                 src  = ansible_path + "/" + target_PlaybookName
559                                 if ".tar.gz" in target_PlaybookName:
560                                     dest = PlaybookDir + "/" + LCM + ".tar.gz"
561                                     shutil.copy2(src, dest)
562                                     retcode = subprocess.call(['tar', '-xvzf',
563                                                    dest, "-C", PlaybookDir])
564                                     ArchiveFlag = True
565                                 else:
566                                     dest = PlaybookDir + "/" + LCM + ".yml"
567                                     shutil.copy2(src, dest)
568                             
569                         else:
570                             # Get playbooks from mySQL
571
572                             sqlintf = AnsibleSql.mySql (host, user, passwd, db)
573                             if sqlintf.con:
574                                 MySqlConFail = False
575                                 
576                                 name, readversion, AnsiblePlaybookFail, diag = \
577                                   readPlaybook (sqlintf, PlaybookName.split(".")[0],
578                                                 version)
579                                 
580                                 if not AnsiblePlaybookFail:
581
582                                     f = open(PlaybookDir + "/" + LCM + diag[1], "w")
583                                     f.write(diag[0])
584                                     f.close()
585                                     
586                                     if ".tar.gz" in diag[1]:
587                                         retcode = subprocess.call(['tar', '-xvzf',
588                                          PlaybookDir + "/" + LCM + diag[1], "-C", PlaybookDir])
589                                         f.close()
590                                         ArchiveFlag = True
591                             else:
592                                 MySqlConFailCause = sqlintf.error
593                             sqlintf.Close()
594
595                         if MySqlConFail:
596                             if os.path.exists(PlaybookDir):
597                                 shutil.rmtree (PlaybookDir)
598                             del TestRecord[Id]
599                             return {"StatusCode": 101,
600                                     "StatusMessage": "CANNOT CONNECT TO MYSQL: " \
601                                     + MySqlConFailCause}
602                         elif AnsiblePlaybookFail:
603                             if os.path.exists(PlaybookDir):
604                                 shutil.rmtree (PlaybookDir)
605                             del TestRecord[Id]
606                             return {"StatusCode": 101,
607                                     "StatusMessage": "PLAYBOOK NOT FOUND"}
608                         elif AnsibleInvFail:
609                             if os.path.exists(PlaybookDir):
610                                 shutil.rmtree (PlaybookDir)
611                             del TestRecord[Id]
612                             return {"StatusCode": 101,
613                                     "StatusMessage": "NODE LIST CREDENTIALS NOT FOUND"}
614                         else:
615
616                             # Test EnvParameters
617                             playbook_path = None
618                             if ArchiveFlag:
619                                 for dName, sdName, fList in os.walk(PlaybookDir):
620                                     if LCM+".yml" in fList:
621                                         playbook_path = dName
622                             else:
623                                 playbook_path = PlaybookDir
624
625                             # Store local vars
626                             if not os.path.exists(playbook_path + "/vars"):
627                                 os.mkdir(playbook_path + "/vars")
628                             if not os.path.isfile(playbook_path + "/vars/defaults.yml"):
629                                 os.mknod(playbook_path + "/vars/defaults.yml")
630
631                             for key in TestRecord[Id]['LocalParameters']:
632                                 host_index = []
633                                 for i in range(len(TestRecord[Id]['HostNameList'])):
634                                     if key in TestRecord[Id]['HostNameList'][i]:
635                                         host_index.append(i)
636                                 if len(host_index) == 0:
637                                     for i in range(len(TestRecord[Id]['HostGroupList'])):
638                                         if key in TestRecord[Id]['HostGroupList'][i]:
639                                             host_index.append(i)
640                                 if len(host_index) > 0:
641                                     for i in range(len(host_index)):
642                                         f = open(playbook_path + "/vars/" +
643                                                  TestRecord[Id]['HostNameList'][host_index[i]] +
644                                                  ".yml", "a")
645                                         for param in TestRecord[Id]['LocalParameters'][key]:
646                                             f.write(param + ": " +
647                                              str (TestRecord[Id]['LocalParameters'][key][param]) +
648                                                   "\n")
649                                         f.close()
650
651                             # Get mandatory parameters from playbook
652                             Mandatory = []
653                             with open(playbook_path + "/" + LCM + ".yml") as origin_file:
654                                 for line in origin_file:
655                                     if "Mandatory" in line:
656                                         temp = line.split(":")[1].strip().replace(' ', '')
657                                         if len(temp) > 0:
658                                             Mandatory = temp.split(",")
659
660                             TestRecord[Id] = {'PlaybookName': TestRecord[Id]['PlaybookName'],
661                                               'LCM': TestRecord[Id]['LCM'],
662                                               'Version': readversion,
663                                               'NodeList': TestRecord[Id]['NodeList'],
664                                               'HostGroupList': TestRecord[Id]['HostGroupList'],
665                                               'HostNameList': TestRecord[Id]['HostNameList'],
666                                               'Time': TestRecord[Id]['Time'],
667                                               'Timeout': TestRecord[Id]['Timeout'],
668                                               'Duration': TestRecord[Id]['Duration'],
669                                               'EnvParameters': TestRecord[Id]['EnvParameters'],
670                                               'LocalParameters': TestRecord[Id]['LocalParameters'],
671                                               'FileParameters': TestRecord[Id]['FileParameters'],
672                                               'CallBack': TestRecord[Id]['CallBack'],
673                                               'Result': TestRecord[Id]['Result'],
674                                               'Log': TestRecord[Id]['Log'],
675                                               'Output': TestRecord[Id]['Output'],
676                                               'Path': TestRecord[Id]['Path'],
677                                               'Mandatory': Mandatory}
678
679                             TestKey = False
680
681                             if Mandatory:
682                                 for val in Mandatory:
683                                     if EnvParam:
684                                         if val in EnvParam:
685                                             TestKey = True
686                                         else:
687                                             if LocalParam:
688                                                 for key in TestRecord[Id]['NodeList']:
689                                                     if key in LocalParam:
690                                                         if val in LocalParam[key]:
691                                                             TestKey = True
692                                     else:
693                                         if LocalParam:
694                                             for key in TestRecord[Id]['NodeList']:
695                                                 if key in LocalParam:
696                                                     if val in LocalParam[key]:
697                                                         TestKey = True
698                                                 
699                                 if not TestKey:
700                                     if os.path.exists(PlaybookDir):
701                                         shutil.rmtree (PlaybookDir)
702                                     del TestRecord[Id]
703                                     return {"StatusCode": 101,
704                                             "StatusMessage": "MISSING MANDATORY PARAMETER: " + \
705                                             " ".join(str(x) for x in Mandatory)}
706                             
707     
708                             # Cannot use thread because ansible module uses
709                             # signals which are only supported in main thread.
710                             # So use multiprocess with shared object
711
712                             p = Process(target = RunAnsible_Playbook,
713                                         args = (callback, Id,  PlaybookDir + "/" + AnsibleInv,
714                                                 playbook_path + "/" + LCM + ".yml",
715                                                 NodeList, TestRecord, PlaybookDir,
716                                                 ArchiveFlag))
717                             p.start()
718                             ActiveProcess[Id] = p
719                             return TestRecord[Id]['Result']
720                     else:
721                         return {"StatusCode": 101, "StatusMessage": "TEST ID ALREADY DEFINED"}
722
723                 else:
724                     return {"StatusCode": 500, "StatusMessage": "REQUEST MUST INCLUDE: NODELIST"}
725                 
726             else:
727                 return {"StatusCode": 500, "StatusMessage": "JSON OBJECT MUST INCLUDE: ID, PLAYBOOKNAME"}
728
729         elif 'GET' in cherrypy.request.method:
730             
731             input_data = parse_query_string(cherrypy.request.query_string)
732             
733             print "***> in RestServer.GET"
734             print "   Payload: ", input_data, input_data['Type']
735             
736             if 'Id' in input_data and 'Type' in input_data:
737                 if not ('GetResult' in input_data['Type'] or 'GetOutput' in input_data['Type'] or 'GetLog' in input_data['Type']):
738                     return {"StatusCode": 500, "StatusMessage": "RESULTS TYPE UNDEFINED"}
739                 if input_data['Id'] in TestRecord:
740                     
741                     if 'GetResult' in input_data['Type']:
742                         
743                         print "Result:", TestRecord[input_data['Id']]['Result']
744
745                         if 'StatusMessage' in TestRecord[input_data['Id']]['Result'] and getresults_block:
746
747                             print "*** Request blocked", input_data['Id']
748
749                             while ActiveProcess[input_data['Id']].is_alive():
750                                 time.sleep(5)
751
752                             print "*** Request released ", input_data['Id']
753
754                         print TestRecord[input_data['Id']]['Result']
755                         if TestRecord[input_data['Id']]['Result']['StatusCode'] == 500:
756                             out_obj = TestRecord[input_data['Id']]['Result']['Results']
757                         else:
758                             out_obj = {"StatusCode": 200,
759                                    "StatusMessage": "FINISHED",
760                                    "PlaybookName": TestRecord[input_data['Id']]["PlaybookName"],
761                                    "Version": TestRecord[input_data['Id']]["Version"],
762                                    "Duration": TestRecord[input_data['Id']]["Duration"],
763                                    "Results": TestRecord[input_data['Id']]['Result']['Results']}
764                         if not TestRecord[input_data['Id']]['Output']['Output'] == {}:
765                             for key in out_obj["Results"]:
766                                 if key in TestRecord[input_data['Id']]['Output']['Output']:
767                                     out_obj["Results"][key]["Output"] = TestRecord[input_data['Id']]['Output']['Output'][key]
768
769                         return out_obj
770
771                     elif 'GetOutput' in input_data['Type']:
772
773                         if TestRecord[input_data['Id']]['Output'] == {} and \
774                                getresults_block:
775
776                             print "*** Request blocked", input_data['Id']
777                             
778                             while TestRecord[input_data['Id']]['Output'] == {} \
779                                       or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
780                                 time.sleep(5)
781
782                             print "*** Request released ", input_data['Id']
783                         
784                         print "Output:", TestRecord[input_data['Id']]['Output']
785                         return {"Output": TestRecord[input_data['Id']]['Output']['Output']}
786                     else:
787                         # GetLog
788
789                         if TestRecord[input_data['Id']]['Log'] == '' and \
790                                getresults_block:
791
792                             print "*** Request blocked", input_data['Id']
793                             
794                             while TestRecord[input_data['Id']]['Log'] == '' \
795                                       or 'StatusMessage' in TestRecord[input_data['Id']]['Result']:
796                                 time.sleep(5)
797
798                             print "*** Request released ", input_data['Id']
799                             
800                         print "Log:", TestRecord[input_data['Id']]['Log']
801                         return {"Log": TestRecord[input_data['Id']]['Log']}
802                 else:
803                     return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
804             else:
805                 return {"StatusCode": 500, "StatusMessage": "MALFORMED REQUEST"}
806         elif 'DELETE' in cherrypy.request.method:
807             input_data = parse_query_string(cherrypy.request.query_string)
808             
809             print "***> in RestServer.DELETE"
810             print "   Payload: ", input_data
811             
812             if input_data['Id'] in TestRecord:
813                 if not 'PENDING' in TestRecord[input_data['Id']]['Result']:
814                     print "   Path:", TestRecord[input_data['Id']]['Path']
815                     if os.path.exists(TestRecord[input_data['Id']]['Path']):
816                         shutil.rmtree (TestRecord[input_data['Id']]['Path'])
817                     TestRecord.pop (input_data['Id'])
818                     if input_data['Id'] in ActiveProcess:
819                         ActiveProcess.pop (input_data['Id'])
820
821                     return {"StatusCode": 200, "StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED"}
822                 else:
823                     return {"StatusCode": 200, "StatusMessage": "PENDING"}
824             else:
825                 return {"StatusCode": 500, "StatusMessage": "TEST ID UNDEFINED"}
826
827
828 if __name__ == '__main__':
829
830     # Read configuration
831
832     config_file_path = "RestServer_config"
833
834     if not os.path.exists(config_file_path):
835         print '[INFO] The config file does not exist'
836         sys.exit(0)
837
838     ip = 'na'
839     port = 'na'
840     tls = False
841     auth = False
842     pub = 'na'
843     id = 'na'
844     priv = 'na'
845     psswd = 'na'
846     timeout_seconds = 'na'
847     ansible_path = 'na'
848     ansible_inv = 'na'
849     ansible_temp = 'na'    
850     host = 'na'
851     user = 'na'
852     passwd = 'na'
853     db = 'na'
854     getresults_block = False
855     from_files = False
856     
857     file = open(config_file_path, 'r')
858     for line in file.readlines():
859         if '#' not in line:
860             if 'ip:' in line:
861                 ip = line.split(':')[1].strip()
862             elif 'port:' in line:
863                 port = line.split(':')[1].strip()
864             elif 'tls:' in line:
865                 tls = 'YES' in line.split(':')[1].strip().upper()
866             elif 'auth:' in line:
867                 auth = 'YES' in line.split(':')[1].strip().upper()
868             if tls and 'priv:' in line:
869                 priv = line.split(':')[1].strip()
870             if tls and 'pub:' in line:
871                 pub = line.split(':')[1].strip()
872             if auth and 'id:' in line:
873                 id = line.split(':')[1].strip()
874             if auth and 'psswd:' in line:
875                 psswd = line.split(':')[1].strip()
876             if 'timeout_seconds' in line:
877                 timeout_seconds = int (line.split(':')[1].strip())
878             if 'ansible_path' in line:
879                 ansible_path = line.split(':')[1].strip()
880             if 'ansible_inv' in line:
881                 ansible_inv = line.split(':')[1].strip()
882                 if not os.path.exists(ansible_path + "/" + ansible_inv):
883                     print '[INFO] The ansible_inv file does not exist'
884                     sys.exit(0)
885             if 'ansible_temp' in line:
886                 ansible_temp = line.split(':')[1].strip()
887             if 'host' in line:
888                 host = line.split(':')[1].strip()
889             if 'user' in line:
890                 user = line.split(':')[1].strip()
891             if 'passwd' in line:
892                 passwd = line.split(':')[1].strip()
893             if 'db' in line:
894                 db = line.split(':')[1].strip()
895             if 'getresults_block' in line:
896                 getresults_block = 'YES' in line.split(':')[1].strip().upper()
897             if 'from_files' in line:
898                 from_files = 'YES' in line.split(':')[1].strip().upper()
899     file.close()
900
901     # Initialization
902     
903     global_conf = {
904         'global': {
905             'server.socket_host': ip,
906             'server.socket_port': int(port),
907             'server.protocol_version': 'HTTP/1.1'
908             }
909         }
910
911     if tls:
912         # Use pythons built-in SSL
913         cherrypy.server.ssl_module = 'builtin'
914
915         # Point to certificate files
916
917         if not os.path.exists(pub):
918             print '[INFO] The public certificate does not exist'
919             sys.exit(0)
920
921         if not os.path.exists(priv):
922             print '[INFO] The private key does not exist'
923             sys.exit(0)            
924         
925         cherrypy.server.ssl_certificate = pub
926         cherrypy.server.ssl_private_key = priv
927
928     if auth:
929         userpassdict = {id: psswd}
930         checkpassword = cherrypy.lib.auth_basic.checkpassword_dict(userpassdict)
931
932         app_conf = {'/':
933                     {'tools.auth_basic.on': True,
934                      'tools.auth_basic.realm': 'earth',
935                      'tools.auth_basic.checkpassword': checkpassword,
936                      }
937                     }
938
939         cherrypy.tree.mount(TestManager(), '/', app_conf)
940     else:
941         cherrypy.tree.mount(TestManager(), '/')
942         
943     cherrypy.config.update(global_conf)
944
945     # Start server
946     
947     cherrypy.engine.start()
948     cherrypy.engine.block()