From 23bf43a66ff8225e7d68f087681b38ceceda9663 Mon Sep 17 00:00:00 2001 From: Adam Harwell Date: Tue, 5 Dec 2017 14:36:01 -0800 Subject: [PATCH] Amphora API Failover call Change-Id: I2830a79723ceafd7ebf6387c62feeae98878e8e1 --- octavia/api/v2/controllers/amphora.py | 64 +++++++++++++++---- octavia/api/v2/controllers/base.py | 5 ++ .../controller/worker/controller_worker.py | 15 ++--- octavia/policies/amphora.py | 8 +++ octavia/tests/functional/api/v2/base.py | 1 + .../tests/functional/api/v2/test_amphora.py | 12 ++++ ...Amphora-Failover-API-612090f761936254.yaml | 7 ++ 7 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 releasenotes/notes/Amphora-Failover-API-612090f761936254.yaml diff --git a/octavia/api/v2/controllers/amphora.py b/octavia/api/v2/controllers/amphora.py index 4128842835..f247fe0a51 100644 --- a/octavia/api/v2/controllers/amphora.py +++ b/octavia/api/v2/controllers/amphora.py @@ -16,6 +16,7 @@ import logging from oslo_config import cfg +from oslo_utils import excutils import pecan from wsme import types as wtypes from wsmeext import pecan as wsme_pecan @@ -23,8 +24,6 @@ from wsmeext import pecan as wsme_pecan from octavia.api.v2.controllers import base from octavia.api.v2.types import amphora as amp_types from octavia.common import constants -from octavia.common import data_models -from octavia.common import exceptions CONF = cfg.CONF @@ -34,16 +33,9 @@ LOG = logging.getLogger(__name__) class AmphoraController(base.BaseController): RBAC_TYPE = constants.RBAC_AMPHORA - def _get_db_amp(self, session, amp_id): - """Gets the current amphora object from the database.""" - db_amp = self.repositories.amphora.get( - session, id=amp_id) - if not db_amp: - LOG.info("Amphora %s was not found", amp_id) - raise exceptions.NotFound( - resource=data_models.Amphora._name(), - id=amp_id) - return db_amp + def __init__(self): + super(AmphoraController, self).__init__() + self.handler = self.handler.amphora @wsme_pecan.wsexpose(amp_types.AmphoraRootResponse, wtypes.text, wtypes.text) @@ -52,7 +44,7 @@ class AmphoraController(base.BaseController): context = pecan.request.context.get('octavia_context') db_amp = self._get_db_amp(context.session, id) - self._auth_validate_action(context, db_amp.load_balancer.project_id, + self._auth_validate_action(context, context.project_id, constants.RBAC_GET_ONE) result = self._convert_db_to_type( @@ -78,3 +70,49 @@ class AmphoraController(base.BaseController): result = self._filter_fields(result, fields) return amp_types.AmphoraeRootResponse( amphorae=result, amphorae_links=links) + + @pecan.expose() + def _lookup(self, amphora_id, *remainder): + """Overridden pecan _lookup method for custom routing. + + Currently it checks if this was a failover request and routes + the request to the FailoverController. + """ + if amphora_id and len(remainder): + controller = remainder[0] + remainder = remainder[1:] + if controller == 'failover': + return FailoverController(amp_id=amphora_id), remainder + + +class FailoverController(base.BaseController): + RBAC_TYPE = constants.RBAC_AMPHORA + + def __init__(self, amp_id): + super(FailoverController, self).__init__() + self.handler = self.handler.amphora + self.amp_id = amp_id + + @wsme_pecan.wsexpose(None, wtypes.text, status_code=202) + def put(self): + """Fails over an amphora""" + pcontext = pecan.request.context + context = pcontext.get('octavia_context') + db_amp = self._get_db_amp(context.session, self.amp_id) + + self._auth_validate_action( + context, db_amp.load_balancer.project_id, + constants.RBAC_PUT_FAILOVER) + + self.repositories.load_balancer.test_and_set_provisioning_status( + context.session, db_amp.load_balancer_id, + status=constants.PENDING_UPDATE, raise_exception=True) + try: + LOG.info("Sending failover request for amphora %s to the handler", + self.amp_id) + self.handler.failover(db_amp) + except Exception: + with excutils.save_and_reraise_exception(reraise=False): + self.repositories.load_balancer.update( + context.session, db_amp.load_balancer.id, + provisioning_status=constants.ERROR) diff --git a/octavia/api/v2/controllers/base.py b/octavia/api/v2/controllers/base.py index 081d73017b..217bd4d699 100644 --- a/octavia/api/v2/controllers/base.py +++ b/octavia/api/v2/controllers/base.py @@ -99,6 +99,11 @@ class BaseController(rest.RestController): return self._get_db_obj(session, self.repositories.l7rule, data_models.L7Rule, id) + def _get_db_amp(self, session, id): + """Gets an Amphora from the database.""" + return self._get_db_obj(session, self.repositories.amphora, + data_models.Amphora, id) + def _get_lb_project_id(self, session, id): """Get the project_id of the load balancer from the database.""" lb = self._get_db_obj(session, self.repositories.load_balancer, diff --git a/octavia/controller/worker/controller_worker.py b/octavia/controller/worker/controller_worker.py index 9905dce3b9..aa62d7d4f1 100644 --- a/octavia/controller/worker/controller_worker.py +++ b/octavia/controller/worker/controller_worker.py @@ -692,17 +692,14 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine): try: amp = self._amphora_repo.get(db_apis.get_session(), id=amphora_id) - if amp.status == constants.AMPHORA_ALLOCATED: - self._lb_repo.test_and_set_provisioning_status( - db_apis.get_session(), amp.load_balancer_id, - status=constants.PENDING_UPDATE, raise_exception=True) self._perform_amphora_failover( amp, constants.LB_CREATE_FAILOVER_PRIORITY) - LOG.info("Mark ACTIVE in DB for load balancer id: %s", - amp.load_balancer_id) - self._lb_repo.update( - db_apis.get_session(), amp.load_balancer_id, - provisioning_status=constants.ACTIVE) + if amp.load_balancer_id: + LOG.info("Mark ACTIVE in DB for load balancer id: %s", + amp.load_balancer_id) + self._lb_repo.update( + db_apis.get_session(), amp.load_balancer_id, + provisioning_status=constants.ACTIVE) except Exception as e: with excutils.save_and_reraise_exception(): LOG.error("Failover exception: %s", e) diff --git a/octavia/policies/amphora.py b/octavia/policies/amphora.py index 120dcd095f..eada69b521 100644 --- a/octavia/policies/amphora.py +++ b/octavia/policies/amphora.py @@ -29,6 +29,14 @@ rules = [ "Show Amphora details", [{'method': 'GET', 'path': '/v2.0/octavia/amphorae/{amphora_id}'}] ), + policy.DocumentedRuleDefault( + '{rbac_obj}{action}'.format(rbac_obj=constants.RBAC_AMPHORA, + action=constants.RBAC_PUT_FAILOVER), + constants.RULE_API_ADMIN, + "Failover Amphora", + [{'method': 'PUT', + 'path': '/v2.0/octavia/amphorae/{amphora_id}/failover'}] + ), ] diff --git a/octavia/tests/functional/api/v2/base.py b/octavia/tests/functional/api/v2/base.py index ecbab5c869..a5f3d296e8 100644 --- a/octavia/tests/functional/api/v2/base.py +++ b/octavia/tests/functional/api/v2/base.py @@ -65,6 +65,7 @@ class BaseAPITest(base_db_test.OctaviaDBTestBase): AMPHORAE_PATH = '/octavia/amphorae' AMPHORA_PATH = AMPHORAE_PATH + '/{amphora_id}' + AMPHORA_FAILOVER_PATH = AMPHORA_PATH + '/failover' NOT_AUTHORIZED_BODY = { 'debuginfo': None, 'faultcode': 'Client', diff --git a/octavia/tests/functional/api/v2/test_amphora.py b/octavia/tests/functional/api/v2/test_amphora.py index d16f08e68f..353bec2e25 100644 --- a/octavia/tests/functional/api/v2/test_amphora.py +++ b/octavia/tests/functional/api/v2/test_amphora.py @@ -89,6 +89,18 @@ class TestAmphora(base.BaseAPITest): amphora_id=self.amp_id)).json.get(self.root_tag) self._assert_amp_equal(self.amp_args, response) + def test_failover(self): + self.put(self.AMPHORA_FAILOVER_PATH.format( + amphora_id=self.amp_id), body={}, status=202) + self.handler_mock().amphora.failover.assert_has_calls( + [mock.call(self.amp)] + ) + + def test_failover_bad_amp_id(self): + self.put(self.AMPHORA_FAILOVER_PATH.format( + amphora_id='asdf'), body={}, status=404) + self.assertFalse(self.handler_mock().amphora.failover.called) + def test_get_authorized(self): self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF)) auth_strategy = self.conf.conf.api_settings.get('auth_strategy') diff --git a/releasenotes/notes/Amphora-Failover-API-612090f761936254.yaml b/releasenotes/notes/Amphora-Failover-API-612090f761936254.yaml new file mode 100644 index 0000000000..2fe3821e4c --- /dev/null +++ b/releasenotes/notes/Amphora-Failover-API-612090f761936254.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Added the 'failover' sub-resource for the Amphora API. Each amphora can be + triggered to failover by sending a PUT (with an empty body) to the resource + ``/v2.0/octavia/amphorae//failover``. It will cause the amphora to be + recycled and replaced, in the same way as the health-triggered failover.