From: Lott, Christopher (cl778h) Date: Thu, 30 Apr 2020 13:38:35 +0000 (-0400) Subject: Revise custom exceptions to require a message X-Git-Tag: 2.1.8~1 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=6803b12d1c2af87c57d7dccaced70b49bcb44815;p=ric-plt%2Fa1.git Revise custom exceptions to require a message 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) Change-Id: I2c020f41d65b7ddadd71185fafb16cf2c4557a88 --- diff --git a/Dockerfile-Unit-Test b/Dockerfile-Unit-Test index a9ea22a..ae04de4 100644 --- a/Dockerfile-Unit-Test +++ b/Dockerfile-Unit-Test @@ -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 diff --git a/a1/controller.py b/a1/controller.py index 23ef804..289cf9a 100644 --- a/a1/controller.py +++ b/a1/controller.py @@ -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 diff --git a/a1/data.py b/a1/data.py index cd44962..a9301d8 100644 --- a/a1/data.py +++ b/a1/data.py @@ -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): diff --git a/a1/exceptions.py b/a1/exceptions.py index c76e9f2..b7d1244 100644 --- a/a1/exceptions.py +++ b/a1/exceptions.py @@ -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""" diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 8febae6..791e912 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -14,16 +14,17 @@ and this project adheres to `Semantic Versioning `__. :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 `_) +* Respond with error if policy type ID differs from ID in object on create [2.1.7] - 2020-04-28 diff --git a/tests/test_controller.py b/tests/test_controller.py index 2aac5a9..f243bf7 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -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