adding vth code as well as aaf and cert config
[it/otf.git] / otf-robot-test-head / app / routes / robot_processor.py
1 """ Copyright (c) 2019 AT&T Intellectual Property.                             #\r
2 #                                                                              #\r
3 #   Licensed under the Apache License, Version 2.0 (the "License");            #\r
4 #   you may not use this file except in compliance with the License.           #\r
5 #   You may obtain a copy of the License at                                    #\r
6 #                                                                              #\r
7 #       http://www.apache.org/licenses/LICENSE-2.0                             #\r
8 #                                                                              #\r
9 #   Unless required by applicable law or agreed to in writing, software        #\r
10 #   distributed under the License is distributed on an "AS IS" BASIS,          #\r
11 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #\r
12 #   See the License for the specific language governing permissions and        #\r
13 #   limitations under the License.                                             #\r
14 #############################################################################"""\r
15 \r
16 \r
17 import shutil\r
18 import uuid\r
19 import zipfile\r
20 \r
21 import gridfs\r
22 from bson import ObjectId\r
23 from flask import request, make_response, jsonify, current_app\r
24 from gridfs import NoFile\r
25 from robot import run\r
26 \r
27 from . import routes\r
28 from .. import database\r
29 from ..utils import *\r
30 \r
31 db_instance = database.DatabaseConfiguration()\r
32 \r
33 \r
34 def verify_directories():\r
35     # retrieve the app object to retrieve the config we created\r
36     app = current_app._get_current_object()\r
37 \r
38     if not os.path.isdir(app.config['g_base_folder']):\r
39         os.makedirs(app.config['g_base_folder'])\r
40     if not os.path.isdir(app.config['g_data_folder']):\r
41         os.makedirs(app.config['g_data_folder'])\r
42     if not os.path.isdir(app.config['g_working_folder']):\r
43         os.makedirs(app.config['g_working_folder'])\r
44 \r
45 \r
46 @routes.route("/v1", methods=['POST'])\r
47 def robot():\r
48     app = current_app._get_current_object()\r
49 \r
50     response_data = {\r
51         "vthResponse": {\r
52             "testDurationMS": "",\r
53             "dateTimeUTC": str(datetime.datetime.now()),\r
54             "abstractMessage": "",\r
55             "resultData": {}\r
56         }\r
57     }\r
58 \r
59     start_time = unix_time_millis(datetime.datetime.now())\r
60 \r
61     try:\r
62         if not request.is_json:\r
63             raise ValueError('Invalid JSON object.')\r
64 \r
65         # get json data from the request\r
66         request_data = request.get_json()\r
67 \r
68         # get values for expected keys\r
69         vth_input = try_get_json_value('vthInput', request_data)\r
70         test_data = try_get_json_value('testData', vth_input)\r
71         robot_file_id = try_get_json_value('robotFileId', test_data)\r
72 \r
73         # set up a GridFS to access the database\r
74         db = db_instance.get_database()\r
75         fs = gridfs.GridFS(db)\r
76 \r
77         # try to find a file using the supplied robot_file_id\r
78         compressed_file = fs.get(ObjectId(robot_file_id))\r
79 \r
80         # create the directories used during robot processing if they don't exist\r
81         verify_directories()\r
82 \r
83         # generate a folder named by a uuid to organize data for each request\r
84         random_uuid = uuid.uuid4().get_hex()\r
85         data_dir = os.path.join(app.config['g_data_folder'], random_uuid)\r
86         os.mkdir(data_dir)\r
87 \r
88         #\r
89         with open(os.path.join(data_dir, compressed_file.name), 'wb') as f:\r
90             f.write(compressed_file.read().__str__())\r
91             f.close()\r
92 \r
93         with zipfile.ZipFile(os.path.join(data_dir, compressed_file.name)) as zip_ref:\r
94             # Create a temporary folder for storing extracted test file(s)\r
95             test_dir = os.path.join(app.config['g_working_folder'], random_uuid)\r
96             os.mkdir(test_dir)\r
97 \r
98             # Create a separate folder for the output files, so they can be compressed and sent back to the TCU\r
99             test_output_dir = os.path.join(test_dir, 'output')\r
100             os.mkdir(test_output_dir)\r
101 \r
102             # Extract the robot tests into the temporary directory\r
103             zip_ref.extractall(test_dir)\r
104 \r
105             # Run the robot tests with the outputdir pointed to the temporary directory\r
106             return_code = run(os.path.join(test_dir), outputdir=os.path.join(test_dir, 'output'))\r
107 \r
108             # this path is hardcoded so the entire system path isn't included in the zip\r
109             path = './files/results/{uuid}/output'.format(uuid=random_uuid)\r
110             zip_file = zipfile.ZipFile(path + '.zip', 'w', zipfile.ZIP_DEFLATED, allowZip64=True)\r
111             zip_dir(path, zip_file)\r
112             zip_file.close()\r
113 \r
114             # save the results to the database\r
115             zf = open(path + '.zip', 'rb')\r
116             result_id = fs.put(zf, filename='output.zip', contentType='application/zip')\r
117             zf.close()\r
118 \r
119             response_data['vthResponse']['resultData']['robotStatusCode'] = return_code\r
120             response_data['vthResponse']['resultData']['robotResultFileId'] = str(result_id)\r
121             response_data['vthResponse']['abstractMessage'] = resolve_robot_status_code(return_code)\r
122 \r
123 \r
124 \r
125         # delete data from the local disk\r
126         shutil.rmtree(path.replace('/output', ''))\r
127         shutil.rmtree(data_dir)\r
128 \r
129         # record the end time of the test\r
130         end_time = unix_time_millis(datetime.datetime.now())\r
131 \r
132         # Calculate the total duration of the test\r
133         total_time = end_time - start_time\r
134 \r
135         # Set the test duration in the result\r
136         response_data['vthResponse']['testDurationMS'] = total_time\r
137 \r
138         return jsonify(response_data)\r
139     except NoFile as e:\r
140         # this exception can only occur if robot_file_id is set to something, so don't worry about reference precedence.\r
141         end_time = unix_time_millis(datetime.datetime.now())\r
142         total_time = end_time - start_time\r
143 \r
144         response_data['vthResponse']['testDurationMS'] = ''\r
145         response_data['vthResponse']['abstractMessage'] = \\r
146             'An exception occurred after running for {totalTime} milliseconds. ' \\r
147             'A file with _id {id} was not found in the collection.'.format(id=robot_file_id, totalTime=total_time)\r
148 \r
149         response = make_response(json.dumps(response_data))\r
150         return response\r
151 \r
152     except Exception as e:\r
153         app.logger.error(e)\r
154         end_time = unix_time_millis(datetime.datetime.now())\r
155         total_time = end_time - start_time\r
156 \r
157         response_data['vthResponse']['testDurationMS'] = ''\r
158         response_data['vthResponse']['abstractMessage'] = \\r
159             'An exception occurred after running for {totalTime} milliseconds. ' \\r
160             'Exception: {exception}.'.format(exception=str(e), totalTime=total_time)\r
161 \r
162         response = make_response(json.dumps(response_data))\r
163 \r
164         return response\r