Amphora API Failover call
Change-Id: I2830a79723ceafd7ebf6387c62feeae98878e8e1
This commit is contained in:
parent
8a7e13b95b
commit
23bf43a66f
@ -16,6 +16,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_utils import excutils
|
||||||
import pecan
|
import pecan
|
||||||
from wsme import types as wtypes
|
from wsme import types as wtypes
|
||||||
from wsmeext import pecan as wsme_pecan
|
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.controllers import base
|
||||||
from octavia.api.v2.types import amphora as amp_types
|
from octavia.api.v2.types import amphora as amp_types
|
||||||
from octavia.common import constants
|
from octavia.common import constants
|
||||||
from octavia.common import data_models
|
|
||||||
from octavia.common import exceptions
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -34,16 +33,9 @@ LOG = logging.getLogger(__name__)
|
|||||||
class AmphoraController(base.BaseController):
|
class AmphoraController(base.BaseController):
|
||||||
RBAC_TYPE = constants.RBAC_AMPHORA
|
RBAC_TYPE = constants.RBAC_AMPHORA
|
||||||
|
|
||||||
def _get_db_amp(self, session, amp_id):
|
def __init__(self):
|
||||||
"""Gets the current amphora object from the database."""
|
super(AmphoraController, self).__init__()
|
||||||
db_amp = self.repositories.amphora.get(
|
self.handler = self.handler.amphora
|
||||||
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
|
|
||||||
|
|
||||||
@wsme_pecan.wsexpose(amp_types.AmphoraRootResponse, wtypes.text,
|
@wsme_pecan.wsexpose(amp_types.AmphoraRootResponse, wtypes.text,
|
||||||
wtypes.text)
|
wtypes.text)
|
||||||
@ -52,7 +44,7 @@ class AmphoraController(base.BaseController):
|
|||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
db_amp = self._get_db_amp(context.session, id)
|
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)
|
constants.RBAC_GET_ONE)
|
||||||
|
|
||||||
result = self._convert_db_to_type(
|
result = self._convert_db_to_type(
|
||||||
@ -78,3 +70,49 @@ class AmphoraController(base.BaseController):
|
|||||||
result = self._filter_fields(result, fields)
|
result = self._filter_fields(result, fields)
|
||||||
return amp_types.AmphoraeRootResponse(
|
return amp_types.AmphoraeRootResponse(
|
||||||
amphorae=result, amphorae_links=links)
|
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,
|
return self._get_db_obj(session, self.repositories.l7rule,
|
||||||
data_models.L7Rule, id)
|
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):
|
def _get_lb_project_id(self, session, id):
|
||||||
"""Get the project_id of the load balancer from the database."""
|
"""Get the project_id of the load balancer from the database."""
|
||||||
lb = self._get_db_obj(session, self.repositories.load_balancer,
|
lb = self._get_db_obj(session, self.repositories.load_balancer,
|
||||||
|
@ -692,17 +692,14 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
|
|||||||
try:
|
try:
|
||||||
amp = self._amphora_repo.get(db_apis.get_session(),
|
amp = self._amphora_repo.get(db_apis.get_session(),
|
||||||
id=amphora_id)
|
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(
|
self._perform_amphora_failover(
|
||||||
amp, constants.LB_CREATE_FAILOVER_PRIORITY)
|
amp, constants.LB_CREATE_FAILOVER_PRIORITY)
|
||||||
LOG.info("Mark ACTIVE in DB for load balancer id: %s",
|
if amp.load_balancer_id:
|
||||||
amp.load_balancer_id)
|
LOG.info("Mark ACTIVE in DB for load balancer id: %s",
|
||||||
self._lb_repo.update(
|
amp.load_balancer_id)
|
||||||
db_apis.get_session(), amp.load_balancer_id,
|
self._lb_repo.update(
|
||||||
provisioning_status=constants.ACTIVE)
|
db_apis.get_session(), amp.load_balancer_id,
|
||||||
|
provisioning_status=constants.ACTIVE)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.error("Failover exception: %s", e)
|
LOG.error("Failover exception: %s", e)
|
||||||
|
@ -29,6 +29,14 @@ rules = [
|
|||||||
"Show Amphora details",
|
"Show Amphora details",
|
||||||
[{'method': 'GET', 'path': '/v2.0/octavia/amphorae/{amphora_id}'}]
|
[{'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'
|
AMPHORAE_PATH = '/octavia/amphorae'
|
||||||
AMPHORA_PATH = AMPHORAE_PATH + '/{amphora_id}'
|
AMPHORA_PATH = AMPHORAE_PATH + '/{amphora_id}'
|
||||||
|
AMPHORA_FAILOVER_PATH = AMPHORA_PATH + '/failover'
|
||||||
|
|
||||||
NOT_AUTHORIZED_BODY = {
|
NOT_AUTHORIZED_BODY = {
|
||||||
'debuginfo': None, 'faultcode': 'Client',
|
'debuginfo': None, 'faultcode': 'Client',
|
||||||
|
@ -89,6 +89,18 @@ class TestAmphora(base.BaseAPITest):
|
|||||||
amphora_id=self.amp_id)).json.get(self.root_tag)
|
amphora_id=self.amp_id)).json.get(self.root_tag)
|
||||||
self._assert_amp_equal(self.amp_args, response)
|
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):
|
def test_get_authorized(self):
|
||||||
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||||
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
|
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…
x
Reference in New Issue
Block a user