adding vth code as well as aaf and cert config
[it/otf.git] / otf-robot-test-head / app / routes / robot_processor.py
diff --git a/otf-robot-test-head/app/routes/robot_processor.py b/otf-robot-test-head/app/routes/robot_processor.py
new file mode 100644 (file)
index 0000000..4444858
--- /dev/null
@@ -0,0 +1,164 @@
+""" Copyright (c) 2019 AT&T Intellectual Property.                             #\r
+#                                                                              #\r
+#   Licensed under the Apache License, Version 2.0 (the "License");            #\r
+#   you may not use this file except in compliance with the License.           #\r
+#   You may obtain a copy of the License at                                    #\r
+#                                                                              #\r
+#       http://www.apache.org/licenses/LICENSE-2.0                             #\r
+#                                                                              #\r
+#   Unless required by applicable law or agreed to in writing, software        #\r
+#   distributed under the License is distributed on an "AS IS" BASIS,          #\r
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #\r
+#   See the License for the specific language governing permissions and        #\r
+#   limitations under the License.                                             #\r
+#############################################################################"""\r
+\r
+\r
+import shutil\r
+import uuid\r
+import zipfile\r
+\r
+import gridfs\r
+from bson import ObjectId\r
+from flask import request, make_response, jsonify, current_app\r
+from gridfs import NoFile\r
+from robot import run\r
+\r
+from . import routes\r
+from .. import database\r
+from ..utils import *\r
+\r
+db_instance = database.DatabaseConfiguration()\r
+\r
+\r
+def verify_directories():\r
+    # retrieve the app object to retrieve the config we created\r
+    app = current_app._get_current_object()\r
+\r
+    if not os.path.isdir(app.config['g_base_folder']):\r
+        os.makedirs(app.config['g_base_folder'])\r
+    if not os.path.isdir(app.config['g_data_folder']):\r
+        os.makedirs(app.config['g_data_folder'])\r
+    if not os.path.isdir(app.config['g_working_folder']):\r
+        os.makedirs(app.config['g_working_folder'])\r
+\r
+\r
+@routes.route("/v1", methods=['POST'])\r
+def robot():\r
+    app = current_app._get_current_object()\r
+\r
+    response_data = {\r
+        "vthResponse": {\r
+            "testDurationMS": "",\r
+            "dateTimeUTC": str(datetime.datetime.now()),\r
+            "abstractMessage": "",\r
+            "resultData": {}\r
+        }\r
+    }\r
+\r
+    start_time = unix_time_millis(datetime.datetime.now())\r
+\r
+    try:\r
+        if not request.is_json:\r
+            raise ValueError('Invalid JSON object.')\r
+\r
+        # get json data from the request\r
+        request_data = request.get_json()\r
+\r
+        # get values for expected keys\r
+        vth_input = try_get_json_value('vthInput', request_data)\r
+        test_data = try_get_json_value('testData', vth_input)\r
+        robot_file_id = try_get_json_value('robotFileId', test_data)\r
+\r
+        # set up a GridFS to access the database\r
+        db = db_instance.get_database()\r
+        fs = gridfs.GridFS(db)\r
+\r
+        # try to find a file using the supplied robot_file_id\r
+        compressed_file = fs.get(ObjectId(robot_file_id))\r
+\r
+        # create the directories used during robot processing if they don't exist\r
+        verify_directories()\r
+\r
+        # generate a folder named by a uuid to organize data for each request\r
+        random_uuid = uuid.uuid4().get_hex()\r
+        data_dir = os.path.join(app.config['g_data_folder'], random_uuid)\r
+        os.mkdir(data_dir)\r
+\r
+        #\r
+        with open(os.path.join(data_dir, compressed_file.name), 'wb') as f:\r
+            f.write(compressed_file.read().__str__())\r
+            f.close()\r
+\r
+        with zipfile.ZipFile(os.path.join(data_dir, compressed_file.name)) as zip_ref:\r
+            # Create a temporary folder for storing extracted test file(s)\r
+            test_dir = os.path.join(app.config['g_working_folder'], random_uuid)\r
+            os.mkdir(test_dir)\r
+\r
+            # Create a separate folder for the output files, so they can be compressed and sent back to the TCU\r
+            test_output_dir = os.path.join(test_dir, 'output')\r
+            os.mkdir(test_output_dir)\r
+\r
+            # Extract the robot tests into the temporary directory\r
+            zip_ref.extractall(test_dir)\r
+\r
+            # Run the robot tests with the outputdir pointed to the temporary directory\r
+            return_code = run(os.path.join(test_dir), outputdir=os.path.join(test_dir, 'output'))\r
+\r
+            # this path is hardcoded so the entire system path isn't included in the zip\r
+            path = './files/results/{uuid}/output'.format(uuid=random_uuid)\r
+            zip_file = zipfile.ZipFile(path + '.zip', 'w', zipfile.ZIP_DEFLATED, allowZip64=True)\r
+            zip_dir(path, zip_file)\r
+            zip_file.close()\r
+\r
+            # save the results to the database\r
+            zf = open(path + '.zip', 'rb')\r
+            result_id = fs.put(zf, filename='output.zip', contentType='application/zip')\r
+            zf.close()\r
+\r
+            response_data['vthResponse']['resultData']['robotStatusCode'] = return_code\r
+            response_data['vthResponse']['resultData']['robotResultFileId'] = str(result_id)\r
+            response_data['vthResponse']['abstractMessage'] = resolve_robot_status_code(return_code)\r
+\r
+\r
+\r
+        # delete data from the local disk\r
+        shutil.rmtree(path.replace('/output', ''))\r
+        shutil.rmtree(data_dir)\r
+\r
+        # record the end time of the test\r
+        end_time = unix_time_millis(datetime.datetime.now())\r
+\r
+        # Calculate the total duration of the test\r
+        total_time = end_time - start_time\r
+\r
+        # Set the test duration in the result\r
+        response_data['vthResponse']['testDurationMS'] = total_time\r
+\r
+        return jsonify(response_data)\r
+    except NoFile as e:\r
+        # this exception can only occur if robot_file_id is set to something, so don't worry about reference precedence.\r
+        end_time = unix_time_millis(datetime.datetime.now())\r
+        total_time = end_time - start_time\r
+\r
+        response_data['vthResponse']['testDurationMS'] = ''\r
+        response_data['vthResponse']['abstractMessage'] = \\r
+            'An exception occurred after running for {totalTime} milliseconds. ' \\r
+            'A file with _id {id} was not found in the collection.'.format(id=robot_file_id, totalTime=total_time)\r
+\r
+        response = make_response(json.dumps(response_data))\r
+        return response\r
+\r
+    except Exception as e:\r
+        app.logger.error(e)\r
+        end_time = unix_time_millis(datetime.datetime.now())\r
+        total_time = end_time - start_time\r
+\r
+        response_data['vthResponse']['testDurationMS'] = ''\r
+        response_data['vthResponse']['abstractMessage'] = \\r
+            'An exception occurred after running for {totalTime} milliseconds. ' \\r
+            'Exception: {exception}.'.format(exception=str(e), totalTime=total_time)\r
+\r
+        response = make_response(json.dumps(response_data))\r
+\r
+        return response\r