Amphora API Failover call

Change-Id: I2830a79723ceafd7ebf6387c62feeae98878e8e1
This commit is contained in:
Adam Harwell 2017-12-05 14:36:01 -08:00
parent 8a7e13b95b
commit 23bf43a66f
7 changed files with 90 additions and 22 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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'}]
),
]

View File

@ -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',

View File

@ -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')

View File

@ -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/<uuid>/failover``. It will cause the amphora to be
recycled and replaced, in the same way as the health-triggered failover.