summaryrefslogtreecommitdiffstats
path: root/utils/test/testapi/opnfv_testapi/resources
diff options
context:
space:
mode:
authorSerenaFeng <feng.xiaowei@zte.com.cn>2016-10-18 17:30:31 +0800
committerSerenaFeng <feng.xiaowei@zte.com.cn>2016-10-18 17:30:31 +0800
commitd30f7afdd2d39302174a788e9e008feb61b25596 (patch)
treeeed10dd17021d0f4ee87fb90089dab6eed5b5071 /utils/test/testapi/opnfv_testapi/resources
parent307a8f07e8c17800ed1214394a3aacf02045a03a (diff)
rename result_collection_api to testapi
Change-Id: Iec4e3db23cd44f30831e17c127eda74e9d9b5d14 Signed-off-by: SerenaFeng <feng.xiaowei@zte.com.cn>
Diffstat (limited to 'utils/test/testapi/opnfv_testapi/resources')
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/__init__.py8
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/handlers.py237
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/models.py71
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/pod_handlers.py87
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/pod_models.py85
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/project_handlers.py92
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/project_models.py94
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/result_handlers.py198
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/result_models.py231
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py115
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/testcase_models.py105
11 files changed, 1323 insertions, 0 deletions
diff --git a/utils/test/testapi/opnfv_testapi/resources/__init__.py b/utils/test/testapi/opnfv_testapi/resources/__init__.py
new file mode 100644
index 000000000..05c0c9392
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# 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
+##############################################################################
diff --git a/utils/test/testapi/opnfv_testapi/resources/handlers.py b/utils/test/testapi/opnfv_testapi/resources/handlers.py
new file mode 100644
index 000000000..5059f5d77
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/handlers.py
@@ -0,0 +1,237 @@
+##############################################################################
+# 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
+# feng.xiaowei@zte.com.cn refactor db.pod to db.pods 5-19-2016
+# feng.xiaowei@zte.com.cn refactor test_project to project 5-19-2016
+# feng.xiaowei@zte.com.cn refactor response body 5-19-2016
+# feng.xiaowei@zte.com.cn refactor pod/project response info 5-19-2016
+# feng.xiaowei@zte.com.cn refactor testcase related handler 5-20-2016
+# feng.xiaowei@zte.com.cn refactor result related handler 5-23-2016
+# feng.xiaowei@zte.com.cn refactor dashboard related handler 5-24-2016
+# feng.xiaowei@zte.com.cn add methods to GenericApiHandler 5-26-2016
+# feng.xiaowei@zte.com.cn remove PodHandler 5-26-2016
+# feng.xiaowei@zte.com.cn remove ProjectHandler 5-26-2016
+# feng.xiaowei@zte.com.cn remove TestcaseHandler 5-27-2016
+# feng.xiaowei@zte.com.cn remove ResultHandler 5-29-2016
+# feng.xiaowei@zte.com.cn remove DashboardHandler 5-30-2016
+##############################################################################
+
+import json
+from datetime import datetime
+
+from tornado import gen
+from tornado.web import RequestHandler, asynchronous, HTTPError
+
+from models import CreateResponse
+from opnfv_testapi.common.constants import DEFAULT_REPRESENTATION, \
+ HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_FORBIDDEN
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class GenericApiHandler(RequestHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericApiHandler, self).__init__(application, request, **kwargs)
+ self.db = self.settings["db"]
+ self.json_args = None
+ self.table = None
+ self.table_cls = None
+ self.db_projects = 'projects'
+ self.db_pods = 'pods'
+ self.db_testcases = 'testcases'
+ self.db_results = 'results'
+
+ def prepare(self):
+ if self.request.method != "GET" and self.request.method != "DELETE":
+ if self.request.headers.get("Content-Type") is not None:
+ if self.request.headers["Content-Type"].startswith(
+ DEFAULT_REPRESENTATION):
+ try:
+ self.json_args = json.loads(self.request.body)
+ except (ValueError, KeyError, TypeError) as error:
+ raise HTTPError(HTTP_BAD_REQUEST,
+ "Bad Json format [{}]".
+ format(error))
+
+ def finish_request(self, json_object=None):
+ if json_object:
+ self.write(json.dumps(json_object))
+ self.set_header("Content-Type", DEFAULT_REPRESENTATION)
+ self.finish()
+
+ def _create_response(self, resource):
+ href = self.request.full_url() + '/' + str(resource)
+ return CreateResponse(href=href).format()
+
+ def format_data(self, data):
+ cls_data = self.table_cls.from_dict(data)
+ return cls_data.format_http()
+
+ @asynchronous
+ @gen.coroutine
+ def _create(self, miss_checks, db_checks, **kwargs):
+ """
+ :param miss_checks: [miss1, miss2]
+ :param db_checks: [(table, exist, query, error)]
+ """
+ if self.json_args is None:
+ raise HTTPError(HTTP_BAD_REQUEST, "no body")
+
+ data = self.table_cls.from_dict(self.json_args)
+ for miss in miss_checks:
+ miss_data = data.__getattribute__(miss)
+ if miss_data is None or miss_data == '':
+ raise HTTPError(HTTP_BAD_REQUEST,
+ '{} missing'.format(miss))
+
+ for k, v in kwargs.iteritems():
+ data.__setattr__(k, v)
+
+ for table, exist, query, error in db_checks:
+ check = yield self._eval_db_find_one(query(data), table)
+ if (exist and not check) or (not exist and check):
+ code, message = error(data)
+ raise HTTPError(code, message)
+
+ if self.table != 'results':
+ data.creation_date = datetime.now()
+ _id = yield self._eval_db(self.table, 'insert', data.format(),
+ check_keys=False)
+ if 'name' in self.json_args:
+ resource = data.name
+ else:
+ resource = _id
+ self.finish_request(self._create_response(resource))
+
+ @asynchronous
+ @gen.coroutine
+ def _list(self, query=None, res_op=None, *args, **kwargs):
+ if query is None:
+ query = {}
+ data = []
+ cursor = self._eval_db(self.table, 'find', query)
+ if 'sort' in kwargs:
+ cursor = cursor.sort(kwargs.get('sort'))
+ if 'last' in kwargs:
+ cursor = cursor.limit(kwargs.get('last'))
+ while (yield cursor.fetch_next):
+ data.append(self.format_data(cursor.next_object()))
+ if res_op is None:
+ res = {self.table: data}
+ else:
+ res = res_op(data, *args)
+ self.finish_request(res)
+
+ @asynchronous
+ @gen.coroutine
+ def _get_one(self, query):
+ data = yield self._eval_db_find_one(query)
+ if data is None:
+ raise HTTPError(HTTP_NOT_FOUND,
+ "[{}] not exist in table [{}]"
+ .format(query, self.table))
+ self.finish_request(self.format_data(data))
+
+ @asynchronous
+ @gen.coroutine
+ def _delete(self, query):
+ data = yield self._eval_db_find_one(query)
+ if data is None:
+ raise HTTPError(HTTP_NOT_FOUND,
+ "[{}] not exit in table [{}]"
+ .format(query, self.table))
+
+ yield self._eval_db(self.table, 'remove', query)
+ self.finish_request()
+
+ @asynchronous
+ @gen.coroutine
+ def _update(self, query, db_keys):
+ if self.json_args is None:
+ raise HTTPError(HTTP_BAD_REQUEST, "No payload")
+
+ # check old data exist
+ from_data = yield self._eval_db_find_one(query)
+ if from_data is None:
+ raise HTTPError(HTTP_NOT_FOUND,
+ "{} could not be found in table [{}]"
+ .format(query, self.table))
+
+ data = self.table_cls.from_dict(from_data)
+ # check new data exist
+ equal, new_query = self._update_query(db_keys, data)
+ if not equal:
+ to_data = yield self._eval_db_find_one(new_query)
+ if to_data is not None:
+ raise HTTPError(HTTP_FORBIDDEN,
+ "{} already exists in table [{}]"
+ .format(new_query, self.table))
+
+ # we merge the whole document """
+ edit_request = data.format()
+ edit_request.update(self._update_requests(data))
+
+ """ Updating the DB """
+ yield self._eval_db(self.table, 'update', query, edit_request,
+ check_keys=False)
+ edit_request['_id'] = str(data._id)
+ self.finish_request(edit_request)
+
+ def _update_requests(self, data):
+ request = dict()
+ for k, v in self.json_args.iteritems():
+ request = self._update_request(request, k, v,
+ data.__getattribute__(k))
+ if not request:
+ raise HTTPError(HTTP_FORBIDDEN, "Nothing to update")
+ return request
+
+ @staticmethod
+ def _update_request(edit_request, key, new_value, old_value):
+ """
+ This function serves to prepare the elements in the update request.
+ We try to avoid replace the exact values in the db
+ edit_request should be a dict in which we add an entry (key) after
+ comparing values
+ """
+ if not (new_value is None):
+ if new_value != old_value:
+ edit_request[key] = new_value
+
+ return edit_request
+
+ def _update_query(self, keys, data):
+ query = dict()
+ equal = True
+ for key in keys:
+ new = self.json_args.get(key)
+ old = data.__getattribute__(key)
+ if new is None:
+ new = old
+ elif new != old:
+ equal = False
+ query[key] = new
+ return equal, query
+
+ def _eval_db(self, table, method, *args, **kwargs):
+ exec_collection = self.db.__getattr__(table)
+ return exec_collection.__getattribute__(method)(*args, **kwargs)
+
+ def _eval_db_find_one(self, query, table=None):
+ if table is None:
+ table = self.table
+ return self._eval_db(table, 'find_one', query)
+
+
+class VersionHandler(GenericApiHandler):
+ @swagger.operation(nickname='list')
+ def get(self):
+ """
+ @description: list all supported versions
+ @rtype: L{Versions}
+ """
+ versions = [{'version': 'v1.0', 'description': 'basics'}]
+ self.finish_request({'versions': versions})
diff --git a/utils/test/testapi/opnfv_testapi/resources/models.py b/utils/test/testapi/opnfv_testapi/resources/models.py
new file mode 100644
index 000000000..e79308b53
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/models.py
@@ -0,0 +1,71 @@
+##############################################################################
+# 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
+# feng.xiaowei@zte.com.cn mv Pod to pod_models.py 5-18-2016
+# feng.xiaowei@zte.com.cn add MetaCreateResponse/MetaGetResponse 5-18-2016
+# feng.xiaowei@zte.com.cn mv TestProject to project_models.py 5-19-2016
+# feng.xiaowei@zte.com.cn delete meta class 5-19-2016
+# feng.xiaowei@zte.com.cn add CreateResponse 5-19-2016
+# feng.xiaowei@zte.com.cn mv TestCase to testcase_models.py 5-20-2016
+# feng.xiaowei@zte.com.cn mv TestResut to result_models.py 5-23-2016
+##############################################################################
+from opnfv_testapi.tornado_swagger import swagger
+
+
+@swagger.model()
+class CreateResponse(object):
+ def __init__(self, href=''):
+ self.href = href
+
+ @staticmethod
+ def from_dict(res_dict):
+ if res_dict is None:
+ return None
+
+ res = CreateResponse()
+ res.href = res_dict.get('href')
+ return res
+
+ def format(self):
+ return {'href': self.href}
+
+
+@swagger.model()
+class Versions(object):
+ """
+ @property versions:
+ @ptype versions: C{list} of L{Version}
+ """
+ def __init__(self):
+ self.versions = list()
+
+ @staticmethod
+ def from_dict(res_dict):
+ if res_dict is None:
+ return None
+
+ res = Versions()
+ for version in res_dict.get('versions'):
+ res.versions.append(Version.from_dict(version))
+ return res
+
+
+@swagger.model()
+class Version(object):
+ def __init__(self, version=None, description=None):
+ self.version = version
+ self.description = description
+
+ @staticmethod
+ def from_dict(a_dict):
+ if a_dict is None:
+ return None
+
+ ver = Version()
+ ver.version = a_dict.get('version')
+ ver.description = str(a_dict.get('description'))
+ return ver
diff --git a/utils/test/testapi/opnfv_testapi/resources/pod_handlers.py b/utils/test/testapi/opnfv_testapi/resources/pod_handlers.py
new file mode 100644
index 000000000..8f44439e7
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/pod_handlers.py
@@ -0,0 +1,87 @@
+##############################################################################
+# 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
+##############################################################################
+from opnfv_testapi.tornado_swagger import swagger
+from handlers import GenericApiHandler
+from pod_models import Pod
+from opnfv_testapi.common.constants import HTTP_FORBIDDEN
+
+
+class GenericPodHandler(GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericPodHandler, self).__init__(application, request, **kwargs)
+ self.table = 'pods'
+ self.table_cls = Pod
+
+
+class PodCLHandler(GenericPodHandler):
+ @swagger.operation(nickname='list-all')
+ def get(self):
+ """
+ @description: list all pods
+ @return 200: list all pods, empty list is no pod exist
+ @rtype: L{Pods}
+ """
+ self._list()
+
+ @swagger.operation(nickname='create')
+ def post(self):
+ """
+ @description: create a pod
+ @param body: pod to be created
+ @type body: L{PodCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: pod is created.
+ @raise 403: pod already exists
+ @raise 400: body or name not provided
+ """
+ def query(data):
+ return {'name': data.name}
+
+ def error(data):
+ message = '{} already exists as a pod'.format(data.name)
+ return HTTP_FORBIDDEN, message
+
+ miss_checks = ['name']
+ db_checks = [(self.table, False, query, error)]
+ self._create(miss_checks, db_checks)
+
+
+class PodGURHandler(GenericPodHandler):
+ @swagger.operation(nickname='get-one')
+ def get(self, pod_name):
+ """
+ @description: get a single pod by pod_name
+ @rtype: L{Pod}
+ @return 200: pod exist
+ @raise 404: pod not exist
+ """
+ query = dict()
+ query['name'] = pod_name
+ self._get_one(query)
+
+ def delete(self, pod_name):
+ """ Remove a POD
+
+ # check for an existing pod to be deleted
+ mongo_dict = yield self.db.pods.find_one(
+ {'name': pod_name})
+ pod = TestProject.pod(mongo_dict)
+ if pod is None:
+ raise HTTPError(HTTP_NOT_FOUND,
+ "{} could not be found as a pod to be deleted"
+ .format(pod_name))
+
+ # just delete it, or maybe save it elsewhere in a future
+ res = yield self.db.projects.remove(
+ {'name': pod_name})
+
+ self.finish_request(answer)
+ """
+ pass
diff --git a/utils/test/testapi/opnfv_testapi/resources/pod_models.py b/utils/test/testapi/opnfv_testapi/resources/pod_models.py
new file mode 100644
index 000000000..7231806f6
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/pod_models.py
@@ -0,0 +1,85 @@
+##############################################################################
+# 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
+##############################################################################
+from opnfv_testapi.tornado_swagger import swagger
+
+# name: name of the POD e.g. zte-1
+# mode: metal or virtual
+# details: any detail
+# role: ci-pod or community-pod or single-node
+
+
+@swagger.model()
+class PodCreateRequest(object):
+ def __init__(self, name, mode='', details='', role=""):
+ self.name = name
+ self.mode = mode
+ self.details = details
+ self.role = role
+
+ def format(self):
+ return {
+ "name": self.name,
+ "mode": self.mode,
+ "details": self.details,
+ "role": self.role,
+ }
+
+
+@swagger.model()
+class Pod(PodCreateRequest):
+ def __init__(self,
+ name='', mode='', details='',
+ role="", _id='', create_date=''):
+ super(Pod, self).__init__(name, mode, details, role)
+ self._id = _id
+ self.creation_date = create_date
+
+ @staticmethod
+ def from_dict(pod_dict):
+ if pod_dict is None:
+ return None
+
+ p = Pod()
+ p._id = pod_dict.get('_id')
+ p.creation_date = str(pod_dict.get('creation_date'))
+ p.name = pod_dict.get('name')
+ p.mode = pod_dict.get('mode')
+ p.details = pod_dict.get('details')
+ p.role = pod_dict.get('role')
+ return p
+
+ def format(self):
+ f = super(Pod, self).format()
+ f['creation_date'] = str(self.creation_date)
+ return f
+
+ def format_http(self):
+ f = self.format()
+ f['_id'] = str(self._id)
+ return f
+
+
+@swagger.model()
+class Pods(object):
+ """
+ @property pods:
+ @ptype pods: C{list} of L{Pod}
+ """
+ def __init__(self):
+ self.pods = list()
+
+ @staticmethod
+ def from_dict(res_dict):
+ if res_dict is None:
+ return None
+
+ res = Pods()
+ for pod in res_dict.get('pods'):
+ res.pods.append(Pod.from_dict(pod))
+ return res
diff --git a/utils/test/testapi/opnfv_testapi/resources/project_handlers.py b/utils/test/testapi/opnfv_testapi/resources/project_handlers.py
new file mode 100644
index 000000000..1e9a97230
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/project_handlers.py
@@ -0,0 +1,92 @@
+##############################################################################
+# 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
+##############################################################################
+from opnfv_testapi.tornado_swagger import swagger
+from handlers import GenericApiHandler
+from opnfv_testapi.common.constants import HTTP_FORBIDDEN
+from project_models import Project
+
+
+class GenericProjectHandler(GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericProjectHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = 'projects'
+ self.table_cls = Project
+
+
+class ProjectCLHandler(GenericProjectHandler):
+ @swagger.operation(nickname="list-all")
+ def get(self):
+ """
+ @description: list all projects
+ @return 200: return all projects, empty list is no project exist
+ @rtype: L{Projects}
+ """
+ self._list()
+
+ @swagger.operation(nickname="create")
+ def post(self):
+ """
+ @description: create a project
+ @param body: project to be created
+ @type body: L{ProjectCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: project is created.
+ @raise 403: project already exists
+ @raise 400: body or name not provided
+ """
+ def query(data):
+ return {'name': data.name}
+
+ def error(data):
+ message = '{} already exists as a project'.format(data.name)
+ return HTTP_FORBIDDEN, message
+
+ miss_checks = ['name']
+ db_checks = [(self.table, False, query, error)]
+ self._create(miss_checks, db_checks)
+
+
+class ProjectGURHandler(GenericProjectHandler):
+ @swagger.operation(nickname='get-one')
+ def get(self, project_name):
+ """
+ @description: get a single project by project_name
+ @rtype: L{Project}
+ @return 200: project exist
+ @raise 404: project not exist
+ """
+ self._get_one({'name': project_name})
+
+ @swagger.operation(nickname="update")
+ def put(self, project_name):
+ """
+ @description: update a single project by project_name
+ @param body: project to be updated
+ @type body: L{ProjectUpdateRequest}
+ @in body: body
+ @rtype: L{Project}
+ @return 200: update success
+ @raise 404: project not exist
+ @raise 403: new project name already exist or nothing to update
+ """
+ query = {'name': project_name}
+ db_keys = ['name']
+ self._update(query, db_keys)
+
+ @swagger.operation(nickname='delete')
+ def delete(self, project_name):
+ """
+ @description: delete a project by project_name
+ @return 200: delete success
+ @raise 404: project not exist
+ """
+ self._delete({'name': project_name})
diff --git a/utils/test/testapi/opnfv_testapi/resources/project_models.py b/utils/test/testapi/opnfv_testapi/resources/project_models.py
new file mode 100644
index 000000000..f70630cda
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/project_models.py
@@ -0,0 +1,94 @@
+##############################################################################
+# 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
+##############################################################################
+from opnfv_testapi.tornado_swagger import swagger
+
+
+@swagger.model()
+class ProjectCreateRequest(object):
+ def __init__(self, name, description=''):
+ self.name = name
+ self.description = description
+
+ def format(self):
+ return {
+ "name": self.name,
+ "description": self.description,
+ }
+
+
+@swagger.model()
+class ProjectUpdateRequest(object):
+ def __init__(self, name='', description=''):
+ self.name = name
+ self.description = description
+
+ def format(self):
+ return {
+ "name": self.name,
+ "description": self.description,
+ }
+
+
+@swagger.model()
+class Project(object):
+ def __init__(self,
+ name=None, _id=None, description=None, create_date=None):
+ self._id = _id
+ self.name = name
+ self.description = description
+ self.creation_date = create_date
+
+ @staticmethod
+ def from_dict(res_dict):
+
+ if res_dict is None:
+ return None
+
+ t = Project()
+ t._id = res_dict.get('_id')
+ t.creation_date = res_dict.get('creation_date')
+ t.name = res_dict.get('name')
+ t.description = res_dict.get('description')
+
+ return t
+
+ def format(self):
+ return {
+ "name": self.name,
+ "description": self.description,
+ "creation_date": str(self.creation_date)
+ }
+
+ def format_http(self):
+ return {
+ "_id": str(self._id),
+ "name": self.name,
+ "description": self.description,
+ "creation_date": str(self.creation_date),
+ }
+
+
+@swagger.model()
+class Projects(object):
+ """
+ @property projects:
+ @ptype projects: C{list} of L{Project}
+ """
+ def __init__(self):
+ self.projects = list()
+
+ @staticmethod
+ def from_dict(res_dict):
+ if res_dict is None:
+ return None
+
+ res = Projects()
+ for project in res_dict.get('projects'):
+ res.projects.append(Project.from_dict(project))
+ return res
diff --git a/utils/test/testapi/opnfv_testapi/resources/result_handlers.py b/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
new file mode 100644
index 000000000..400b84ac1
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
@@ -0,0 +1,198 @@
+##############################################################################
+# 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
+##############################################################################
+from datetime import datetime, timedelta
+
+from bson.objectid import ObjectId
+from tornado.web import HTTPError
+
+from opnfv_testapi.common.constants import HTTP_BAD_REQUEST, HTTP_NOT_FOUND
+from opnfv_testapi.resources.handlers import GenericApiHandler
+from opnfv_testapi.resources.result_models import TestResult
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class GenericResultHandler(GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericResultHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = self.db_results
+ self.table_cls = TestResult
+
+ def get_int(self, key, value):
+ try:
+ value = int(value)
+ except:
+ raise HTTPError(HTTP_BAD_REQUEST, '{} must be int'.format(key))
+ return value
+
+ def set_query(self):
+ query = dict()
+ for k in self.request.query_arguments.keys():
+ v = self.get_query_argument(k)
+ if k == 'project' or k == 'pod' or k == 'case':
+ query[k + '_name'] = v
+ elif k == 'period':
+ v = self.get_int(k, v)
+ if v > 0:
+ period = datetime.now() - timedelta(days=v)
+ obj = {"$gte": str(period)}
+ query['start_date'] = obj
+ elif k == 'trust_indicator':
+ query[k + '.current'] = float(v)
+ elif k != 'last':
+ query[k] = v
+ return query
+
+
+class ResultsCLHandler(GenericResultHandler):
+ @swagger.operation(nickname="list-all")
+ 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 :
+ - project : project name
+ - case : case name
+ - pod : pod name
+ - version : platform version (Arno-R1, ...)
+ - installer (fuel, ...)
+ - build_tag : Jenkins build tag name
+ - period : x (x last days)
+ - scenario : the test scenario (previously version)
+ - criteria : the global criteria status passed or failed
+ - trust_indicator : evaluate the stability of the test case
+ to avoid running systematically long and stable test case
+
+ GET /results/project=functest&case=vPing&version=Arno-R1 \
+ &pod=pod_name&period=15
+ @return 200: all test results consist with query,
+ empty list if no result is found
+ @rtype: L{TestResults}
+ @param pod: pod name
+ @type pod: L{string}
+ @in pod: query
+ @required pod: False
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: False
+ @param case: case name
+ @type case: L{string}
+ @in case: query
+ @required case: False
+ @param version: i.e. Colorado
+ @type version: L{string}
+ @in version: query
+ @required version: False
+ @param installer: fuel/apex/joid/compass
+ @type installer: L{string}
+ @in installer: query
+ @required installer: False
+ @param build_tag: i.e. v3.0
+ @type build_tag: L{string}
+ @in build_tag: query
+ @required build_tag: False
+ @param scenario: i.e. odl
+ @type scenario: L{string}
+ @in scenario: query
+ @required scenario: False
+ @param criteria: i.e. passed
+ @type criteria: L{string}
+ @in criteria: query
+ @required criteria: False
+ @param period: last days
+ @type period: L{string}
+ @in period: query
+ @required period: False
+ @param last: last records stored until now
+ @type last: L{string}
+ @in last: query
+ @required last: False
+ @param trust_indicator: must be float
+ @type trust_indicator: L{float}
+ @in trust_indicator: query
+ @required trust_indicator: False
+ """
+ last = self.get_query_argument('last', 0)
+ if last is not None:
+ last = self.get_int('last', last)
+
+ self._list(self.set_query(), sort=[('start_date', -1)], last=last)
+
+ @swagger.operation(nickname="create")
+ def post(self):
+ """
+ @description: create a test result
+ @param body: result to be created
+ @type body: L{ResultCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: result is created.
+ @raise 404: pod/project/testcase not exist
+ @raise 400: body/pod_name/project_name/case_name not provided
+ """
+ def pod_query(data):
+ return {'name': data.pod_name}
+
+ def pod_error(data):
+ message = 'Could not find pod [{}]'.format(data.pod_name)
+ return HTTP_NOT_FOUND, message
+
+ def project_query(data):
+ return {'name': data.project_name}
+
+ def project_error(data):
+ message = 'Could not find project [{}]'.format(data.project_name)
+ return HTTP_NOT_FOUND, message
+
+ def testcase_query(data):
+ return {'project_name': data.project_name, 'name': data.case_name}
+
+ def testcase_error(data):
+ message = 'Could not find testcase [{}] in project [{}]'\
+ .format(data.case_name, data.project_name)
+ return HTTP_NOT_FOUND, message
+
+ miss_checks = ['pod_name', 'project_name', 'case_name']
+ db_checks = [('pods', True, pod_query, pod_error),
+ ('projects', True, project_query, project_error),
+ ('testcases', True, testcase_query, testcase_error)]
+ self._create(miss_checks, db_checks)
+
+
+class ResultsGURHandler(GenericResultHandler):
+ @swagger.operation(nickname='get-one')
+ def get(self, result_id):
+ """
+ @description: get a single result by result_id
+ @rtype: L{TestResult}
+ @return 200: test result exist
+ @raise 404: test result not exist
+ """
+ query = dict()
+ query["_id"] = ObjectId(result_id)
+ self._get_one(query)
+
+ @swagger.operation(nickname="update")
+ def put(self, result_id):
+ """
+ @description: update a single result by _id
+ @param body: fields to be updated
+ @type body: L{ResultUpdateRequest}
+ @in body: body
+ @rtype: L{Result}
+ @return 200: update success
+ @raise 404: result not exist
+ @raise 403: nothing to update
+ """
+ query = {'_id': ObjectId(result_id)}
+ db_keys = []
+ self._update(query, db_keys)
diff --git a/utils/test/testapi/opnfv_testapi/resources/result_models.py b/utils/test/testapi/opnfv_testapi/resources/result_models.py
new file mode 100644
index 000000000..f73f5c612
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/result_models.py
@@ -0,0 +1,231 @@
+##############################################################################
+# 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
+##############################################################################
+from opnfv_testapi.tornado_swagger import swagger
+
+
+@swagger.model()
+class TIHistory(object):
+ """
+ @ptype step: L{float}
+ """
+ def __init__(self, date=None, step=0):
+ self.date = date
+ self.step = step
+
+ def format(self):
+ return {
+ "date": self.date,
+ "step": self.step
+ }
+
+ @staticmethod
+ def from_dict(a_dict):
+ if a_dict is None:
+ return None
+
+ return TIHistory(a_dict.get('date'), a_dict.get('step'))
+
+
+@swagger.model()
+class TI(object):
+ """
+ @property histories: trust_indicator update histories
+ @ptype histories: C{list} of L{TIHistory}
+ @ptype current: L{float}
+ """
+ def __init__(self, current=0):
+ self.current = current
+ self.histories = list()
+
+ def format(self):
+ hs = []
+ for h in self.histories:
+ hs.append(h.format())
+
+ return {
+ "current": self.current,
+ "histories": hs
+ }
+
+ @staticmethod
+ def from_dict(a_dict):
+ t = TI()
+ if a_dict:
+ t.current = a_dict.get('current')
+ if 'histories' in a_dict.keys():
+ for history in a_dict.get('histories', None):
+ t.histories.append(TIHistory.from_dict(history))
+ else:
+ t.histories = []
+ return t
+
+
+@swagger.model()
+class ResultCreateRequest(object):
+ """
+ @property trust_indicator:
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self,
+ pod_name=None,
+ project_name=None,
+ case_name=None,
+ installer=None,
+ version=None,
+ start_date=None,
+ stop_date=None,
+ details=None,
+ build_tag=None,
+ scenario=None,
+ criteria=None,
+ trust_indicator=None):
+ self.pod_name = pod_name
+ self.project_name = project_name
+ self.case_name = case_name
+ self.installer = installer
+ self.version = version
+ self.start_date = start_date
+ self.stop_date = stop_date
+ self.details = details
+ self.build_tag = build_tag
+ self.scenario = scenario
+ self.criteria = criteria
+ self.trust_indicator = trust_indicator if trust_indicator else TI(0)
+
+ def format(self):
+ return {
+ "pod_name": self.pod_name,
+ "project_name": self.project_name,
+ "case_name": self.case_name,
+ "installer": self.installer,
+ "version": self.version,
+ "start_date": self.start_date,
+ "stop_date": self.stop_date,
+ "details": self.details,
+ "build_tag": self.build_tag,
+ "scenario": self.scenario,
+ "criteria": self.criteria,
+ "trust_indicator": self.trust_indicator.format()
+ }
+
+
+@swagger.model()
+class ResultUpdateRequest(object):
+ """
+ @property trust_indicator:
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self, trust_indicator=None):
+ self.trust_indicator = trust_indicator
+
+ def format(self):
+ return {
+ "trust_indicator": self.trust_indicator.format(),
+ }
+
+
+@swagger.model()
+class TestResult(object):
+ """
+ @property trust_indicator: used for long duration test case
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self, _id=None, case_name=None, project_name=None,
+ pod_name=None, installer=None, version=None,
+ start_date=None, stop_date=None, details=None,
+ build_tag=None, scenario=None, criteria=None,
+ trust_indicator=None):
+ self._id = _id
+ self.case_name = case_name
+ self.project_name = project_name
+ self.pod_name = pod_name
+ self.installer = installer
+ self.version = version
+ self.start_date = start_date
+ self.stop_date = stop_date
+ self.details = details
+ self.build_tag = build_tag
+ self.scenario = scenario
+ self.criteria = criteria
+ self.trust_indicator = trust_indicator
+
+ @staticmethod
+ def from_dict(a_dict):
+
+ if a_dict is None:
+ return None
+
+ t = TestResult()
+ t._id = a_dict.get('_id')
+ t.case_name = a_dict.get('case_name')
+ t.pod_name = a_dict.get('pod_name')
+ t.project_name = a_dict.get('project_name')
+ t.start_date = str(a_dict.get('start_date'))
+ t.stop_date = str(a_dict.get('stop_date'))
+ t.details = a_dict.get('details')
+ t.version = a_dict.get('version')
+ t.installer = a_dict.get('installer')
+ t.build_tag = a_dict.get('build_tag')
+ t.scenario = a_dict.get('scenario')
+ t.criteria = a_dict.get('criteria')
+ t.trust_indicator = TI.from_dict(a_dict.get('trust_indicator'))
+ return t
+
+ def format(self):
+ return {
+ "case_name": self.case_name,
+ "project_name": self.project_name,
+ "pod_name": self.pod_name,
+ "start_date": str(self.start_date),
+ "stop_date": str(self.stop_date),
+ "version": self.version,
+ "installer": self.installer,
+ "details": self.details,
+ "build_tag": self.build_tag,
+ "scenario": self.scenario,
+ "criteria": self.criteria,
+ "trust_indicator": self.trust_indicator.format()
+ }
+
+ def format_http(self):
+ return {
+ "_id": str(self._id),
+ "case_name": self.case_name,
+ "project_name": self.project_name,
+ "pod_name": self.pod_name,
+ "start_date": str(self.start_date),
+ "stop_date": str(self.stop_date),
+ "version": self.version,
+ "installer": self.installer,
+ "details": self.details,
+ "build_tag": self.build_tag,
+ "scenario": self.scenario,
+ "criteria": self.criteria,
+ "trust_indicator": self.trust_indicator.format()
+ }
+
+
+@swagger.model()
+class TestResults(object):
+ """
+ @property results:
+ @ptype results: C{list} of L{TestResult}
+ """
+ def __init__(self):
+ self.results = list()
+
+ @staticmethod
+ def from_dict(a_dict):
+ if a_dict is None:
+ return None
+
+ res = TestResults()
+ for result in a_dict.get('results'):
+ res.results.append(TestResult.from_dict(result))
+ return res
diff --git a/utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py b/utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py
new file mode 100644
index 000000000..253aa6649
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py
@@ -0,0 +1,115 @@
+##############################################################################
+# 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
+##############################################################################
+from opnfv_testapi.common.constants import HTTP_FORBIDDEN
+from opnfv_testapi.resources.handlers import GenericApiHandler
+from opnfv_testapi.resources.testcase_models import Testcase
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class GenericTestcaseHandler(GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericTestcaseHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = self.db_testcases
+ self.table_cls = Testcase
+
+
+class TestcaseCLHandler(GenericTestcaseHandler):
+ @swagger.operation(nickname="list-all")
+ def get(self, project_name):
+ """
+ @description: list all testcases of a project by project_name
+ @return 200: return all testcases of this project,
+ empty list is no testcase exist in this project
+ @rtype: L{TestCases}
+ """
+ query = dict()
+ query['project_name'] = project_name
+ self._list(query)
+
+ @swagger.operation(nickname="create")
+ def post(self, project_name):
+ """
+ @description: create a testcase of a project by project_name
+ @param body: testcase to be created
+ @type body: L{TestcaseCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: testcase is created in this project.
+ @raise 403: project not exist
+ or testcase already exists in this project
+ @raise 400: body or name not provided
+ """
+ def p_query(data):
+ return {'name': data.project_name}
+
+ def tc_query(data):
+ return {
+ 'project_name': data.project_name,
+ 'name': data.name
+ }
+
+ def p_error(data):
+ message = 'Could not find project [{}]'.format(data.project_name)
+ return HTTP_FORBIDDEN, message
+
+ def tc_error(data):
+ message = '{} already exists as a testcase in project {}'\
+ .format(data.name, data.project_name)
+ return HTTP_FORBIDDEN, message
+
+ miss_checks = ['name']
+ db_checks = [(self.db_projects, True, p_query, p_error),
+ (self.db_testcases, False, tc_query, tc_error)]
+ self._create(miss_checks, db_checks, project_name=project_name)
+
+
+class TestcaseGURHandler(GenericTestcaseHandler):
+ @swagger.operation(nickname='get-one')
+ def get(self, project_name, case_name):
+ """
+ @description: get a single testcase
+ by case_name and project_name
+ @rtype: L{Testcase}
+ @return 200: testcase exist
+ @raise 404: testcase not exist
+ """
+ query = dict()
+ query['project_name'] = project_name
+ query["name"] = case_name
+ self._get_one(query)
+
+ @swagger.operation(nickname="update")
+ def put(self, project_name, case_name):
+ """
+ @description: update a single testcase
+ by project_name and case_name
+ @param body: testcase to be updated
+ @type body: L{TestcaseUpdateRequest}
+ @in body: body
+ @rtype: L{Project}
+ @return 200: update success
+ @raise 404: testcase or project not exist
+ @raise 403: new testcase name already exist in project
+ or nothing to update
+ """
+ query = {'project_name': project_name, 'name': case_name}
+ db_keys = ['name', 'project_name']
+ self._update(query, db_keys)
+
+ @swagger.operation(nickname='delete')
+ def delete(self, project_name, case_name):
+ """
+ @description: delete a testcase by project_name and case_name
+ @return 200: delete success
+ @raise 404: testcase not exist
+ """
+ query = {'project_name': project_name, 'name': case_name}
+ self._delete(query)
diff --git a/utils/test/testapi/opnfv_testapi/resources/testcase_models.py b/utils/test/testapi/opnfv_testapi/resources/testcase_models.py
new file mode 100644
index 000000000..c9dce6088
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/resources/testcase_models.py
@@ -0,0 +1,105 @@
+##############################################################################
+# 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
+##############################################################################
+from opnfv_testapi.tornado_swagger import swagger
+
+
+@swagger.model()
+class TestcaseCreateRequest(object):
+ def __init__(self, name, url=None, description=None):
+ self.name = name
+ self.url = url
+ self.description = description
+
+ def format(self):
+ return {
+ "name": self.name,
+ "description": self.description,
+ "url": self.url,
+ }
+
+
+@swagger.model()
+class TestcaseUpdateRequest(object):
+ def __init__(self, name=None, description=None, project_name=None):
+ self.name = name
+ self.description = description
+ self.project_name = project_name
+
+ def format(self):
+ return {
+ "name": self.name,
+ "description": self.description,
+ "project_name": self.project_name,
+ }
+
+
+@swagger.model()
+class Testcase(object):
+ def __init__(self):
+ self._id = None
+ self.name = None
+ self.project_name = None
+ self.description = None
+ self.url = None
+ self.creation_date = None
+
+ @staticmethod
+ def from_dict(a_dict):
+
+ if a_dict is None:
+ return None
+
+ t = Testcase()
+ t._id = a_dict.get('_id')
+ t.project_name = a_dict.get('project_name')
+ t.creation_date = a_dict.get('creation_date')
+ t.name = a_dict.get('name')
+ t.description = a_dict.get('description')
+ t.url = a_dict.get('url')
+
+ return t
+
+ def format(self):
+ return {
+ "name": self.name,
+ "description": self.description,
+ "project_name": self.project_name,
+ "creation_date": str(self.creation_date),
+ "url": self.url
+ }
+
+ def format_http(self):
+ return {
+ "_id": str(self._id),
+ "name": self.name,
+ "project_name": self.project_name,
+ "description": self.description,
+ "creation_date": str(self.creation_date),
+ "url": self.url,
+ }
+
+
+@swagger.model()
+class Testcases(object):
+ """
+ @property testcases:
+ @ptype testcases: C{list} of L{Testcase}
+ """
+ def __init__(self):
+ self.testcases = list()
+
+ @staticmethod
+ def from_dict(res_dict):
+ if res_dict is None:
+ return None
+
+ res = Testcases()
+ for testcase in res_dict.get('testcases'):
+ res.testcases.append(Testcase.from_dict(testcase))
+ return res