Amphora API Failover call
Change-Id: I2830a79723ceafd7ebf6387c62feeae98878e8e1
This commit is contained in:
parent
8a7e13b95b
commit
23bf43a66f
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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'}]
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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')
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user