Add status tree to V2 API
This patch implements status tree to the Octavia API. Change-Id: I92a5bb7d1814c79e7d03c75916b5324f1497f2e4 Co-Authored-By: German Eichberger <German.eichberger@rackspace.com> Co-Authored-By: Michael Johnson <johnsomor@gmail.com>
This commit is contained in:
parent
7466016ae9
commit
ee08aaff56
|
@ -393,3 +393,40 @@ class LoadBalancersController(base.BaseController):
|
|||
self.repositories.load_balancer.update(
|
||||
context.session, id,
|
||||
provisioning_status=constants.ERROR)
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, id, *remainder):
|
||||
"""Overridden pecan _lookup method for custom routing.
|
||||
|
||||
Currently it checks if this was a statuses request and routes
|
||||
the request to the StatusesController.
|
||||
"""
|
||||
if id and len(remainder) and remainder[0] == 'statuses':
|
||||
return StatusesController(lb_id=id), remainder[1:]
|
||||
|
||||
|
||||
class StatusesController(base.BaseController):
|
||||
RBAC_TYPE = constants.RBAC_LOADBALANCER
|
||||
|
||||
def __init__(self, lb_id):
|
||||
super(StatusesController, self).__init__()
|
||||
self.id = lb_id
|
||||
|
||||
@wsme_pecan.wsexpose(lb_types.StatusesRootResponse, wtypes.text,
|
||||
status_code=200)
|
||||
def get(self):
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
load_balancer = self._get_db_lb(context.session, self.id)
|
||||
if not load_balancer:
|
||||
LOG.info("Load balancer %s not found.", id)
|
||||
raise exceptions.NotFound(
|
||||
resource=data_models.LoadBalancer._name(),
|
||||
id=id)
|
||||
|
||||
self._auth_validate_action(context, load_balancer.project_id,
|
||||
constants.RBAC_GET_STATUS)
|
||||
|
||||
result = self._convert_db_to_type(
|
||||
load_balancer, lb_types.LoadBalancerStatusesResponse)
|
||||
result = lb_types.StatusesResponse(loadbalancer=result)
|
||||
return lb_types.StatusesRootResponse(statuses=result)
|
||||
|
|
|
@ -155,3 +155,12 @@ class HealthMonitorSingleCreate(BaseHealthMonitorType):
|
|||
wtypes.StringType(pattern=r'^(\d{3}(\s*,\s*\d{3})*)$|^(\d{3}-\d{3})$'),
|
||||
default=constants.HEALTH_MONITOR_DEFAULT_EXPECTED_CODES)
|
||||
admin_state_up = wtypes.wsattr(bool, default=True)
|
||||
|
||||
|
||||
class HealthMonitorStatusesResponse(BaseHealthMonitorType):
|
||||
"""Defines which attributes are to be shown on statuses response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
type = wtypes.wsattr(wtypes.text)
|
||||
provisioning_status = wtypes.wsattr(wtypes.StringType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
|
|
|
@ -154,3 +154,26 @@ class ListenerSingleCreate(BaseListenerType):
|
|||
l7policies = wtypes.wsattr([l7policy.L7PolicySingleCreate], default=[])
|
||||
insert_headers = wtypes.wsattr(
|
||||
wtypes.DictType(str, wtypes.StringType(max_length=255)))
|
||||
|
||||
|
||||
class ListenerStatusesResponse(BaseListenerType):
|
||||
"""Defines which attributes are to be shown on statuses response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
provisioning_status = wtypes.wsattr(wtypes.StringType())
|
||||
pools = wtypes.wsattr([pool.PoolStatusesResponse])
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
listener = super(ListenerStatusesResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
|
||||
pool_model = pool.PoolStatusesResponse
|
||||
listener.pools = [
|
||||
pool_model.from_data_model(i) for i in data_model.pools]
|
||||
|
||||
if not listener.name:
|
||||
listener.name = ""
|
||||
|
||||
return listener
|
||||
|
|
|
@ -139,3 +139,32 @@ class LoadBalancerPUT(BaseLoadBalancerType):
|
|||
|
||||
class LoadBalancerRootPUT(types.BaseType):
|
||||
loadbalancer = wtypes.wsattr(LoadBalancerPUT)
|
||||
|
||||
|
||||
class LoadBalancerStatusesResponse(BaseLoadBalancerType):
|
||||
"""Defines which attributes are to be shown on statuses response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
provisioning_status = wtypes.wsattr(wtypes.StringType())
|
||||
listeners = wtypes.wsattr([listener.ListenerStatusesResponse])
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
result = super(LoadBalancerStatusesResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
listener_model = listener.ListenerStatusesResponse
|
||||
result.listeners = [
|
||||
listener_model.from_data_model(i) for i in data_model.listeners]
|
||||
if not result.name:
|
||||
result.name = ""
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class StatusesResponse(wtypes.Base):
|
||||
loadbalancer = wtypes.wsattr(LoadBalancerStatusesResponse)
|
||||
|
||||
|
||||
class StatusesRootResponse(types.BaseType):
|
||||
statuses = wtypes.wsattr(StatusesResponse)
|
||||
|
|
|
@ -112,3 +112,23 @@ class MemberSingleCreate(BaseMemberType):
|
|||
weight = wtypes.wsattr(wtypes.IntegerType(
|
||||
minimum=constants.MIN_WEIGHT, maximum=constants.MAX_WEIGHT), default=1)
|
||||
subnet_id = wtypes.wsattr(wtypes.UuidType())
|
||||
|
||||
|
||||
class MemberStatusesResponse(BaseMemberType):
|
||||
"""Defines which attributes are to be shown on statuses response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
provisioning_status = wtypes.wsattr(wtypes.StringType())
|
||||
address = wtypes.wsattr(types.IPAddressType())
|
||||
protocol_port = wtypes.wsattr(wtypes.IntegerType())
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
member = super(MemberStatusesResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
|
||||
if not member.name:
|
||||
member.name = ""
|
||||
|
||||
return member
|
||||
|
|
|
@ -164,3 +164,29 @@ class PoolSingleCreate(BasePoolType):
|
|||
session_persistence = wtypes.wsattr(SessionPersistencePOST)
|
||||
healthmonitor = wtypes.wsattr(health_monitor.HealthMonitorSingleCreate)
|
||||
members = wtypes.wsattr([member.MemberSingleCreate])
|
||||
|
||||
|
||||
class PoolStatusesResponse(BasePoolType):
|
||||
"""Defines which attributes are to be shown on statuses response."""
|
||||
id = wtypes.wsattr(wtypes.UuidType())
|
||||
name = wtypes.wsattr(wtypes.StringType())
|
||||
provisioning_status = wtypes.wsattr(wtypes.StringType())
|
||||
operating_status = wtypes.wsattr(wtypes.StringType())
|
||||
health_monitor = wtypes.wsattr(
|
||||
health_monitor.HealthMonitorStatusesResponse)
|
||||
members = wtypes.wsattr([member.MemberStatusesResponse])
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
pool = super(PoolStatusesResponse, cls).from_data_model(
|
||||
data_model, children=children)
|
||||
|
||||
member_model = member.MemberStatusesResponse
|
||||
if data_model.health_monitor:
|
||||
pool.health_monitor = (
|
||||
health_monitor.HealthMonitorStatusesResponse.from_data_model(
|
||||
data_model.health_monitor))
|
||||
pool.members = [
|
||||
member_model.from_data_model(i) for i in data_model.members]
|
||||
|
||||
return pool
|
||||
|
|
|
@ -1825,3 +1825,304 @@ class TestLoadBalancerGraph(base.BaseAPITest):
|
|||
body, _ = self._test_with_one_of_everything_helper()
|
||||
self.start_quota_mock(data_models.L7Policy)
|
||||
self.post(self.LBS_PATH, body)
|
||||
|
||||
def _getStatus(self, lb_id):
|
||||
res = self.get(self.LB_PATH.format(lb_id=lb_id + "/statuses"))
|
||||
return res.json.get('statuses').get('loadbalancer')
|
||||
|
||||
def test_statuses(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
self.assertEqual(lb['name'], response['name'])
|
||||
self.assertEqual(lb['id'], response['id'])
|
||||
self.assertEqual(lb['operating_status'],
|
||||
response['operating_status'])
|
||||
self.assertEqual(lb['provisioning_status'],
|
||||
response['provisioning_status'])
|
||||
|
||||
def _assertLB(self, lb, response):
|
||||
self.assertEqual(lb['name'], response['name'])
|
||||
self.assertEqual(lb['id'], response['id'])
|
||||
self.assertEqual(constants.ONLINE,
|
||||
response['operating_status'])
|
||||
self.assertEqual(constants.PENDING_UPDATE,
|
||||
response['provisioning_status'])
|
||||
|
||||
def test_statuses_listener(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.set_lb_status(lb['id'])
|
||||
listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb['id']).get('listener')
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
|
||||
self._assertLB(lb, response)
|
||||
response = response.get('listeners')[0]
|
||||
self.assertEqual(listener['name'], response['name'])
|
||||
self.assertEqual(listener['id'], response['id'])
|
||||
self.assertEqual(listener['operating_status'],
|
||||
response['operating_status'])
|
||||
self.assertEqual(listener['provisioning_status'],
|
||||
response['provisioning_status'])
|
||||
|
||||
def _assertListener(self, listener, response,
|
||||
prov_status=constants.ACTIVE):
|
||||
self.assertEqual(listener['name'], response['name'])
|
||||
self.assertEqual(listener['id'], response['id'])
|
||||
self.assertEqual(constants.ONLINE,
|
||||
response['operating_status'])
|
||||
self.assertEqual(prov_status, response['provisioning_status'])
|
||||
|
||||
def _assertListenerPending(self, listener, response):
|
||||
self._assertListener(listener, response, constants.PENDING_UPDATE)
|
||||
|
||||
def test_statuses_multiple_listeners(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.set_lb_status(lb['id'])
|
||||
listener1 = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb['id']).get('listener')
|
||||
|
||||
self.set_lb_status(lb['id'])
|
||||
listener2 = self.create_listener(
|
||||
constants.PROTOCOL_HTTPS, 443, lb['id']).get('listener')
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
|
||||
self._assertLB(lb, response)
|
||||
self._assertListener(listener1, response.get('listeners')[0])
|
||||
response = response.get('listeners')[1]
|
||||
self.assertEqual(listener2['name'], response['name'])
|
||||
self.assertEqual(listener2['id'], response['id'])
|
||||
self.assertEqual(listener2['operating_status'],
|
||||
response['operating_status'])
|
||||
self.assertEqual(listener2['provisioning_status'],
|
||||
response['provisioning_status'])
|
||||
|
||||
def test_statuses_pool(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.set_lb_status(lb['id'])
|
||||
listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb['id']).get('listener')
|
||||
self.set_lb_status(lb['id'])
|
||||
pool = self.create_pool(
|
||||
lb['id'],
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=listener['id']).get('pool')
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
|
||||
self._assertLB(lb, response)
|
||||
self._assertListenerPending(listener, response.get('listeners')[0])
|
||||
response = response.get('listeners')[0]['pools'][0]
|
||||
self.assertEqual(pool['name'], response['name'])
|
||||
self.assertEqual(pool['id'], response['id'])
|
||||
self.assertEqual(pool['operating_status'],
|
||||
response['operating_status'])
|
||||
self.assertEqual(pool['provisioning_status'],
|
||||
response['provisioning_status'])
|
||||
|
||||
def _assertPool(self, pool, response,
|
||||
prov_status=constants.ACTIVE):
|
||||
self.assertEqual(pool['name'], response['name'])
|
||||
self.assertEqual(pool['id'], response['id'])
|
||||
self.assertEqual(constants.ONLINE,
|
||||
response['operating_status'])
|
||||
self.assertEqual(prov_status, response['provisioning_status'])
|
||||
|
||||
def _assertPoolPending(self, pool, response):
|
||||
self._assertPool(pool, response, constants.PENDING_UPDATE)
|
||||
|
||||
def test_statuses_pools(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.set_lb_status(lb['id'])
|
||||
listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb['id']).get('listener')
|
||||
self.set_lb_status(lb['id'])
|
||||
pool1 = self.create_pool(
|
||||
lb['id'],
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=listener['id']).get('pool')
|
||||
self.set_lb_status(lb['id'])
|
||||
pool2 = self.create_pool(
|
||||
lb['id'],
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN).get('pool')
|
||||
self.set_lb_status(lb['id'])
|
||||
l7_policy = self.create_l7policy(
|
||||
listener['id'],
|
||||
constants.L7POLICY_ACTION_REDIRECT_TO_POOL,
|
||||
redirect_pool_id=pool2.get('id')).get('l7policy')
|
||||
self.set_lb_status(lb['id'])
|
||||
self.create_l7rule(
|
||||
l7_policy['id'], constants.L7RULE_TYPE_HOST_NAME,
|
||||
constants.L7RULE_COMPARE_TYPE_EQUAL_TO,
|
||||
'www.example.com').get(self.root_tag)
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
|
||||
self._assertLB(lb, response)
|
||||
self._assertListenerPending(listener, response.get('listeners')[0])
|
||||
self._assertPool(pool1, response.get('listeners')[0]['pools'][0])
|
||||
self._assertPool(pool2, response.get('listeners')[0]['pools'][1])
|
||||
|
||||
def test_statuses_health_monitor(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.set_lb_status(lb['id'])
|
||||
listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb['id']).get('listener')
|
||||
self.set_lb_status(lb['id'])
|
||||
pool = self.create_pool(
|
||||
lb['id'],
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=listener['id']).get('pool')
|
||||
self.set_lb_status(lb['id'])
|
||||
hm = self.create_health_monitor(
|
||||
pool['id'], constants.HEALTH_MONITOR_HTTP,
|
||||
1, 1, 1, 1).get('healthmonitor')
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
|
||||
self._assertLB(lb, response)
|
||||
self._assertListenerPending(listener, response.get('listeners')[0])
|
||||
self._assertPoolPending(pool, response.get('listeners')[0]['pools'][0])
|
||||
response = response.get('listeners')[0]['pools'][0]['health_monitor']
|
||||
self.assertEqual(hm['name'], response['name'])
|
||||
self.assertEqual(hm['id'], response['id'])
|
||||
self.assertEqual(hm['type'], response['type'])
|
||||
self.assertEqual(hm['operating_status'],
|
||||
response['operating_status'])
|
||||
self.assertEqual(hm['provisioning_status'],
|
||||
response['provisioning_status'])
|
||||
|
||||
def test_statuses_member(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.set_lb_status(lb['id'])
|
||||
listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb['id']).get('listener')
|
||||
self.set_lb_status(lb['id'])
|
||||
pool = self.create_pool(
|
||||
lb['id'],
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=listener['id']).get('pool')
|
||||
self.set_lb_status(lb['id'])
|
||||
member = self.create_member(
|
||||
pool['id'], '10.0.0.1', 80).get('member')
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
|
||||
self._assertLB(lb, response)
|
||||
self._assertListenerPending(listener, response.get('listeners')[0])
|
||||
self._assertPoolPending(pool, response.get('listeners')[0]['pools'][0])
|
||||
response = response.get('listeners')[0]['pools'][0]['members'][0]
|
||||
self.assertEqual(member['name'], response['name'])
|
||||
self.assertEqual(member['id'], response['id'])
|
||||
self.assertEqual(member['address'], response['address'])
|
||||
self.assertEqual(member['protocol_port'], response['protocol_port'])
|
||||
self.assertEqual(member['operating_status'],
|
||||
response['operating_status'])
|
||||
self.assertEqual(member['provisioning_status'],
|
||||
response['provisioning_status'])
|
||||
|
||||
def test_statuses_members(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.set_lb_status(lb['id'])
|
||||
listener = self.create_listener(
|
||||
constants.PROTOCOL_HTTP, 80, lb['id']).get('listener')
|
||||
self.set_lb_status(lb['id'])
|
||||
pool = self.create_pool(
|
||||
lb['id'],
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=listener['id']).get('pool')
|
||||
self.set_lb_status(lb['id'])
|
||||
member1 = self.create_member(
|
||||
pool['id'], '10.0.0.1', 80).get('member')
|
||||
self.set_lb_status(lb['id'])
|
||||
member2 = self.create_member(
|
||||
pool['id'], '10.0.0.2', 88, name='test').get('member')
|
||||
|
||||
response = self._getStatus(lb['id'])
|
||||
|
||||
self._assertLB(lb, response)
|
||||
self._assertListenerPending(listener, response.get('listeners')[0])
|
||||
self._assertPoolPending(pool, response.get('listeners')[0]['pools'][0])
|
||||
members = response.get('listeners')[0]['pools'][0]['members']
|
||||
response = members[0]
|
||||
self.assertEqual(member1['name'], response['name'])
|
||||
self.assertEqual(member1['id'], response['id'])
|
||||
self.assertEqual(member1['address'], response['address'])
|
||||
self.assertEqual(member1['protocol_port'], response['protocol_port'])
|
||||
self.assertEqual(constants.ONLINE,
|
||||
response['operating_status'])
|
||||
self.assertEqual(constants.ACTIVE,
|
||||
response['provisioning_status'])
|
||||
response = members[1]
|
||||
self.assertEqual(member2['name'], response['name'])
|
||||
self.assertEqual(member2['id'], response['id'])
|
||||
self.assertEqual(member2['address'], response['address'])
|
||||
self.assertEqual(member2['protocol_port'], response['protocol_port'])
|
||||
|
||||
def test_statuses_authorized(self):
|
||||
project_id = uuidutils.generate_uuid()
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid(),
|
||||
project_id=project_id).get('loadbalancer')
|
||||
|
||||
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
|
||||
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
|
||||
|
||||
with mock.patch.object(octavia.common.context.Context, 'project_id',
|
||||
project_id):
|
||||
override_credentials = {
|
||||
'service_user_id': None,
|
||||
'user_domain_id': None,
|
||||
'is_admin_project': True,
|
||||
'service_project_domain_id': None,
|
||||
'service_project_id': None,
|
||||
'roles': ['load-balancer_member'],
|
||||
'user_id': None,
|
||||
'is_admin': False,
|
||||
'service_user_domain_id': None,
|
||||
'project_domain_id': None,
|
||||
'service_roles': [],
|
||||
'project_id': project_id}
|
||||
with mock.patch(
|
||||
"oslo_context.context.RequestContext.to_policy_values",
|
||||
return_value=override_credentials):
|
||||
response = self._getStatus(lb['id'])
|
||||
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
|
||||
|
||||
self.assertEqual(lb['name'], response['name'])
|
||||
self.assertEqual(lb['id'], response['id'])
|
||||
self.assertEqual(lb['operating_status'],
|
||||
response['operating_status'])
|
||||
self.assertEqual(lb['provisioning_status'],
|
||||
response['provisioning_status'])
|
||||
|
||||
def test_statuses_not_authorized(self):
|
||||
lb = self.create_load_balancer(
|
||||
uuidutils.generate_uuid()).get('loadbalancer')
|
||||
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
|
||||
auth_strategy = self.conf.conf.api_settings.get('auth_strategy')
|
||||
self.conf.config(group='api_settings', auth_strategy=constants.TESTING)
|
||||
with mock.patch.object(octavia.common.context.Context, 'project_id',
|
||||
uuidutils.generate_uuid()):
|
||||
|
||||
res = self.get(self.LB_PATH.format(lb_id=lb['id'] + "/statuses"),
|
||||
status=403)
|
||||
self.conf.config(group='api_settings', auth_strategy=auth_strategy)
|
||||
self.assertEqual(self.NOT_AUTHORIZED_BODY, res.json)
|
||||
|
|
Loading…
Reference in New Issue