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