aboutsummaryrefslogtreecommitdiffstats
path: root/opnfv_testapi/resources/test_handlers.py
diff options
context:
space:
mode:
Diffstat (limited to 'opnfv_testapi/resources/test_handlers.py')
-rw-r--r--opnfv_testapi/resources/test_handlers.py307
1 files changed, 307 insertions, 0 deletions
diff --git a/opnfv_testapi/resources/test_handlers.py b/opnfv_testapi/resources/test_handlers.py
new file mode 100644
index 0000000..82cf9ae
--- /dev/null
+++ b/opnfv_testapi/resources/test_handlers.py
@@ -0,0 +1,307 @@
+##############################################################################
+# Copyright (c) 2015 Orange
+# guyrodrigue.koffi@orange.com / koffirodrigue@gmail.com
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+import logging
+import os
+import json
+
+from tornado import web
+from tornado import gen
+from bson import objectid
+
+from opnfv_testapi.common.config import CONF
+from opnfv_testapi.common import message
+from opnfv_testapi.common import raises
+from opnfv_testapi.resources import handlers
+from opnfv_testapi.resources import test_models
+from opnfv_testapi.tornado_swagger import swagger
+from opnfv_testapi.ui.auth import constants as auth_const
+from opnfv_testapi.db import api as dbapi
+
+DOVETAIL_LOG_PATH = '/home/testapi/logs/{}/results/dovetail.log'
+
+
+class GenericTestHandler(handlers.GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericTestHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = "tests"
+ self.table_cls = test_models.Test
+
+
+class TestsCLHandler(GenericTestHandler):
+ @swagger.operation(nickname="queryTests")
+ @web.asynchronous
+ @gen.coroutine
+ def get(self):
+ """
+ @description: Retrieve result(s) for a test project
+ on a specific pod.
+ @notes: Retrieve result(s) for a test project on a specific pod.
+ Available filters for this request are :
+ - id : Test id
+ - period : x last days, incompatible with from/to
+ - from : starting time in 2016-01-01 or 2016-01-01 00:01:23
+ - to : ending time in 2016-01-01 or 2016-01-01 00:01:23
+ - signed : get logined user result
+
+ GET /results/project=functest&case=vPing&version=Arno-R1 \
+ &pod=pod_name&period=15&signed
+ @return 200: all test results consist with query,
+ empty list if no result is found
+ @rtype: L{Tests}
+ """
+ def descend_limit():
+ descend = self.get_query_argument('descend', 'true')
+ return -1 if descend.lower() == 'true' else 1
+
+ def last_limit():
+ return self.get_int('last', self.get_query_argument('last', 0))
+
+ def page_limit():
+ return self.get_int('page', self.get_query_argument('page', 0))
+
+ limitations = {
+ 'sort': {'_id': descend_limit()},
+ 'last': last_limit(),
+ 'page': page_limit(),
+ 'per_page': CONF.api_results_per_page
+ }
+
+ query = yield self.set_query()
+ yield self._list(query=query, **limitations)
+ logging.debug('list end')
+
+ @swagger.operation(nickname="createTest")
+ @web.asynchronous
+ def post(self):
+ """
+ @description: create a test
+ @param body: test to be created
+ @type body: L{TestCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: test is created.
+ @raise 404: pod/project/testcase not exist
+ @raise 400: body/pod_name/project_name/case_name not provided
+ """
+ openid = self.get_secure_cookie(auth_const.OPENID)
+ if openid:
+ self.json_args['owner'] = openid
+
+ self._post()
+
+ @gen.coroutine
+ def _post(self):
+ miss_fields = []
+ carriers = []
+ query = {'owner': self.json_args['owner'], 'id': self.json_args['id']}
+ ret, msg = yield self._check_if_exists(table="tests", query=query)
+ if ret:
+ self.finish_request({'code': '403', 'msg': msg})
+ return
+
+ self._create(miss_fields=miss_fields, carriers=carriers)
+
+
+class TestsGURHandler(GenericTestHandler):
+
+ @swagger.operation(nickname="getTestById")
+ @web.asynchronous
+ @gen.coroutine
+ def get(self, test_id):
+ query = dict()
+ query["_id"] = objectid.ObjectId(test_id)
+
+ data = yield dbapi.db_find_one(self.table, query)
+ if not data:
+ raises.NotFound(message.not_found(self.table, query))
+
+ validation = yield self._check_api_response_validation(data['id'])
+
+ data.update({'validation': validation})
+
+ self.finish_request(self.format_data(data))
+
+ @gen.coroutine
+ def _check_api_response_validation(self, test_id):
+ log_path = DOVETAIL_LOG_PATH.format(test_id)
+ if not os.path.exists(log_path):
+ raises.Forbidden('dovetail.log not found, please check')
+
+ with open(log_path) as f:
+ log_content = f.read()
+
+ warning_keyword = 'Strict API response validation DISABLED'
+ if warning_keyword in log_content:
+ raise gen.Return('API response validation disabled')
+ else:
+ raise gen.Return('API response validation enabled')
+
+ @swagger.operation(nickname="deleteTestById")
+ def delete(self, test_id):
+ query = {'_id': objectid.ObjectId(test_id)}
+ self._delete(query=query)
+
+ @swagger.operation(nickname="updateTestById")
+ @web.asynchronous
+ def put(self, _id):
+ """
+ @description: update a single test by id
+ @param body: fields to be updated
+ @type body: L{TestUpdateRequest}
+ @in body: body
+ @rtype: L{Test}
+ @return 200: update success
+ @raise 404: Test not exist
+ @raise 403: nothing to update
+ """
+ logging.debug('put')
+ data = json.loads(self.request.body)
+ item = data.get('item')
+ value = data.get(item)
+ logging.debug('%s:%s', item, value)
+ try:
+ self.update(_id, item, value)
+ except Exception as e:
+ logging.error('except:%s', e)
+ return
+
+ @gen.coroutine
+ def _convert_to_id(self, email):
+ query = {"email": email}
+ table = "users"
+ if query and table:
+ data = yield dbapi.db_find_one(table, query)
+ if data:
+ raise gen.Return((True, 'Data alreay exists. %s' % (query),
+ data.get("openid")))
+ raise gen.Return((False, 'Data does not exist. %s' % (query), None))
+
+ @gen.coroutine
+ def update(self, _id, item, value):
+ logging.debug("update")
+ if item == "shared":
+ new_list = []
+ for user in value:
+ ret, msg, user_id = yield self._convert_to_id(user)
+ if ret:
+ user = user_id
+ new_list.append(user)
+ query = {"$or": [{"openid": user}, {"email": user}]}
+ table = "users"
+ ret, msg = yield self._check_if_exists(table=table,
+ query=query)
+ logging.debug('ret:%s', ret)
+ if not ret:
+ self.finish_request({'code': '403', 'msg': msg})
+ return
+
+ if len(new_list) != len(set(new_list)):
+ msg = "Already shared with this user"
+ self.finish_request({'code': '403', 'msg': msg})
+ return
+
+ logging.debug("before _update")
+ self.json_args = {}
+ self.json_args[item] = value
+ ret, msg = yield self.check_auth(item, value)
+ if not ret:
+ self.finish_request({'code': '404', 'msg': msg})
+ return
+
+ query = {'_id': objectid.ObjectId(_id)}
+ db_keys = ['_id', ]
+
+ test = yield dbapi.db_find_one("tests", query)
+ if not test:
+ msg = 'Record does not exist'
+ self.finish_request({'code': 404, 'msg': msg})
+ return
+
+ curr_user = self.get_secure_cookie(auth_const.OPENID)
+ if item in {"shared", "label", "sut_label"}:
+ query['owner'] = curr_user
+ db_keys.append('owner')
+
+ if item == 'sut_label':
+ if test['status'] != 'private' and not value:
+ msg = 'SUT version cannot be changed to None after submitting.'
+ self.finish_request({'code': 403, 'msg': msg})
+ return
+
+ if item == "status":
+ if value in {'approved', 'not approved'}:
+ if test['status'] == 'private':
+ msg = 'Not allowed to approve/not approve'
+ self.finish_request({'code': 403, 'msg': msg})
+ return
+
+ user = yield dbapi.db_find_one("users", {'openid': curr_user})
+ if 'administrator' not in user['role']:
+ msg = 'No permission to operate'
+ self.finish_request({'code': 403, 'msg': msg})
+ return
+ elif value == 'review':
+ if test['status'] != 'private':
+ msg = 'Not allowed to submit to review'
+ self.finish_request({'code': 403, 'msg': msg})
+ return
+
+ if not test['sut_label']:
+ msg = 'Please fill out SUT version before submission'
+ self.finish_request({'code': 403, 'msg': msg})
+ return
+
+ query['owner'] = curr_user
+ db_keys.append('owner')
+
+ test_query = {
+ 'id': test['id'],
+ '$or': [
+ {'status': 'review'},
+ {'status': 'approved'},
+ {'status': 'not approved'}
+ ]
+ }
+ record = yield dbapi.db_find_one("tests", test_query)
+ if record:
+ msg = ('{} has already submitted one record with the same '
+ 'Test ID: {}'.format(record['owner'], test['id']))
+ self.finish_request({'code': 403, 'msg': msg})
+ return
+ else:
+ query['owner'] = curr_user
+ db_keys.append('owner')
+
+ logging.debug("before _update 2")
+ self._update(query=query, db_keys=db_keys)
+
+ @gen.coroutine
+ def check_auth(self, item, value):
+ logging.debug('check_auth')
+ user = self.get_secure_cookie(auth_const.OPENID)
+ query = {}
+ if item == "status":
+ if value == "private" or value == "review":
+ logging.debug('check review')
+ query['user_id'] = user
+ data = yield dbapi.db_find_one('applications', query)
+ if not data:
+ logging.debug('not found')
+ raise gen.Return((False, message.no_auth()))
+ if value == "approve" or value == "not approved":
+ logging.debug('check approve')
+ query['role'] = {"$regex": ".*reviewer.*"}
+ query['openid'] = user
+ data = yield dbapi.db_find_one('users', query)
+ if not data:
+ logging.debug('not found')
+ raise gen.Return((False, message.no_auth()))
+ raise gen.Return((True, {}))