summaryrefslogtreecommitdiffstats
path: root/cvp/opnfv_testapi/resources
diff options
context:
space:
mode:
Diffstat (limited to 'cvp/opnfv_testapi/resources')
-rw-r--r--cvp/opnfv_testapi/resources/__init__.py8
-rw-r--r--cvp/opnfv_testapi/resources/application_handlers.py147
-rw-r--r--cvp/opnfv_testapi/resources/application_models.py39
-rw-r--r--cvp/opnfv_testapi/resources/handlers.py321
-rw-r--r--cvp/opnfv_testapi/resources/models.py115
-rw-r--r--cvp/opnfv_testapi/resources/pod_handlers.py78
-rw-r--r--cvp/opnfv_testapi/resources/pod_models.py52
-rw-r--r--cvp/opnfv_testapi/resources/project_handlers.py86
-rw-r--r--cvp/opnfv_testapi/resources/project_models.py48
-rw-r--r--cvp/opnfv_testapi/resources/result_handlers.py303
-rw-r--r--cvp/opnfv_testapi/resources/result_models.py133
-rw-r--r--cvp/opnfv_testapi/resources/scenario_handlers.py282
-rw-r--r--cvp/opnfv_testapi/resources/scenario_models.py204
-rw-r--r--cvp/opnfv_testapi/resources/test_handlers.py200
-rw-r--r--cvp/opnfv_testapi/resources/test_models.py78
-rw-r--r--cvp/opnfv_testapi/resources/testcase_handlers.py103
-rw-r--r--cvp/opnfv_testapi/resources/testcase_models.py95
17 files changed, 2292 insertions, 0 deletions
diff --git a/cvp/opnfv_testapi/resources/__init__.py b/cvp/opnfv_testapi/resources/__init__.py
new file mode 100644
index 00000000..05c0c939
--- /dev/null
+++ b/cvp/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/cvp/opnfv_testapi/resources/application_handlers.py b/cvp/opnfv_testapi/resources/application_handlers.py
new file mode 100644
index 00000000..16dae4f3
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/application_handlers.py
@@ -0,0 +1,147 @@
+##############################################################################
+# 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 json
+
+from tornado import web
+from tornado import gen
+from bson import objectid
+
+from opnfv_testapi.common.config import CONF
+from opnfv_testapi.resources import handlers
+from opnfv_testapi.resources import application_models
+from opnfv_testapi.tornado_swagger import swagger
+from opnfv_testapi.ui.auth import constants as auth_const
+
+
+class GenericApplicationHandler(handlers.GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericApplicationHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = "applications"
+ self.table_cls = application_models.Application
+
+
+class ApplicationsCLHandler(GenericApplicationHandler):
+ @swagger.operation(nickname="queryApplications")
+ def get(self):
+ """
+ @description: Retrieve result(s) for a application project
+ on a specific pod.
+ @notes: Retrieve application.
+ Available filters for this request are :
+ - id : Application 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
+
+ @return 200: all application results consist with query,
+ empty list if no result is found
+ @rtype: L{Applications}
+ """
+ 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
+ }
+
+ self._list(query=self.set_query(), **limitations)
+ logging.debug('list end')
+
+ @swagger.operation(nickname="createApplication")
+ @web.asynchronous
+ def post(self):
+ """
+ @description: create a application
+ @param body: application to be created
+ @type body: L{ApplicationCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: application is created.
+ @raise 404: pod/project/applicationcase 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 = []
+
+ role = self.get_secure_cookie(auth_const.ROLE)
+ if role.find('administrator') == -1:
+ self.finish_request({'code': '403', 'msg': 'Only administrator \
+ is allowed to submit application.'})
+ return
+
+ query = {"openid": self.json_args['user_id']}
+ 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
+
+ self._create(miss_fields=miss_fields, carriers=carriers)
+
+
+class ApplicationsGURHandler(GenericApplicationHandler):
+ @swagger.operation(nickname="deleteAppById")
+ def delete(self, id):
+ query = {'_id': objectid.ObjectId(id)}
+ self._delete(query=query)
+
+ @swagger.operation(nickname="updateApplicationById")
+ def put(self, application_id):
+ """
+ @description: update a single application by id
+ @param body: fields to be updated
+ @type body: L{ApplicationUpdateRequest}
+ @in body: body
+ @rtype: L{Application}
+ @return 200: update success
+ @raise 404: Application not exist
+ @raise 403: nothing to update
+ """
+ data = json.loads(self.request.body)
+ item = data.get('item')
+ value = data.get(item)
+ logging.debug('%s:%s', item, value)
+ try:
+ self.update(application_id, item, value)
+ except Exception as e:
+ logging.error('except:%s', e)
+ return
+
+ @web.asynchronous
+ @gen.coroutine
+ def update(self, application_id, item, value):
+ self.json_args = {}
+ self.json_args[item] = value
+ query = {'_id': application_id, 'owner':
+ self.get_secure_cookie(auth_const.OPENID)}
+ db_keys = ['_id', 'owner']
+ self._update(query=query, db_keys=db_keys)
diff --git a/cvp/opnfv_testapi/resources/application_models.py b/cvp/opnfv_testapi/resources/application_models.py
new file mode 100644
index 00000000..e2bb652d
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/application_models.py
@@ -0,0 +1,39 @@
+##############################################################################
+# Copyright (c) 2015
+# 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.resources import models
+from opnfv_testapi.tornado_swagger import swagger
+
+from datetime import datetime
+
+
+@swagger.model()
+class Application(models.ModelBase):
+ """
+ @property trust_indicator: used for long duration test case
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self, _id=None, owner=None, status="created",
+ creation_date=[], trust_indicator=None):
+ self._id = _id
+ self.owner = owner
+ self.creation_date = datetime.now()
+ self.status = status
+
+
+@swagger.model()
+class Applications(models.ModelBase):
+ """
+ @property applications:
+ @ptype tests: C{list} of L{Application}
+ """
+ def __init__(self):
+ self.applications = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'applications': Application}
diff --git a/cvp/opnfv_testapi/resources/handlers.py b/cvp/opnfv_testapi/resources/handlers.py
new file mode 100644
index 00000000..17a7e359
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/handlers.py
@@ -0,0 +1,321 @@
+##############################################################################
+# 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 datetime import timedelta
+
+import logging
+from tornado import gen
+from tornado import web
+
+from opnfv_testapi.common import check
+from opnfv_testapi.common import message
+from opnfv_testapi.common import raises
+from opnfv_testapi.db import api as dbapi
+from opnfv_testapi.resources import models
+from opnfv_testapi.tornado_swagger import swagger
+from opnfv_testapi.ui.auth import constants as auth_const
+
+DEFAULT_REPRESENTATION = "application/json"
+
+
+class GenericApiHandler(web.RequestHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericApiHandler, self).__init__(application, request, **kwargs)
+ 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'
+ self.db_scenarios = 'scenarios'
+ self.auth = self.settings["auth"]
+
+ def get_int(self, key, value):
+ try:
+ value = int(value)
+ except:
+ raises.BadRequest(message.must_int(key))
+ return value
+
+ def set_query(self):
+ query = dict()
+ date_range = dict()
+ for k in self.request.query_arguments.keys():
+ v = self.get_query_argument(k)
+ if 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 == 'from':
+ date_range.update({'$gte': str(v)})
+ elif k == 'to':
+ date_range.update({'$lt': str(v)})
+ elif k == 'signed':
+ openid = self.get_secure_cookie(auth_const.OPENID)
+ role = self.get_secure_cookie(auth_const.ROLE)
+ logging.info('role:%s', role)
+ if role:
+ if role.find("reviewer") != -1:
+ query['$or'] = [{"shared":
+ {"$elemMatch": {"$eq": openid}}
+ }, {"owner": openid},
+ {"status": {"$ne": "private"}}]
+ else:
+ query['$or'] = [{"shared":
+ {"$elemMatch": {"$eq": openid}}
+ }, {"owner": openid}]
+ elif k not in ['last', 'page', 'descend', 'per_page']:
+ query[k] = v
+ if date_range:
+ query['start_date'] = date_range
+
+ # if $lt is not provided,
+ # empty/None/null/'' start_date will also be returned
+ if 'start_date' in query and '$lt' not in query['start_date']:
+ query['start_date'].update({'$lt': str(datetime.now())})
+
+ logging.debug("query:%s", query)
+ return query
+
+ 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:
+ raises.BadRequest(message.bad_format(str(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 models.CreateResponse(href=href).format()
+
+ def format_data(self, data):
+ cls_data = self.table_cls.from_dict(data)
+ return cls_data.format_http()
+
+ @gen.coroutine
+ @check.no_body
+ @check.miss_fields
+ @check.carriers_exist
+ @check.new_not_exists
+ def _inner_create(self, **kwargs):
+ data = self.table_cls.from_dict(self.json_args)
+ for k, v in kwargs.iteritems():
+ if k != 'query':
+ data.__setattr__(k, v)
+
+ if self.table != 'results':
+ data.creation_date = datetime.now()
+ _id = yield dbapi.db_save(self.table, data.format())
+ logging.warning("_id:%s", _id)
+ raise gen.Return(_id)
+
+ def _create_only(self, **kwargs):
+ resource = self._inner_create(**kwargs)
+ logging.warning("resource:%s", resource)
+
+ @check.authenticate
+ @check.no_body
+ @check.miss_fields
+ @check.carriers_exist
+ @check.new_not_exists
+ def _create(self, **kwargs):
+ # resource = self._inner_create(**kwargs)
+ data = self.table_cls.from_dict(self.json_args)
+ for k, v in kwargs.iteritems():
+ if k != 'query':
+ data.__setattr__(k, v)
+
+ if self.table != 'results':
+ data.creation_date = datetime.now()
+ _id = yield dbapi.db_save(self.table, data.format())
+ if 'name' in self.json_args:
+ resource = data.name
+ else:
+ resource = _id
+
+ self.finish_request(self._create_response(resource))
+
+ @gen.coroutine
+ def _check_if_exists(self, *args, **kwargs):
+ query = kwargs['query']
+ table = kwargs['table']
+ if query and table:
+ data = yield dbapi.db_find_one(table, query)
+ if data:
+ raise gen.Return((True, 'Data alreay exists. %s' % (query)))
+ raise gen.Return((False, 'Data does not exist. %s' % (query)))
+
+ @web.asynchronous
+ @gen.coroutine
+ def _list(self, query=None, res_op=None, *args, **kwargs):
+ sort = kwargs.get('sort')
+ page = kwargs.get('page', 0)
+ last = kwargs.get('last', 0)
+ per_page = kwargs.get('per_page', 0)
+ if query is None:
+ query = {}
+
+ total_pages = 0
+ if page > 0:
+ cursor = dbapi.db_list(self.table, query)
+ records_count = yield cursor.count()
+ total_pages = self._calc_total_pages(records_count,
+ last,
+ page,
+ per_page)
+ pipelines = self._set_pipelines(query, sort, last, page, per_page)
+ cursor = dbapi.db_aggregate(self.table, pipelines)
+ data = list()
+ 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)
+ if page > 0:
+ res.update({
+ 'pagination': {
+ 'current_page': kwargs.get('page'),
+ 'total_pages': total_pages
+ }
+ })
+ self.finish_request(res)
+ logging.debug('_list end')
+
+ @staticmethod
+ def _calc_total_pages(records_count, last, page, per_page):
+ logging.debug("totalItems:%d per_page:%d", records_count, per_page)
+ records_nr = records_count
+ if (records_count > last) and (last > 0):
+ records_nr = last
+
+ total_pages, remainder = divmod(records_nr, per_page)
+ if remainder > 0:
+ total_pages += 1
+ if page > 1 and page > total_pages:
+ raises.BadRequest(
+ 'Request page > total_pages [{}]'.format(total_pages))
+ return total_pages
+
+ @staticmethod
+ def _set_pipelines(query, sort, last, page, per_page):
+ pipelines = list()
+ if query:
+ pipelines.append({'$match': query})
+ if sort:
+ pipelines.append({'$sort': sort})
+
+ if page > 0:
+ pipelines.append({'$skip': (page - 1) * per_page})
+ pipelines.append({'$limit': per_page})
+ elif last > 0:
+ pipelines.append({'$limit': last})
+
+ return pipelines
+
+ @web.asynchronous
+ @gen.coroutine
+ @check.not_exist
+ def _get_one(self, data, query=None):
+ self.finish_request(self.format_data(data))
+
+ @check.authenticate
+ @check.not_exist
+ def _delete(self, data, query=None):
+ yield dbapi.db_delete(self.table, query)
+ self.finish_request()
+
+ @check.authenticate
+ @check.no_body
+ @check.not_exist
+ @check.updated_one_not_exist
+ def _update(self, data, query=None, **kwargs):
+ logging.debug("_update")
+ data = self.table_cls.from_dict(data)
+ update_req = self._update_requests(data)
+ yield dbapi.db_update(self.table, query, update_req)
+ update_req['_id'] = str(data._id)
+ self.finish_request(update_req)
+
+ 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:
+ raises.Forbidden(message.no_update())
+
+ edit_request = data.format()
+ edit_request.update(request)
+ return edit_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.get(key)
+ if new is None:
+ new = old
+ elif new != old:
+ equal = False
+ query[key] = new
+ return query if not equal else dict()
+
+
+class VersionHandler(GenericApiHandler):
+ @swagger.operation(nickname='listAllVersions')
+ def get(self):
+ """
+ @description: list all supported versions
+ @rtype: L{Versions}
+ """
+ versions = [{'version': 'api.cvp.0.7.0', 'description': 'basics'}]
+ self.finish_request({'versions': versions})
diff --git a/cvp/opnfv_testapi/resources/models.py b/cvp/opnfv_testapi/resources/models.py
new file mode 100644
index 00000000..e8fc532b
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/models.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
+# 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
+# feng.xiaowei@zte.com.cn add ModelBase 12-20-2016
+##############################################################################
+import ast
+import copy
+
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class ModelBase(object):
+
+ def format(self):
+ return self._format(['_id'])
+
+ def format_http(self):
+ return self._format([])
+
+ @classmethod
+ def from_dict(cls, a_dict):
+ if a_dict is None:
+ return None
+
+ attr_parser = cls.attr_parser()
+ t = cls()
+ for k, v in a_dict.iteritems():
+ value = v
+ if isinstance(v, dict) and k in attr_parser:
+ value = attr_parser[k].from_dict(v)
+ elif isinstance(v, list) and k in attr_parser:
+ value = []
+ for item in v:
+ value.append(attr_parser[k].from_dict(item))
+
+ t.__setattr__(k, value)
+
+ return t
+
+ @staticmethod
+ def attr_parser():
+ return {}
+
+ def _format(self, excludes):
+ new_obj = copy.deepcopy(self)
+ dicts = new_obj.__dict__
+ for k in dicts.keys():
+ if k in excludes:
+ del dicts[k]
+ elif dicts[k]:
+ dicts[k] = self._obj_format(dicts[k])
+ return dicts
+
+ def _obj_format(self, obj):
+ if self._has_format(obj):
+ obj = obj.format()
+ elif isinstance(obj, unicode):
+ try:
+ obj = self._obj_format(ast.literal_eval(obj))
+ except:
+ try:
+ obj = str(obj)
+ except:
+ obj = obj
+ elif isinstance(obj, list):
+ hs = list()
+ for h in obj:
+ hs.append(self._obj_format(h))
+ obj = hs
+ elif not isinstance(obj, (str, int, float, dict)):
+ obj = str(obj)
+ return obj
+
+ @staticmethod
+ def _has_format(obj):
+ return not isinstance(obj, (str, unicode)) and hasattr(obj, 'format')
+
+
+@swagger.model()
+class CreateResponse(ModelBase):
+ def __init__(self, href=''):
+ self.href = href
+
+
+@swagger.model()
+class Versions(ModelBase):
+ """
+ @property versions:
+ @ptype versions: C{list} of L{Version}
+ """
+
+ def __init__(self):
+ self.versions = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'versions': Version}
+
+
+@swagger.model()
+class Version(ModelBase):
+ def __init__(self, version=None, description=None):
+ self.version = version
+ self.description = description
diff --git a/cvp/opnfv_testapi/resources/pod_handlers.py b/cvp/opnfv_testapi/resources/pod_handlers.py
new file mode 100644
index 00000000..50298875
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/pod_handlers.py
@@ -0,0 +1,78 @@
+##############################################################################
+# 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 handlers
+from opnfv_testapi.resources import pod_models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class GenericPodHandler(handlers.GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericPodHandler, self).__init__(application, request, **kwargs)
+ self.table = 'pods'
+ self.table_cls = pod_models.Pod
+
+
+class PodCLHandler(GenericPodHandler):
+ @swagger.operation(nickname='listAllPods')
+ 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='createPod')
+ 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():
+ return {'name': self.json_args.get('name')}
+ miss_fields = ['name']
+ self._create(miss_fields=miss_fields, query=query)
+
+
+class PodGURHandler(GenericPodHandler):
+ @swagger.operation(nickname='getPodByName')
+ 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
+ """
+ self._get_one(query={'name': pod_name})
+
+ 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/cvp/opnfv_testapi/resources/pod_models.py b/cvp/opnfv_testapi/resources/pod_models.py
new file mode 100644
index 00000000..2c3ea978
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/pod_models.py
@@ -0,0 +1,52 @@
+##############################################################################
+# 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.resources import models
+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(models.ModelBase):
+ def __init__(self, name, mode='', details='', role=""):
+ self.name = name
+ self.mode = mode
+ self.details = details
+ self.role = role
+
+
+@swagger.model()
+class Pod(models.ModelBase):
+ def __init__(self,
+ name='', mode='', details='',
+ role="", _id='', create_date=''):
+ self.name = name
+ self.mode = mode
+ self.details = details
+ self.role = role
+ self._id = _id
+ self.creation_date = create_date
+
+
+@swagger.model()
+class Pods(models.ModelBase):
+ """
+ @property pods:
+ @ptype pods: C{list} of L{Pod}
+ """
+ def __init__(self):
+ self.pods = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'pods': Pod}
diff --git a/cvp/opnfv_testapi/resources/project_handlers.py b/cvp/opnfv_testapi/resources/project_handlers.py
new file mode 100644
index 00000000..be295070
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/project_handlers.py
@@ -0,0 +1,86 @@
+##############################################################################
+# 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.resources import handlers
+from opnfv_testapi.resources import project_models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class GenericProjectHandler(handlers.GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericProjectHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = 'projects'
+ self.table_cls = project_models.Project
+
+
+class ProjectCLHandler(GenericProjectHandler):
+ @swagger.operation(nickname="listAllProjects")
+ 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="createProject")
+ 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():
+ return {'name': self.json_args.get('name')}
+ miss_fields = ['name']
+ self._create(miss_fields=miss_fields, query=query)
+
+
+class ProjectGURHandler(GenericProjectHandler):
+ @swagger.operation(nickname='getProjectByName')
+ 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(query={'name': project_name})
+
+ @swagger.operation(nickname="updateProjectByName")
+ 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=query, db_keys=db_keys)
+
+ @swagger.operation(nickname='deleteProjectByName')
+ def delete(self, project_name):
+ """
+ @description: delete a project by project_name
+ @return 200: delete success
+ @raise 404: project not exist
+ """
+ self._delete(query={'name': project_name})
diff --git a/cvp/opnfv_testapi/resources/project_models.py b/cvp/opnfv_testapi/resources/project_models.py
new file mode 100644
index 00000000..3243882b
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/project_models.py
@@ -0,0 +1,48 @@
+##############################################################################
+# 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.resources import models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+@swagger.model()
+class ProjectCreateRequest(models.ModelBase):
+ def __init__(self, name, description=''):
+ self.name = name
+ self.description = description
+
+
+@swagger.model()
+class ProjectUpdateRequest(models.ModelBase):
+ def __init__(self, name='', description=''):
+ self.name = name
+ self.description = description
+
+
+@swagger.model()
+class Project(models.ModelBase):
+ 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
+
+
+@swagger.model()
+class Projects(models.ModelBase):
+ """
+ @property projects:
+ @ptype projects: C{list} of L{Project}
+ """
+ def __init__(self):
+ self.projects = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'projects': Project}
diff --git a/cvp/opnfv_testapi/resources/result_handlers.py b/cvp/opnfv_testapi/resources/result_handlers.py
new file mode 100644
index 00000000..8cb9a347
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/result_handlers.py
@@ -0,0 +1,303 @@
+##############################################################################
+# 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
+from datetime import datetime
+from datetime import timedelta
+import json
+import tarfile
+import io
+
+from tornado import gen
+from tornado import web
+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 result_models
+from opnfv_testapi.tornado_swagger import swagger
+from opnfv_testapi.ui.auth import constants as auth_const
+
+
+class GenericResultHandler(handlers.GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericResultHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = self.db_results
+ self.table_cls = result_models.TestResult
+
+ def get_int(self, key, value):
+ try:
+ value = int(value)
+ except:
+ raises.BadRequest(message.must_int(key))
+ return value
+
+ def set_query(self):
+ query = dict()
+ date_range = dict()
+
+ query['public'] = {'$not': {'$eq': 'false'}}
+ 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 == 'from':
+ date_range.update({'$gte': str(v)})
+ elif k == 'to':
+ date_range.update({'$lt': str(v)})
+ elif k == 'signed':
+ openid = self.get_secure_cookie(auth_const.OPENID)
+ role = self.get_secure_cookie(auth_const.ROLE)
+ logging.info('role:%s', role)
+ if role:
+ del query['public']
+ query['user'] = openid
+ if role.find("reviewer") != -1:
+ del query['user']
+ query['review'] = 'true'
+ elif k not in ['last', 'page', 'descend']:
+ query[k] = v
+ if date_range:
+ query['start_date'] = date_range
+
+ # if $lt is not provided,
+ # empty/None/null/'' start_date will also be returned
+ if 'start_date' in query and '$lt' not in query['start_date']:
+ query['start_date'].update({'$lt': str(datetime.now())})
+
+ return query
+
+
+class ResultsCLHandler(GenericResultHandler):
+ @swagger.operation(nickname="queryTestResults")
+ 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/apex/compass/joid/daisy
+ - build_tag : Jenkins build tag name
+ - 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
+ - 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
+ - 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{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 from: i.e. 2016-01-01 or 2016-01-01 00:01:23
+ @type from: L{string}
+ @in from: query
+ @required from: False
+ @param to: i.e. 2016-01-01 or 2016-01-01 00:01:23
+ @type to: L{string}
+ @in to: query
+ @required to: False
+ @param last: last records stored until now
+ @type last: L{string}
+ @in last: query
+ @required last: False
+ @param page: which page to list
+ @type page: L{int}
+ @in page: query
+ @required page: False
+ @param trust_indicator: must be float
+ @type trust_indicator: L{float}
+ @in trust_indicator: query
+ @required trust_indicator: False
+ @param signed: user results or all results
+ @type signed: L{string}
+ @in signed: query
+ @required signed: False
+ @param descend: true, newest2oldest; false, oldest2newest
+ @type descend: L{string}
+ @in descend: query
+ @required descend: False
+ """
+ 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
+ }
+
+ self._list(query=self.set_query(), **limitations)
+
+ @swagger.operation(nickname="createTestResult")
+ 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
+ """
+ self._post()
+
+ def _post(self):
+ def pod_query():
+ return {'name': self.json_args.get('pod_name')}
+
+ def project_query():
+ return {'name': self.json_args.get('project_name')}
+
+ def testcase_query():
+ return {'project_name': self.json_args.get('project_name'),
+ 'name': self.json_args.get('case_name')}
+
+ miss_fields = ['pod_name', 'project_name', 'case_name']
+ carriers = [('pods', pod_query),
+ ('projects', project_query),
+ ('testcases', testcase_query)]
+
+ self._create(miss_fields=miss_fields, carriers=carriers)
+
+
+class ResultsUploadHandler(ResultsCLHandler):
+ @swagger.operation(nickname="uploadTestResult")
+ @web.asynchronous
+ @gen.coroutine
+ def post(self):
+ """
+ @description: upload and 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
+ """
+ fileinfo = self.request.files['file'][0]
+ tar_in = tarfile.open(fileobj=io.BytesIO(fileinfo['body']),
+ mode="r:gz")
+ results = tar_in.extractfile('results/results.json').read()
+ results = results.split('\n')
+ result_ids = []
+ for result in results:
+ if result == '':
+ continue
+ self.json_args = json.loads(result).copy()
+ build_tag = self.json_args['build_tag']
+ _id = yield self._inner_create()
+ result_ids.append(str(_id))
+ test_id = build_tag[13:49]
+ log_path = '/home/testapi/logs/%s' % (test_id)
+ tar_in.extractall(log_path)
+ log_filename = "/home/testapi/logs/log_%s.tar.gz" % (test_id)
+ with open(log_filename, "wb") as tar_out:
+ tar_out.write(fileinfo['body'])
+ resp = {'id': test_id, 'results': result_ids}
+ self.finish_request(resp)
+
+
+class ResultsGURHandler(GenericResultHandler):
+ @swagger.operation(nickname='DeleteTestResultById')
+ def delete(self, result_id):
+ query = {'_id': objectid.ObjectId(result_id)}
+ self._delete(query=query)
+
+ @swagger.operation(nickname='getTestResultById')
+ 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.ObjectId(result_id)
+ self._get_one(query=query)
+
+ @swagger.operation(nickname="updateTestResultById")
+ 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.ObjectId(result_id)}
+ db_keys = []
+ self._update(query=query, db_keys=db_keys)
diff --git a/cvp/opnfv_testapi/resources/result_models.py b/cvp/opnfv_testapi/resources/result_models.py
new file mode 100644
index 00000000..698f4981
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/result_models.py
@@ -0,0 +1,133 @@
+##############################################################################
+# 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.resources import models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+@swagger.model()
+class TIHistory(models.ModelBase):
+ """
+ @ptype step: L{float}
+ """
+ def __init__(self, date=None, step=0):
+ self.date = date
+ self.step = step
+
+
+@swagger.model()
+class TI(models.ModelBase):
+ """
+ @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()
+
+ @staticmethod
+ def attr_parser():
+ return {'histories': TIHistory}
+
+
+@swagger.model()
+class ResultCreateRequest(models.ModelBase):
+ """
+ @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,
+ user=None,
+ public="true",
+ review="false",
+ 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.user = user
+ self.public = public
+ self.review = review
+ self.trust_indicator = trust_indicator if trust_indicator else TI(0)
+
+
+@swagger.model()
+class ResultUpdateRequest(models.ModelBase):
+ """
+ @property trust_indicator:
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self, trust_indicator=None):
+ self.trust_indicator = trust_indicator
+
+
+@swagger.model()
+class TestResult(models.ModelBase):
+ """
+ @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,
+ user=None, public="true", review="false",
+ 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.user = user
+ self.public = public
+ self.review = review
+ self.trust_indicator = trust_indicator
+
+ @staticmethod
+ def attr_parser():
+ return {'trust_indicator': TI}
+
+
+@swagger.model()
+class TestResults(models.ModelBase):
+ """
+ @property results:
+ @ptype results: C{list} of L{TestResult}
+ """
+ def __init__(self):
+ self.results = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'results': TestResult}
diff --git a/cvp/opnfv_testapi/resources/scenario_handlers.py b/cvp/opnfv_testapi/resources/scenario_handlers.py
new file mode 100644
index 00000000..5d420a56
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/scenario_handlers.py
@@ -0,0 +1,282 @@
+import functools
+
+from opnfv_testapi.common import message
+from opnfv_testapi.common import raises
+from opnfv_testapi.resources import handlers
+import opnfv_testapi.resources.scenario_models as models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class GenericScenarioHandler(handlers.GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericScenarioHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = self.db_scenarios
+ self.table_cls = models.Scenario
+
+
+class ScenariosCLHandler(GenericScenarioHandler):
+ @swagger.operation(nickname="queryScenarios")
+ def get(self):
+ """
+ @description: Retrieve scenario(s).
+ @notes: Retrieve scenario(s)
+ Available filters for this request are :
+ - name : scenario name
+
+ GET /scenarios?name=scenario_1
+ @param name: scenario name
+ @type name: L{string}
+ @in name: query
+ @required name: False
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: False
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: False
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: False
+ @return 200: all scenarios satisfy queries,
+ empty list if no scenario is found
+ @rtype: L{Scenarios}
+ """
+
+ def _set_query():
+ query = dict()
+ elem_query = dict()
+ for k in self.request.query_arguments.keys():
+ v = self.get_query_argument(k)
+ if k == 'installer':
+ elem_query["installer"] = v
+ elif k == 'version':
+ elem_query["versions.version"] = v
+ elif k == 'project':
+ elem_query["versions.projects.project"] = v
+ else:
+ query[k] = v
+ if elem_query:
+ query['installers'] = {'$elemMatch': elem_query}
+ return query
+
+ self._list(query=_set_query())
+
+ @swagger.operation(nickname="createScenario")
+ def post(self):
+ """
+ @description: create a new scenario by name
+ @param body: scenario to be created
+ @type body: L{ScenarioCreateRequest}
+ @in body: body
+ @rtype: L{CreateResponse}
+ @return 200: scenario is created.
+ @raise 403: scenario already exists
+ @raise 400: body or name not provided
+ """
+ def query():
+ return {'name': self.json_args.get('name')}
+ miss_fields = ['name']
+ self._create(miss_fields=miss_fields, query=query)
+
+
+class ScenarioGURHandler(GenericScenarioHandler):
+ @swagger.operation(nickname='getScenarioByName')
+ def get(self, name):
+ """
+ @description: get a single scenario by name
+ @rtype: L{Scenario}
+ @return 200: scenario exist
+ @raise 404: scenario not exist
+ """
+ self._get_one(query={'name': name})
+ pass
+
+ @swagger.operation(nickname="updateScenarioByName")
+ def put(self, name):
+ """
+ @description: update a single scenario by name
+ @param body: fields to be updated
+ @type body: L{ScenarioUpdateRequest}
+ @in body: body
+ @rtype: L{Scenario}
+ @return 200: update success
+ @raise 404: scenario not exist
+ @raise 403: nothing to update
+ """
+ query = {'name': name}
+ db_keys = ['name']
+ self._update(query=query, db_keys=db_keys)
+
+ @swagger.operation(nickname="deleteScenarioByName")
+ def delete(self, name):
+ """
+ @description: delete a scenario by name
+ @return 200: delete success
+ @raise 404: scenario not exist:
+ """
+
+ self._delete(query={'name': name})
+
+ def _update_query(self, keys, data):
+ query = dict()
+ if self._is_rename():
+ new = self._term.get('name')
+ if data.get('name') != new:
+ query['name'] = new
+
+ return query
+
+ def _update_requests(self, data):
+ updates = {
+ ('name', 'update'): self._update_requests_rename,
+ ('installer', 'add'): self._update_requests_add_installer,
+ ('installer', 'delete'): self._update_requests_delete_installer,
+ ('version', 'add'): self._update_requests_add_version,
+ ('version', 'delete'): self._update_requests_delete_version,
+ ('owner', 'update'): self._update_requests_change_owner,
+ ('project', 'add'): self._update_requests_add_project,
+ ('project', 'delete'): self._update_requests_delete_project,
+ ('customs', 'add'): self._update_requests_add_customs,
+ ('customs', 'delete'): self._update_requests_delete_customs,
+ ('score', 'add'): self._update_requests_add_score,
+ ('trust_indicator', 'add'): self._update_requests_add_ti,
+ }
+
+ updates[(self._field, self._op)](data)
+
+ return data.format()
+
+ def _iter_installers(xstep):
+ @functools.wraps(xstep)
+ def magic(self, data):
+ [xstep(self, installer)
+ for installer in self._filter_installers(data.installers)]
+ return magic
+
+ def _iter_versions(xstep):
+ @functools.wraps(xstep)
+ def magic(self, installer):
+ [xstep(self, version)
+ for version in (self._filter_versions(installer.versions))]
+ return magic
+
+ def _iter_projects(xstep):
+ @functools.wraps(xstep)
+ def magic(self, version):
+ [xstep(self, project)
+ for project in (self._filter_projects(version.projects))]
+ return magic
+
+ def _update_requests_rename(self, data):
+ data.name = self._term.get('name')
+ if not data.name:
+ raises.BadRequest(message.missing('name'))
+
+ def _update_requests_add_installer(self, data):
+ data.installers.append(models.ScenarioInstaller.from_dict(self._term))
+
+ def _update_requests_delete_installer(self, data):
+ data.installers = self._remove_installers(data.installers)
+
+ @_iter_installers
+ def _update_requests_add_version(self, installer):
+ installer.versions.append(models.ScenarioVersion.from_dict(self._term))
+
+ @_iter_installers
+ def _update_requests_delete_version(self, installer):
+ installer.versions = self._remove_versions(installer.versions)
+
+ @_iter_installers
+ @_iter_versions
+ def _update_requests_change_owner(self, version):
+ version.owner = self._term.get('owner')
+
+ @_iter_installers
+ @_iter_versions
+ def _update_requests_add_project(self, version):
+ version.projects.append(models.ScenarioProject.from_dict(self._term))
+
+ @_iter_installers
+ @_iter_versions
+ def _update_requests_delete_project(self, version):
+ version.projects = self._remove_projects(version.projects)
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_add_customs(self, project):
+ project.customs = list(set(project.customs + self._term))
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_delete_customs(self, project):
+ project.customs = filter(
+ lambda f: f not in self._term,
+ project.customs)
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_add_score(self, project):
+ project.scores.append(
+ models.ScenarioScore.from_dict(self._term))
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_add_ti(self, project):
+ project.trust_indicators.append(
+ models.ScenarioTI.from_dict(self._term))
+
+ def _is_rename(self):
+ return self._field == 'name' and self._op == 'update'
+
+ def _remove_installers(self, installers):
+ return self._remove('installer', installers)
+
+ def _filter_installers(self, installers):
+ return self._filter('installer', installers)
+
+ def _remove_versions(self, versions):
+ return self._remove('version', versions)
+
+ def _filter_versions(self, versions):
+ return self._filter('version', versions)
+
+ def _remove_projects(self, projects):
+ return self._remove('project', projects)
+
+ def _filter_projects(self, projects):
+ return self._filter('project', projects)
+
+ def _remove(self, field, fields):
+ return filter(
+ lambda f: getattr(f, field) != self._locate.get(field),
+ fields)
+
+ def _filter(self, field, fields):
+ return filter(
+ lambda f: getattr(f, field) == self._locate.get(field),
+ fields)
+
+ @property
+ def _field(self):
+ return self.json_args.get('field')
+
+ @property
+ def _op(self):
+ return self.json_args.get('op')
+
+ @property
+ def _locate(self):
+ return self.json_args.get('locate')
+
+ @property
+ def _term(self):
+ return self.json_args.get('term')
diff --git a/cvp/opnfv_testapi/resources/scenario_models.py b/cvp/opnfv_testapi/resources/scenario_models.py
new file mode 100644
index 00000000..467cff24
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/scenario_models.py
@@ -0,0 +1,204 @@
+from opnfv_testapi.resources import models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+def list_default(value):
+ return value if value else list()
+
+
+def dict_default(value):
+ return value if value else dict()
+
+
+@swagger.model()
+class ScenarioTI(models.ModelBase):
+ def __init__(self, date=None, status='silver'):
+ self.date = date
+ self.status = status
+
+
+@swagger.model()
+class ScenarioScore(models.ModelBase):
+ def __init__(self, date=None, score='0'):
+ self.date = date
+ self.score = score
+
+
+@swagger.model()
+class ScenarioProject(models.ModelBase):
+ """
+ @property customs:
+ @ptype customs: C{list} of L{string}
+ @property scores:
+ @ptype scores: C{list} of L{ScenarioScore}
+ @property trust_indicators:
+ @ptype trust_indicators: C{list} of L{ScenarioTI}
+ """
+ def __init__(self,
+ project='',
+ customs=None,
+ scores=None,
+ trust_indicators=None):
+ self.project = project
+ self.customs = list_default(customs)
+ self.scores = list_default(scores)
+ self.trust_indicators = list_default(trust_indicators)
+
+ @staticmethod
+ def attr_parser():
+ return {'scores': ScenarioScore,
+ 'trust_indicators': ScenarioTI}
+
+ def __eq__(self, other):
+ return [self.project == other.project and
+ self._customs_eq(other) and
+ self._scores_eq(other) and
+ self._ti_eq(other)]
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _customs_eq(self, other):
+ return set(self.customs) == set(other.customs)
+
+ def _scores_eq(self, other):
+ return set(self.scores) == set(other.scores)
+
+ def _ti_eq(self, other):
+ return set(self.trust_indicators) == set(other.trust_indicators)
+
+
+@swagger.model()
+class ScenarioVersion(models.ModelBase):
+ """
+ @property projects:
+ @ptype projects: C{list} of L{ScenarioProject}
+ """
+ def __init__(self, version=None, projects=None):
+ self.version = version
+ self.projects = list_default(projects)
+
+ @staticmethod
+ def attr_parser():
+ return {'projects': ScenarioProject}
+
+ def __eq__(self, other):
+ return [self.version == other.version and self._projects_eq(other)]
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _projects_eq(self, other):
+ for s_project in self.projects:
+ for o_project in other.projects:
+ if s_project.project == o_project.project:
+ if s_project != o_project:
+ return False
+
+ return True
+
+
+@swagger.model()
+class ScenarioInstaller(models.ModelBase):
+ """
+ @property versions:
+ @ptype versions: C{list} of L{ScenarioVersion}
+ """
+ def __init__(self, installer=None, versions=None):
+ self.installer = installer
+ self.versions = list_default(versions)
+
+ @staticmethod
+ def attr_parser():
+ return {'versions': ScenarioVersion}
+
+ def __eq__(self, other):
+ return [self.installer == other.installer and self._versions_eq(other)]
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _versions_eq(self, other):
+ for s_version in self.versions:
+ for o_version in other.versions:
+ if s_version.version == o_version.version:
+ if s_version != o_version:
+ return False
+
+ return True
+
+
+@swagger.model()
+class ScenarioCreateRequest(models.ModelBase):
+ """
+ @property installers:
+ @ptype installers: C{list} of L{ScenarioInstaller}
+ """
+ def __init__(self, name='', installers=None):
+ self.name = name
+ self.installers = list_default(installers)
+
+ @staticmethod
+ def attr_parser():
+ return {'installers': ScenarioInstaller}
+
+
+@swagger.model()
+class ScenarioUpdateRequest(models.ModelBase):
+ """
+ @property field: update field
+ @property op: add/delete/update
+ @property locate: information used to locate the field
+ @property term: new value
+ """
+ def __init__(self, field=None, op=None, locate=None, term=None):
+ self.field = field
+ self.op = op
+ self.locate = dict_default(locate)
+ self.term = dict_default(term)
+
+
+@swagger.model()
+class Scenario(models.ModelBase):
+ """
+ @property installers:
+ @ptype installers: C{list} of L{ScenarioInstaller}
+ """
+ def __init__(self, name='', create_date='', _id='', installers=None):
+ self.name = name
+ self._id = _id
+ self.creation_date = create_date
+ self.installers = list_default(installers)
+
+ @staticmethod
+ def attr_parser():
+ return {'installers': ScenarioInstaller}
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __eq__(self, other):
+ return [self.name == other.name and self._installers_eq(other)]
+
+ def _installers_eq(self, other):
+ for s_install in self.installers:
+ for o_install in other.installers:
+ if s_install.installer == o_install.installer:
+ if s_install != o_install:
+ return False
+
+ return True
+
+
+@swagger.model()
+class Scenarios(models.ModelBase):
+ """
+ @property scenarios:
+ @ptype scenarios: C{list} of L{Scenario}
+ """
+ def __init__(self):
+ self.scenarios = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'scenarios': Scenario}
diff --git a/cvp/opnfv_testapi/resources/test_handlers.py b/cvp/opnfv_testapi/resources/test_handlers.py
new file mode 100644
index 00000000..9ad1bbc2
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/test_handlers.py
@@ -0,0 +1,200 @@
+##############################################################################
+# 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 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.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
+
+
+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")
+ 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
+ }
+
+ self._list(query=self.set_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")
+ def get(self, test_id):
+ query = dict()
+ query["_id"] = objectid.ObjectId(test_id)
+ self._get_one(query=query)
+
+ @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, test_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(test_id, item, value)
+ except Exception as e:
+ logging.error('except:%s', e)
+ return
+
+ @gen.coroutine
+ def update(self, test_id, item, value):
+ logging.debug("update")
+ if item == "shared":
+ if len(value) != len(set(value)):
+ msg = "Already shared with this user"
+ self.finish_request({'code': '403', 'msg': msg})
+ return
+
+ for user in value:
+ query = {"openid": 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
+
+ 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': test_id}
+ db_keys = ['id', ]
+ user = self.get_secure_cookie(auth_const.OPENID)
+ if item == "shared":
+ query['owner'] = 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, {}))
diff --git a/cvp/opnfv_testapi/resources/test_models.py b/cvp/opnfv_testapi/resources/test_models.py
new file mode 100644
index 00000000..41fa6852
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/test_models.py
@@ -0,0 +1,78 @@
+##############################################################################
+# 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.resources import models
+from opnfv_testapi.tornado_swagger import swagger
+
+from datetime import datetime
+
+
+@swagger.model()
+class TestCreateRequest(models.ModelBase):
+ """
+ @property trust_indicator:
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self,
+ _id=None,
+ owner=None,
+ results=[],
+ public="false",
+ review="false",
+ status="private",
+ shared=[]):
+ self._id = _id
+ self.owner = owner
+ self.results = results.copy()
+ self.public = public
+ self.review = review
+ self.upload_date = datetime.now()
+ self.status = status
+ self.shared = shared
+
+
+class ResultUpdateRequest(models.ModelBase):
+ """
+ @property trust_indicator:
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self, trust_indicator=None):
+ self.trust_indicator = trust_indicator
+
+
+@swagger.model()
+class Test(models.ModelBase):
+ """
+ @property trust_indicator: used for long duration test case
+ @ptype trust_indicator: L{TI}
+ """
+ def __init__(self, _id=None, owner=None, results=[],
+ public="false", review="false", status="private",
+ shared=[], trust_indicator=None):
+ self._id = _id
+ self.owner = owner
+ self.results = results
+ self.public = public
+ self.review = review
+ self.upload_date = datetime.now()
+ self.status = status
+ self.shared = shared
+
+
+@swagger.model()
+class Tests(models.ModelBase):
+ """
+ @property tests:
+ @ptype tests: C{list} of L{Test}
+ """
+ def __init__(self):
+ self.tests = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'tests': Test}
diff --git a/cvp/opnfv_testapi/resources/testcase_handlers.py b/cvp/opnfv_testapi/resources/testcase_handlers.py
new file mode 100644
index 00000000..9399326f
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/testcase_handlers.py
@@ -0,0 +1,103 @@
+##############################################################################
+# 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.resources import handlers
+from opnfv_testapi.resources import testcase_models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class GenericTestcaseHandler(handlers.GenericApiHandler):
+ def __init__(self, application, request, **kwargs):
+ super(GenericTestcaseHandler, self).__init__(application,
+ request,
+ **kwargs)
+ self.table = self.db_testcases
+ self.table_cls = testcase_models.Testcase
+
+
+class TestcaseCLHandler(GenericTestcaseHandler):
+ @swagger.operation(nickname="listAllTestCases")
+ 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}
+ """
+ self._list(query={'project_name': project_name})
+
+ @swagger.operation(nickname="createTestCase")
+ 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 project_query():
+ return {'name': project_name}
+
+ def testcase_query():
+ return {'project_name': project_name,
+ 'name': self.json_args.get('name')}
+ miss_fields = ['name']
+ carriers = [(self.db_projects, project_query)]
+ self._create(miss_fields=miss_fields,
+ carriers=carriers,
+ query=testcase_query,
+ project_name=project_name)
+
+
+class TestcaseGURHandler(GenericTestcaseHandler):
+ @swagger.operation(nickname='getTestCaseByName')
+ 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=query)
+
+ @swagger.operation(nickname="updateTestCaseByName")
+ 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=query, db_keys=db_keys)
+
+ @swagger.operation(nickname='deleteTestCaseByName')
+ 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=query)
diff --git a/cvp/opnfv_testapi/resources/testcase_models.py b/cvp/opnfv_testapi/resources/testcase_models.py
new file mode 100644
index 00000000..2379dfc4
--- /dev/null
+++ b/cvp/opnfv_testapi/resources/testcase_models.py
@@ -0,0 +1,95 @@
+##############################################################################
+# 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.resources import models
+from opnfv_testapi.tornado_swagger import swagger
+
+
+@swagger.model()
+class TestcaseCreateRequest(models.ModelBase):
+ def __init__(self, name, url=None, description=None,
+ catalog_description=None, tier=None, ci_loop=None,
+ criteria=None, blocking=None, dependencies=None, run=None,
+ domains=None, tags=None, version=None):
+ self.name = name
+ self.url = url
+ self.description = description
+ self.catalog_description = catalog_description
+ self.tier = tier
+ self.ci_loop = ci_loop
+ self.criteria = criteria
+ self.blocking = blocking
+ self.dependencies = dependencies
+ self.run = run
+ self.domains = domains
+ self.tags = tags
+ self.version = version
+ self.trust = "Silver"
+
+
+@swagger.model()
+class TestcaseUpdateRequest(models.ModelBase):
+ def __init__(self, name=None, description=None, project_name=None,
+ catalog_description=None, tier=None, ci_loop=None,
+ criteria=None, blocking=None, dependencies=None, run=None,
+ domains=None, tags=None, version=None, trust=None):
+ self.name = name
+ self.description = description
+ self.catalog_description = catalog_description
+ self.project_name = project_name
+ self.tier = tier
+ self.ci_loop = ci_loop
+ self.criteria = criteria
+ self.blocking = blocking
+ self.dependencies = dependencies
+ self.run = run
+ self.domains = domains
+ self.tags = tags
+ self.version = version
+ self.trust = trust
+
+
+@swagger.model()
+class Testcase(models.ModelBase):
+ def __init__(self, _id=None, name=None, project_name=None,
+ description=None, url=None, creation_date=None,
+ catalog_description=None, tier=None, ci_loop=None,
+ criteria=None, blocking=None, dependencies=None, run=None,
+ domains=None, tags=None, version=None,
+ trust=None):
+ self._id = None
+ self.name = None
+ self.project_name = None
+ self.description = None
+ self.catalog_description = None
+ self.url = None
+ self.creation_date = None
+ self.tier = None
+ self.ci_loop = None
+ self.criteria = None
+ self.blocking = None
+ self.dependencies = None
+ self.run = None
+ self.domains = None
+ self.tags = None
+ self.version = None
+ self.trust = None
+
+
+@swagger.model()
+class Testcases(models.ModelBase):
+ """
+ @property testcases:
+ @ptype testcases: C{list} of L{Testcase}
+ """
+ def __init__(self):
+ self.testcases = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'testcases': Testcase}