Updates for result_collection_api 77/2177/3
authorGuy Rodrigue Koffi <koffirodrigue@gmail.com>
Thu, 1 Oct 2015 08:56:55 +0000 (10:56 +0200)
committerGuy Rodrigue Koffi <koffirodrigue@gmail.com>
Thu, 8 Oct 2015 09:53:34 +0000 (11:53 +0200)
- pod identification : RELENG-33
- add filters on results : RELENG-32 / RELENG-31
- fix bugs on db connection

Change-Id: Ie720bb8028e5bfabd914c57df323b81d40c47fcd
Signed-off-by: Guy Rodrigue Koffi <koffirodrigue@gmail.com>
utils/test/result_collection_api/common/config.py
utils/test/result_collection_api/config.ini
utils/test/result_collection_api/resources/handlers.py
utils/test/result_collection_api/resources/models.py
utils/test/result_collection_api/result_collection_api.py

index a0d0757..a62e805 100644 (file)
@@ -47,6 +47,7 @@ class APIConfig:
     def __init__(self):
         self._default_config_location = "config.ini"
         self.mongo_url = None
+        self.mongo_dbname = None
         self.api_port = None
         self.api_debug_on = None
         self._parser = None
@@ -87,13 +88,18 @@ class APIConfig:
 
         # Linking attributes to keys from file with their sections
         obj.mongo_url = obj._get_parameter("mongo", "url")
+        obj.mongo_dbname = obj._get_parameter("mongo", "dbname")
+
         obj.api_port = obj._get_int_parameter("api", "port")
         obj.api_debug_on = obj._get_bool_parameter("api", "debug")
+
         return obj
 
     def __str__(self):
         return "mongo_url = %s \n" \
+               "mongo_dbname = %s \n" \
                "api_port = %s \n" \
                "api_debug_on = %s \n" % (self.mongo_url,
+                                         self.mongo_dbname,
                                          self.api_port,
                                          self.api_debug_on)
index e00b56c..f703cc6 100644 (file)
@@ -1,10 +1,12 @@
+# to add a new parameter in the config file,
+# the CONF object in config.ini must be updated
 [mongo]
 # URL of the mongo DB
 # Mongo auth url => mongodb://user1:pwd1@host1/?authSource=db1
-url = mongodb://127.0.0.1:27017/test_results_collection
-
+url = mongodb://127.0.0.1:27017/
+dbname = test_results_collection
 [api]
 # Listening port
-port = 80
+port = 8000
 # With debug_on set to true, error traces will be shown in HTTP responses
 debug = True
\ No newline at end of file
index 3faba5a..f8b26d0 100644 (file)
@@ -11,7 +11,7 @@ import json
 
 from tornado.web import RequestHandler, asynchronous, HTTPError
 from tornado import gen
-from datetime import datetime
+from datetime import datetime, timedelta
 
 from models import Pod, TestProject, TestCase, TestResult
 from common.constants import DEFAULT_REPRESENTATION, HTTP_BAD_REQUEST, \
@@ -60,6 +60,8 @@ class PodHandler(GenericApiHandler):
     """ Handle the requests about the POD Platforms
     HTTP Methdods :
         - GET : Get PODS
+        - POST : Create a pod
+        - DELETE : DELETE POD
     """
 
     def initialize(self):
@@ -68,19 +70,15 @@ class PodHandler(GenericApiHandler):
 
     @asynchronous
     @gen.coroutine
-    def get(self, pod_id=None):
+    def get(self, pod_name=None):
         """
         Get all pods or a single pod
         :param pod_id:
         """
-
-        if pod_id is None:
-            pod_id = ""
-
         get_request = dict()
 
-        if len(pod_id) > 0:
-            get_request["_id"] = int(pod_id)
+        if pod_name is not None:
+            get_request["name"] = pod_name
 
         res = []
         cursor = self.db.pod.find(get_request)
@@ -98,6 +96,69 @@ class PodHandler(GenericApiHandler):
 
         self.finish_request(answer)
 
