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