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:
Jude Cross 2017-06-14 16:27:33 -07:00 committed by Michael Johnson
parent 7466016ae9
commit ee08aaff56
7 changed files with 445 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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