summaryrefslogtreecommitdiffstats
path: root/reporting/api
diff options
context:
space:
mode:
Diffstat (limited to 'reporting/api')
-rw-r--r--reporting/api/conf.py4
-rw-r--r--reporting/api/extension/__init__.py0
-rw-r--r--reporting/api/extension/client.py15
-rw-r--r--reporting/api/handlers/scenarios.py27
-rw-r--r--reporting/api/server.py8
-rw-r--r--reporting/api/service/__init__.py0
-rw-r--r--reporting/api/service/result.py90
-rw-r--r--reporting/api/service/scenario.py133
-rw-r--r--reporting/api/urls.py5
9 files changed, 280 insertions, 2 deletions
diff --git a/reporting/api/conf.py b/reporting/api/conf.py
index 5897d4f..e05c1fd 100644
--- a/reporting/api/conf.py
+++ b/reporting/api/conf.py
@@ -1 +1,5 @@
base_url = 'http://testresults.opnfv.org/test/api/v1'
+
+versions = ['master', 'euphrates', 'fraser']
+
+period = 10
diff --git a/reporting/api/extension/__init__.py b/reporting/api/extension/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/reporting/api/extension/__init__.py
diff --git a/reporting/api/extension/client.py b/reporting/api/extension/client.py
new file mode 100644
index 0000000..03371fd
--- /dev/null
+++ b/reporting/api/extension/client.py
@@ -0,0 +1,15 @@
+from tornado.simple_httpclient import SimpleAsyncHTTPClient
+from tornado.log import gen_log
+
+
+class NoQueueTimeoutHTTPClient(SimpleAsyncHTTPClient):
+ def fetch_impl(self, request, callback):
+ key = object()
+
+ self.queue.append((key, request, callback))
+ self.waiting[key] = (request, callback, None)
+
+ self._process_queue()
+
+ if self.queue:
+ gen_log.debug("max_clients limit reached, request queued.")
diff --git a/reporting/api/handlers/scenarios.py b/reporting/api/handlers/scenarios.py
new file mode 100644
index 0000000..70447c7
--- /dev/null
+++ b/reporting/api/handlers/scenarios.py
@@ -0,0 +1,27 @@
+from tornado.web import asynchronous
+from tornado.gen import coroutine
+from tornado.escape import json_encode
+
+from api.handlers import BaseHandler
+from api.service.scenario import ScenarioTableResult
+
+
+class Result(BaseHandler):
+ @asynchronous
+ @coroutine
+ def get(self):
+ self._set_header()
+
+ scenario = self.get_argument('scenario', None)
+ version = self.get_argument('version', 'master')
+ installer = self.get_argument('installer', None)
+ iteration = int(self.get_argument('iteration', 10))
+
+ yield self._get_scenario_data(scenario, version, installer, iteration)
+
+ @coroutine
+ def _get_scenario_data(self, scenario, version, installer, iteration):
+ results = ScenarioTableResult(scenario, version, installer, iteration)
+ result = yield results.get()
+ self.write(json_encode(result))
+ self.finish()
diff --git a/reporting/api/server.py b/reporting/api/server.py
index e340b01..461e6d5 100644
--- a/reporting/api/server.py
+++ b/reporting/api/server.py
@@ -12,6 +12,7 @@ from tornado.options import define
from tornado.options import options
from api.urls import mappings
+from api.service.result import ResultCache
define("port", default=8000, help="run on the given port", type=int)
@@ -20,7 +21,12 @@ def main():
tornado.options.parse_command_line()
application = tornado.web.Application(mappings)
application.listen(options.port)
- tornado.ioloop.IOLoop.current().start()
+
+ tornado.ioloop.PeriodicCallback(ResultCache.update, 1800000).start()
+
+ ioloop = tornado.ioloop.IOLoop.current()
+ ioloop.call_later(1000, ResultCache.update())
+ ioloop.start()
if __name__ == "__main__":
diff --git a/reporting/api/service/__init__.py b/reporting/api/service/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/reporting/api/service/__init__.py
diff --git a/reporting/api/service/result.py b/reporting/api/service/result.py
new file mode 100644
index 0000000..fa6553c
--- /dev/null
+++ b/reporting/api/service/result.py
@@ -0,0 +1,90 @@
+import json
+import logging
+from collections import defaultdict
+
+from tornado.gen import coroutine
+from tornado.gen import Return
+from tornado.httpclient import AsyncHTTPClient
+
+from api import conf as consts
+from api.extension.client import NoQueueTimeoutHTTPClient
+
+LOG = logging.getLogger(__name__)
+AsyncHTTPClient.configure(NoQueueTimeoutHTTPClient)
+
+
+class Result(object):
+
+ def __init__(self):
+ self._url = '{}/results?period={}&version={}&page={}'
+ self._client = AsyncHTTPClient()
+ self._result = defaultdict(list)
+
+ @property
+ @coroutine
+ def result(self):
+ if not self._result:
+ yield self.update_results()
+ raise Return(self._result)
+
+ @coroutine
+ def update_results(self):
+ LOG.info('start update results')
+
+ for version in consts.versions:
+ yield self._update_version_result(version)
+
+ LOG.info('results update finished')
+
+ @coroutine
+ def _update_version_result(self, version):
+ total_page = yield self._get_total_page(version)
+
+ responses = yield self._fetch_results(version, total_page)
+
+ self._update_version_dict(version, responses)
+
+ @coroutine
+ def _fetch_results(self, version, total_page):
+ urls = [self._url.format(consts.base_url, consts.period, version, i)
+ for i in range(1, total_page + 1)]
+ responses = yield [self._client.fetch(url) for url in urls]
+ raise Return(responses)
+
+ @coroutine
+ def _get_total_page(self, version):
+ url = self._url.format(consts.base_url, consts.period, version, 1)
+ response = yield self._client.fetch(url)
+ raise Return(json.loads(response.body)['pagination']['total_pages'])
+
+ def _update_version_dict(self, version, responses):
+ for response in responses:
+ results = json.loads(response.body)['results']
+ for result in results:
+ data = {k: v for k, v in result.items() if k != 'details'}
+ self._result[version].append(data)
+
+
+class ResultCache(Result):
+
+ @classmethod
+ @coroutine
+ def update(cls):
+ cls._check()
+
+ yield cls.cache.update_results()
+
+ @classmethod
+ @coroutine
+ def get(cls, version):
+ cls._check()
+
+ result = yield cls.cache.result
+ raise Return(result[version])
+
+ @classmethod
+ def _check(cls):
+ try:
+ cls.cache
+ except AttributeError:
+ cls.cache = cls()
diff --git a/reporting/api/service/scenario.py b/reporting/api/service/scenario.py
new file mode 100644
index 0000000..f9e2a91
--- /dev/null
+++ b/reporting/api/service/scenario.py
@@ -0,0 +1,133 @@
+import abc
+from collections import defaultdict
+
+import six
+from tornado.gen import coroutine
+from tornado.gen import Return
+
+from api.service.result import ResultCache
+
+PROJECTS = ['fastdatastacks', 'barometer', 'sfc', 'sdnvpn', 'doctor', 'parser']
+
+
+def _set(key, llist):
+ return set(x[key] for x in llist)
+
+
+def _filter(key, value, llist):
+ return filter(lambda x: x[key] == value, llist)
+
+
+class ScenarioTableResult(object):
+
+ def __init__(self, scenario, version, installer, iteration):
+ self.scenario = scenario
+ self.version = version
+ self.installer = installer
+ self.iteration = iteration
+
+ @coroutine
+ def get(self):
+ results = yield ResultCache.get(self.version)
+ results = self._filter_result(results)
+ results = self._struct_result(results)
+
+ raise Return(results)
+
+ def _filter_result(self, results):
+ results = [x for x in results if x['build_tag']]
+ if self.installer:
+ results = _filter('installer', self.installer, results)
+ if self.scenario:
+ results = _filter('scenario', self.scenario, results)
+ return results
+
+ def _struct_result(self, results):
+
+ return {
+ s: self._struct_scenario(_filter('scenario', s, results))
+ for s in _set('scenario', results)
+ }
+
+ def _struct_scenario(self, data):
+ return sorted([
+ HandlerFacade.get_result(b, _filter('build_tag', b, data))
+ for b in _set('build_tag', data)
+ ], key=lambda x: x['date'], reverse=True)[:self.iteration]
+
+
+class HandlerFacade(object):
+
+ @classmethod
+ def get_result(cls, index, data):
+ if not data:
+ return {}
+
+ cls._change_name_to_functest(data)
+ data = cls._sort_by_start_date(data)
+
+ return {
+ 'id': index,
+ 'date': data[0]['start_date'],
+ 'version': data[0]['version'],
+ 'installer': data[0]['installer'],
+ 'projects': cls._get_projects_data(data)
+ }
+
+ @classmethod
+ def _sort_by_start_date(cls, data):
+ return sorted(data, key=lambda x: x['start_date'])
+
+ @classmethod
+ def _get_projects_data(cls, data):
+
+ return {
+ p: HANDLER_MAP[p](_filter('project_name', p, data)).struct()
+ for p in _set('project_name', data)
+ }
+
+ @classmethod
+ def _change_name_to_functest(cls, data):
+ for ele in data:
+ if ele['project_name'] in PROJECTS:
+ ele['project_name'] = 'functest'
+
+
+@six.add_metaclass(abc.ABCMeta)
+class ProjectHandler(object):
+
+ def __init__(self, data):
+ self.data = data
+
+ def struct(self):
+ return self._struct_project_data()
+
+ def _struct_project_data(self):
+ return {
+ 'gating': self._gating()
+ }
+
+ @abc.abstractmethod
+ def _gating(self):
+ pass
+
+
+class DefaultHandler(ProjectHandler):
+
+ def _struct_project_data(self):
+ return {}
+
+ def _gating(self):
+ pass
+
+
+class FunctestHandler(ProjectHandler):
+
+ def _gating(self):
+ if all([x['criteria'] == 'PASS' for x in self.data]):
+ return 'PASS'
+ else:
+ return 'FAIL'
+
+
+HANDLER_MAP = defaultdict(lambda: DefaultHandler, functest=FunctestHandler)
diff --git a/reporting/api/urls.py b/reporting/api/urls.py
index a5228b2..34f61d8 100644
--- a/reporting/api/urls.py
+++ b/reporting/api/urls.py
@@ -9,6 +9,7 @@
from api.handlers import landing
from api.handlers import projects
from api.handlers import testcases
+from api.handlers import scenarios
mappings = [
(r"/landing-page/filters", landing.FiltersHandler),
@@ -16,5 +17,7 @@ mappings = [
(r"/projects-page/projects", projects.Projects),
(r"/projects/([^/]+)/cases", testcases.TestCases),
- (r"/projects/([^/]+)/cases/([^/]+)", testcases.TestCase)
+ (r"/projects/([^/]+)/cases/([^/]+)", testcases.TestCase),
+
+ (r"/scenarios/results", scenarios.Result)
]