+    @asynchronous
+    @gen.coroutine
+    def post(self):
+        """ Create a POD"""
+
+        if self.json_args is None:
+            raise HTTPError(HTTP_BAD_REQUEST)
+
+        query = {"name": self.json_args.get("name")}
+
+        # check for existing name in db
+        mongo_dict = yield self.db.pod.find_one(query)
+        if mongo_dict is not None:
+            raise HTTPError(HTTP_FORBIDDEN,
+                            "{} already exists as a pod".format(
+                                self.json_args.get("name")))
+
+        pod = Pod.pod_from_dict(self.json_args)
+        pod.creation_date = datetime.now()
+
+        future = self.db.pod.insert(pod.format())
+        result = yield future
+        pod._id = result
+
+        meta = dict()
+        meta["success"] = True
+        meta["uri"] = "/pods/{}".format(pod.name)
+
+        answer = dict()
+        answer["pod"] = pod.format_http()
+        answer["meta"] = meta
+
+        self.finish_request(answer)
+
+    @asynchronous
+    @gen.coroutine
+    def delete(self, pod_name):
+        """ Remove a POD
+
+        # check for an existing pod to be deleted
+        mongo_dict = yield self.db.pod.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.test_projects.remove(
+            {'name': pod_name})
+
+        meta = dict()
+        meta["success"] = True
+        meta["deletion-data"] = res
+
+        answer = dict()
+        answer["meta"] = meta
+
+        self.finish_request(answer)
+        """
+        pass
+
 
 class TestProjectHandler(GenericApiHandler):
     """
@@ -440,18 +501,26 @@ class TestResultsHandler(GenericApiHandler):
         Available filters for this request are :
          - project : project name
          - case : case name
-         - pod : pod ID
+         - pod : pod name
+         - version : platform version (Arno-R1, ...)
+         - installer (fuel, ...)
+         - period : x (x last days)
+
 
         :param result_id: Get a result by ID
         :raise HTTPError
 
-        GET /results/project=functest&case=keystone.catalog&pod=1
+        GET /results/project=functest&case=vPing&version=Arno-R1 \
+        &pod=pod_name&period=15
         => get results with optional filters
         """
 
         project_arg = self.get_query_argument("project", None)
         case_arg = self.get_query_argument("case", None)
         pod_arg = self.get_query_argument("pod", None)
+        version_arg = self.get_query_argument("version", None)
+        installer_arg = self.get_query_argument("installer", None)
+        period_arg = self.get_query_argument("period", None)
 
         # prepare request
         get_request = dict()
@@ -463,15 +532,34 @@ class TestResultsHandler(GenericApiHandler):
                 get_request["case_name"] = case_arg
 
             if pod_arg is not None:
-                get_request["pod_id"] = int(pod_arg)
+                get_request["pod_name"] = pod_arg
+
+            if version_arg is not None:
+                get_request["version"] = version_arg
+
+            if installer_arg is not None:
+                get_request["installer"] = installer_arg
+
+            if period_arg is not None:
+                try:
+                    period_arg = int(period_arg)
+                except:
+                    raise HTTPError(HTTP_BAD_REQUEST)
+
+                if period_arg > 0:
+                    period = datetime.now() - timedelta(days=period_arg)
+                    obj = {"$gte": period}
+                    get_request["creation_date"] = obj
         else:
             get_request["_id"] = result_id
 
+        print get_request
         res = []
         # fetching results
         cursor = self.db.test_results.find(get_request)
         while (yield cursor.fetch_next):
-            test_result = TestResult.test_result_from_dict(cursor.next_object())
+            test_result = TestResult.test_result_from_dict(
+                cursor.next_object())
             res.append(test_result.format_http())
 
         # building meta object
@@ -502,7 +590,9 @@ class TestResultsHandler(GenericApiHandler):
             raise HTTPError(HTTP_BAD_REQUEST)
         if self.json_args.get("case_name") is None:
             raise HTTPError(HTTP_BAD_REQUEST)
-        if self.json_args.get("pod_id") is None:
+        # check for pod_name instead of id,
+        # keeping id for current implementations
+        if self.json_args.get("pod_name") is None:
             raise HTTPError(HTTP_BAD_REQUEST)
 
         # TODO : replace checks with jsonschema
@@ -524,11 +614,11 @@ class TestResultsHandler(GenericApiHandler):
 
         # check for pod
         mongo_dict = yield self.db.pod.find_one(
-            {"_id": self.json_args.get("pod_id")})
+            {"name": self.json_args.get("pod_name")})
         if mongo_dict is None:
             raise HTTPError(HTTP_NOT_FOUND,
                             "Could not find POD [{}] "
-                            .format(self.json_args.get("pod_id")))
+                            .format(self.json_args.get("pod_name")))
 
         # convert payload to object
         test_result = TestResult.test_result_from_dict(self.json_args)
