Revise custom exceptions to require a message 43/3543/2
authorLott, Christopher (cl778h) <cl778h@att.com>
Thu, 30 Apr 2020 13:38:35 +0000 (09:38 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Thu, 30 Apr 2020 13:49:15 +0000 (09:49 -0400)
Reduce code redundancy in controller exception handling.
Raise exception on ID mismatch in create policy type request.
Add test of ID mismatch case.

Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
Change-Id: I2c020f41d65b7ddadd71185fafb16cf2c4557a88

Dockerfile-Unit-Test
a1/controller.py
a1/data.py
a1/exceptions.py
docs/release-notes.rst
tests/test_controller.py

index a9ea22a..ae04de4 100644 (file)
@@ -26,9 +26,9 @@ RUN pip install --upgrade pip && pip install tox gevent
 COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.2 /usr/local/lib64/librmr* /usr/local/lib64/
 
 # copies
+COPY setup.py tox.ini /tmp/
 COPY a1/ /tmp/a1
 COPY tests/ /tmp/tests
-COPY setup.py tox.ini /tmp/
 WORKDIR /tmp
 
 # Run the unit tests but skip doc
index 23ef804..289cf9a 100644 (file)
@@ -28,20 +28,25 @@ from a1 import a1rmr, exceptions, data
 mdc_logger = Logger(name=__name__)
 
 
+def _log_build_http_resp(exception, http_resp_code):
+    """
+    helper method that logs the exception and returns a tuple of (str, int) as a http response
+    """
+    msg = repr(exception)
+    mdc_logger.warning("Request failed, returning {0}: {1}".format(http_resp_code, msg))
+    return msg, http_resp_code
+
+
 def _try_func_return(func):
     """
     helper method that runs the function and returns a detailed http response if an exception is raised.
     """
     try:
         return func()
-    except (ValidationError, exceptions.PolicyTypeAlreadyExists, exceptions.CantDeleteNonEmptyType) as exc:
-        msg = repr(exc)
-        mdc_logger.warning("Request failed, returning 400: {0}".format(msg))
-        return msg, 400
+    except (ValidationError, exceptions.PolicyTypeAlreadyExists, exceptions.PolicyTypeIdMismatch, exceptions.CantDeleteNonEmptyType) as exc:
+        return _log_build_http_resp(exc, 400)
     except (exceptions.PolicyTypeNotFound, exceptions.PolicyInstanceNotFound) as exc:
-        msg = repr(exc)
-        mdc_logger.warning("Request failed, returning 404: {0}".format(msg))
-        return msg, 404
+        return _log_build_http_resp(exc, 404)
     except (RejectedByBackend, NotConnected, BackendError) as exc:
         """
         These are SDL errors. At the time of development here, we do not have a good understanding
@@ -52,10 +57,7 @@ def _try_func_return(func):
         For now, we log, and 503, and investigate the logs later to improve the handling/reporting.
         """
         # mdc_logger.exception(exc)  # waiting for https://jira.o-ran-sc.org/browse/RIC-39
-        msg = repr(exc)
-        mdc_logger.warning("Request failed, returning 503: {0}".format(msg))
-        return msg, 503
-
+        return _log_build_http_resp(exc, 503)
     # let other types of unexpected exceptions blow up and log
 
 
index cd44962..a9301d8 100644 (file)
@@ -23,7 +23,7 @@ import time
 from threading import Thread
 from mdclogpy import Logger
 from ricxappframe.xapp_sdl import SDLWrapper
-from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound, PolicyTypeAlreadyExists, CantDeleteNonEmptyType
+from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound, PolicyTypeAlreadyExists, PolicyTypeIdMismatch, CantDeleteNonEmptyType
 
 # constants
 INSTANCE_DELETE_NO_RESP_TTL = int(os.environ.get("INSTANCE_DELETE_NO_RESP_TTL", 5))
@@ -84,7 +84,7 @@ def _type_is_valid(policy_type_id):
     check that a type is valid
     """
     if SDL.get(A1NS, _generate_type_key(policy_type_id)) is None:
-        raise PolicyTypeNotFound()
+        raise PolicyTypeNotFound(policy_type_id)
 
 
 def _instance_is_valid(policy_type_id, policy_instance_id):
@@ -93,7 +93,7 @@ def _instance_is_valid(policy_type_id, policy_instance_id):
     """
     _type_is_valid(policy_type_id)
     if SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) is None:
-        raise PolicyInstanceNotFound
+        raise PolicyInstanceNotFound(policy_type_id)
 
 
 def _get_statuses(policy_type_id, policy_instance_id):