index 82c17fd..3b4d843 100644 (file)
@@ -22,13 +22,19 @@ class Pod:
 
         p = Pod()
         p._id = pod_dict.get('_id')
-        p.creation_date = pod_dict.get('creation_date')
+        p.creation_date = str(pod_dict.get('creation_date'))
         p.name = pod_dict.get('name')
         return p
 
     def format(self):
         return {
-            "_id": self._id,
+            "name": self.name,
+            "creation_date": str(self.creation_date),
+        }
+
+    def format_http(self):
+        return {
+            "_id": str(self._id),
             "name": self.name,
             "creation_date": str(self.creation_date),
         }
@@ -82,6 +88,7 @@ class TestCase:
         self.name = None
         self.project_name = None
         self.description = None
+        self.url = None
         self.creation_date = None
 
     @staticmethod
@@ -96,6 +103,7 @@ class TestCase:
         t.creation_date = testcase_dict.get('creation_date')
         t.name = testcase_dict.get('name')
         t.description = testcase_dict.get('description')
+        t.url = testcase_dict.get('url')
 
         return t
 
@@ -104,7 +112,8 @@ class TestCase:
             "name": self.name,
             "description": self.description,
             "project_name": self.project_name,
-            "creation_date": str(self.creation_date)
+            "creation_date": str(self.creation_date),
+            "url": self.url
         }
 
     def format_http(self, test_project=None):
@@ -113,6 +122,7 @@ class TestCase:
             "name": self.name,
             "description": self.description,
             "creation_date": str(self.creation_date),
+            "url": self.url,
         }
         if test_project is not None:
             res["test_project"] = test_project
@@ -127,9 +137,9 @@ class TestResult:
         self._id = None
         self.case_name = None
         self.project_name = None
-        self.pod_id = None
+        self.pod_name = None
         self.installer = None
-        self.platform_version = None
+        self.version = None
         self.description = None
         self.creation_date = None
         self.details = None
@@ -143,11 +153,13 @@ class TestResult:
         t = TestResult()
         t._id = test_result_dict.get('_id')
         t.case_name = test_result_dict.get('case_name')
+        t.pod_name = test_result_dict.get('pod_name')
         t.project_name = test_result_dict.get('project_name')
-        t.pod_id = test_result_dict.get('pod_id')
         t.description = test_result_dict.get('description')
         t.creation_date = str(test_result_dict.get('creation_date'))
         t.details = test_result_dict.get('details')
+        t.version = test_result_dict.get('version')
+        t.installer = test_result_dict.get('installer')
 
         return t
 
@@ -155,9 +167,11 @@ class TestResult:
         return {
             "case_name": self.case_name,
             "project_name": self.project_name,
-            "pod_id": self.pod_id,
+            "pod_name": self.pod_name,
             "description": self.description,
             "creation_date": str(self.creation_date),
+            "version": self.version,
+            "installer": self.installer,
             "details": self.details,
         }
 
@@ -166,8 +180,10 @@ class TestResult:
             "_id": str(self._id),
             "case_name": self.case_name,
             "project_name": self.project_name,
-            "pod_id": self.pod_id,
+            "pod_name": self.pod_name,
             "description": self.description,
             "creation_date": str(self.creation_date),
+            "version": self.version,
+            "installer": self.installer,
             "details": self.details,
         }
index c04e034..4969577 100644 (file)
@@ -15,14 +15,12 @@ Pre-requisites:
 We can launch the API with this file
 
 TODOs :
-  - use POD name instead of id
   - logging
   - json args validation with schemes
   - POST/PUT/DELETE for PODs
   - POST/PUT/GET/DELETE for installers, platforms (enrich results info)
   - count cases for GET on test_projects
   - count results for GET on cases
-  - provide filtering on requests
   - include objects
   - swagger documentation
   - setup file
@@ -48,7 +46,8 @@ args = parser.parse_args()
 CONF = APIConfig().parse(args.config_file)
 
 # connecting to MongoDB server, and choosing database
-db = motor.MotorClient(CONF.mongo_url)
+client = motor.MotorClient(CONF.mongo_url)
+db = client[CONF.mongo_dbname]
 
 
 def make_app():
@@ -61,7 +60,7 @@ def make_app():
             # GET /pods => Get all pods
             # GET /pods/1 => Get details on POD 1
             (r"/pods", PodHandler),
-            (r"/pods/(\d*)", PodHandler),
+            (r"/pods/([^/]+)", PodHandler),
 
             # few examples:
             # GET /test_projects