@@ -166,11 +166,11 @@ def store_policy_type(policy_type_id, body):
     """
     store a policy type if it doesn't already exist
     """
+    if policy_type_id != body['policy_type_id']:
+        raise PolicyTypeIdMismatch("{0} vs. {1}".format(policy_type_id, body['policy_type_id']))
     key = _generate_type_key(policy_type_id)
     if SDL.get(A1NS, key) is not None:
-        raise PolicyTypeAlreadyExists()
-    # overwrite ID in body to enforce consistency
-    body['policy_type_id'] = policy_type_id
+        raise PolicyTypeAlreadyExists(policy_type_id)
     SDL.set(A1NS, key, body)
 
 
@@ -182,7 +182,7 @@ def delete_policy_type(policy_type_id):
     if pil == []:  # empty, can delete
         SDL.delete(A1NS, _generate_type_key(policy_type_id))
     else:
-        raise CantDeleteNonEmptyType()
+        raise CantDeleteNonEmptyType(policy_type_id)
 
 
 def get_policy_type(policy_type_id):
index c76e9f2..b7d1244 100644 (file)
@@ -19,17 +19,29 @@ Custom Exceptions
 """
 
 
-class CantDeleteNonEmptyType(BaseException):
+class A1Error(Exception):
+    """A base class for A1 exceptions."""
+
+    def __init__(self, message):
+        # Call the base class constructor with the parameters it needs
+        super(A1Error, self).__init__(message)
+
+
+class CantDeleteNonEmptyType(A1Error):
     """tried to delete a type that isn't empty"""
 
 
-class PolicyInstanceNotFound(BaseException):
+class PolicyInstanceNotFound(A1Error):
     """a policy instance cannot be found"""
 
 
-class PolicyTypeNotFound(BaseException):
+class PolicyTypeNotFound(A1Error):
     """a policy type instance cannot be found"""
 
 
-class PolicyTypeAlreadyExists(BaseException):
+class PolicyTypeAlreadyExists(A1Error):
     """a policy type already exists and replace not supported at this time"""
+
+
+class PolicyTypeIdMismatch(A1Error):
+    """a policy type request path ID differs from its body ID"""
index 8febae6..791e912 100644 (file)
@@ -14,16 +14,17 @@ and this project adheres to `Semantic Versioning <http://semver.org/>`__.
    :depth: 3
    :local:
 
-[2.1.8] - 2020-04-29
+[2.1.8] - 2020-04-30
 --------------------
 
 * Revise Dockerfile to set user as owner of .local dir with a1 package
-* Rename console shell start script to run-a1 from run.py 
+* Rename console shell start script to run-a1 from run.py
 * Extend start script to report webserver listening port
 * Add tiny RMR routing table for use in demo and test
 * Extend documentation for running a container locally
 * Add documentation of start/init parameters to _RmrLoop class
 * Add new environment variable USE_FAKE_SDL (`RIC-351 <https://jira.o-ran-sc.org/browse/RIC-351>`_)
+* Respond with error if policy type ID differs from ID in object on create
 
 
 [2.1.7] - 2020-04-28
index 2aac5a9..f243bf7 100644 (file)
@@ -363,20 +363,31 @@ def test_bad_instances(client, monkeypatch, adm_type_good):
 
     monkeypatch.setattr("a1.data.SDL.set", monkey_set)
 
-    res = client.put("/a1-p/policytypes/111", json=adm_type_good)
-    assert res.status_code == 503
-    res = client.put("/a1-p/policytypes/112", json=adm_type_good)
-    assert res.status_code == 503
-    res = client.put("/a1-p/policytypes/113", json=adm_type_good)
-    assert res.status_code == 503
+    def create_alt_id(json, id):
+        """
+        Overwrites the json's policy type ID, attempts create and tests for 503
+        """
+        json['policy_type_id'] = id
+        url = "/a1-p/policytypes/{0}".format(id)
+        res = client.put(url, json=json)
+        assert res.status_code == 503
+
+    create_alt_id(adm_type_good, 111)
+    create_alt_id(adm_type_good, 112)
+    create_alt_id(adm_type_good, 113)
 
 
 def test_illegal_types(client, adm_type_good):
     """
     Test illegal types
     """
+    # below valid range
     res = client.put("/a1-p/policytypes/0", json=adm_type_good)
     assert res.status_code == 400
+    # ID mismatch
+    res = client.put("/a1-p/policytypes/1", json=adm_type_good)
+    assert res.status_code == 400
+    # above valid range
     res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good)
     assert res.status_code == 400