From dd1f2fe418e9eb9b299f863d6abe3e7d957db307 Mon Sep 17 00:00:00 2001 From: Adam Harwell Date: Tue, 10 Apr 2018 06:04:51 +0900 Subject: [PATCH] Allow fields filter on single object GETs The LBaaSv2-API spec had this, and tempest seems to test for it. We missed adding it when we implemented the v2 API in Pike. Conflicts: octavia/api/v2/controllers/listener.py Backport-Candidate: Queens Pike Change-Id: I16c04b6f12fe4db9f1a2a0dc1d2b6fb54d24fd38 (cherry picked from commit 84ef448a87ac7251a8087e435778f3ed761706b5) --- octavia/api/v2/controllers/amphora.py | 6 +++-- octavia/api/v2/controllers/health_monitor.py | 6 +++-- octavia/api/v2/controllers/l7policy.py | 11 +++++--- octavia/api/v2/controllers/l7rule.py | 11 +++++--- octavia/api/v2/controllers/listener.py | 7 +++-- octavia/api/v2/controllers/load_balancer.py | 7 +++-- octavia/api/v2/controllers/member.py | 11 +++++--- octavia/api/v2/controllers/pool.py | 7 +++-- .../tests/functional/api/v2/test_amphora.py | 14 +++++++--- .../functional/api/v2/test_health_monitor.py | 27 ++++++++++++++++--- .../tests/functional/api/v2/test_l7policy.py | 19 ++++++++++--- .../tests/functional/api/v2/test_l7rule.py | 20 +++++++++++--- .../tests/functional/api/v2/test_listener.py | 19 ++++++++++--- .../functional/api/v2/test_load_balancer.py | 18 ++++++++++--- .../tests/functional/api/v2/test_member.py | 21 ++++++++++++--- octavia/tests/functional/api/v2/test_pool.py | 21 ++++++++++++--- 16 files changed, 178 insertions(+), 47 deletions(-) diff --git a/octavia/api/v2/controllers/amphora.py b/octavia/api/v2/controllers/amphora.py index 9ef9892760..bfed1cfd41 100644 --- a/octavia/api/v2/controllers/amphora.py +++ b/octavia/api/v2/controllers/amphora.py @@ -38,8 +38,8 @@ class AmphoraController(base.BaseController): self.handler = self.handler.amphora @wsme_pecan.wsexpose(amp_types.AmphoraRootResponse, wtypes.text, - wtypes.text) - def get_one(self, id): + [wtypes.text], ignore_extra_args=True) + def get_one(self, id, fields=None): """Gets a single amphora's details.""" context = pecan.request.context.get('octavia_context') db_amp = self._get_db_amp(context.session, id) @@ -49,6 +49,8 @@ class AmphoraController(base.BaseController): result = self._convert_db_to_type( db_amp, amp_types.AmphoraResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return amp_types.AmphoraRootResponse(amphora=result) @wsme_pecan.wsexpose(amp_types.AmphoraeRootResponse, [wtypes.text], diff --git a/octavia/api/v2/controllers/health_monitor.py b/octavia/api/v2/controllers/health_monitor.py index 2d1aa26f4d..ffcd7f1053 100644 --- a/octavia/api/v2/controllers/health_monitor.py +++ b/octavia/api/v2/controllers/health_monitor.py @@ -54,8 +54,8 @@ class HealthMonitorController(base.BaseController): return db_hm @wsme_pecan.wsexpose(hm_types.HealthMonitorRootResponse, wtypes.text, - wtypes.text) - def get_one(self, id): + [wtypes.text], ignore_extra_args=True) + def get_one(self, id, fields=None): """Gets a single healthmonitor's details.""" context = pecan.request.context.get('octavia_context') db_hm = self._get_db_hm(context.session, id) @@ -65,6 +65,8 @@ class HealthMonitorController(base.BaseController): result = self._convert_db_to_type( db_hm, hm_types.HealthMonitorResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return hm_types.HealthMonitorRootResponse(healthmonitor=result) @wsme_pecan.wsexpose(hm_types.HealthMonitorsRootResponse, wtypes.text, diff --git a/octavia/api/v2/controllers/l7policy.py b/octavia/api/v2/controllers/l7policy.py index 7ac1d1ac81..097c70ede8 100644 --- a/octavia/api/v2/controllers/l7policy.py +++ b/octavia/api/v2/controllers/l7policy.py @@ -43,8 +43,9 @@ class L7PolicyController(base.BaseController): super(L7PolicyController, self).__init__() self.handler = self.handler.l7policy - @wsme_pecan.wsexpose(l7policy_types.L7PolicyRootResponse, wtypes.text) - def get(self, id): + @wsme_pecan.wsexpose(l7policy_types.L7PolicyRootResponse, wtypes.text, + [wtypes.text], ignore_extra_args=True) + def get(self, id, fields=None): """Gets a single l7policy's details.""" context = pecan.request.context.get('octavia_context') db_l7policy = self._get_db_l7policy(context.session, id) @@ -52,8 +53,10 @@ class L7PolicyController(base.BaseController): self._auth_validate_action(context, db_l7policy.project_id, constants.RBAC_GET_ONE) - result = self._convert_db_to_type(db_l7policy, - l7policy_types.L7PolicyResponse) + result = self._convert_db_to_type( + db_l7policy, l7policy_types.L7PolicyResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return l7policy_types.L7PolicyRootResponse(l7policy=result) @wsme_pecan.wsexpose(l7policy_types.L7PoliciesRootResponse, wtypes.text, diff --git a/octavia/api/v2/controllers/l7rule.py b/octavia/api/v2/controllers/l7rule.py index bcda5036cd..08ff023340 100644 --- a/octavia/api/v2/controllers/l7rule.py +++ b/octavia/api/v2/controllers/l7rule.py @@ -41,8 +41,9 @@ class L7RuleController(base.BaseController): self.l7policy_id = l7policy_id self.handler = self.handler.l7rule - @wsme_pecan.wsexpose(l7rule_types.L7RuleRootResponse, wtypes.text) - def get(self, id): + @wsme_pecan.wsexpose(l7rule_types.L7RuleRootResponse, wtypes.text, + [wtypes.text], ignore_extra_args=True) + def get(self, id, fields=None): """Gets a single l7rule's details.""" context = pecan.request.context.get('octavia_context') db_l7rule = self._get_db_l7rule(context.session, id) @@ -50,8 +51,10 @@ class L7RuleController(base.BaseController): self._auth_validate_action(context, db_l7rule.project_id, constants.RBAC_GET_ONE) - result = self._convert_db_to_type(db_l7rule, - l7rule_types.L7RuleResponse) + result = self._convert_db_to_type( + db_l7rule, l7rule_types.L7RuleResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return l7rule_types.L7RuleRootResponse(rule=result) @wsme_pecan.wsexpose(l7rule_types.L7RulesRootResponse, [wtypes.text], diff --git a/octavia/api/v2/controllers/listener.py b/octavia/api/v2/controllers/listener.py index 2bd9052fa7..64803defef 100644 --- a/octavia/api/v2/controllers/listener.py +++ b/octavia/api/v2/controllers/listener.py @@ -63,8 +63,9 @@ class ListenersController(base.BaseController): resource=data_models.Listener._name(), id=id) return db_listener - @wsme_pecan.wsexpose(listener_types.ListenerRootResponse, wtypes.text) - def get_one(self, id): + @wsme_pecan.wsexpose(listener_types.ListenerRootResponse, wtypes.text, + [wtypes.text], ignore_extra_args=True) + def get_one(self, id, fields=None): """Gets a single listener's details.""" context = pecan.request.context.get('octavia_context') db_listener = self._get_db_listener(context.session, id) @@ -74,6 +75,8 @@ class ListenersController(base.BaseController): result = self._convert_db_to_type(db_listener, listener_types.ListenerResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return listener_types.ListenerRootResponse(listener=result) @wsme_pecan.wsexpose(listener_types.ListenersRootResponse, wtypes.text, diff --git a/octavia/api/v2/controllers/load_balancer.py b/octavia/api/v2/controllers/load_balancer.py index f7cac8f764..40582cb6e6 100644 --- a/octavia/api/v2/controllers/load_balancer.py +++ b/octavia/api/v2/controllers/load_balancer.py @@ -49,8 +49,9 @@ class LoadBalancersController(base.BaseController): super(LoadBalancersController, self).__init__() self.handler = self.handler.load_balancer - @wsme_pecan.wsexpose(lb_types.LoadBalancerRootResponse, wtypes.text) - def get_one(self, id): + @wsme_pecan.wsexpose(lb_types.LoadBalancerRootResponse, wtypes.text, + [wtypes.text], ignore_extra_args=True) + def get_one(self, id, fields=None): """Gets a single load balancer's details.""" context = pecan.request.context.get('octavia_context') load_balancer = self._get_db_lb(context.session, id) @@ -60,6 +61,8 @@ class LoadBalancersController(base.BaseController): result = self._convert_db_to_type( load_balancer, lb_types.LoadBalancerResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return lb_types.LoadBalancerRootResponse(loadbalancer=result) @wsme_pecan.wsexpose(lb_types.LoadBalancersRootResponse, wtypes.text, diff --git a/octavia/api/v2/controllers/member.py b/octavia/api/v2/controllers/member.py index 24cf05a455..14be88a8e7 100644 --- a/octavia/api/v2/controllers/member.py +++ b/octavia/api/v2/controllers/member.py @@ -42,8 +42,9 @@ class MemberController(base.BaseController): self.pool_id = pool_id self.handler = self.handler.member - @wsme_pecan.wsexpose(member_types.MemberRootResponse, wtypes.text) - def get(self, id): + @wsme_pecan.wsexpose(member_types.MemberRootResponse, wtypes.text, + [wtypes.text], ignore_extra_args=True) + def get(self, id, fields=None): """Gets a single pool member's details.""" context = pecan.request.context.get('octavia_context') db_member = self._get_db_member(context.session, id) @@ -51,8 +52,10 @@ class MemberController(base.BaseController): self._auth_validate_action(context, db_member.project_id, constants.RBAC_GET_ONE) - result = self._convert_db_to_type(db_member, - member_types.MemberResponse) + result = self._convert_db_to_type( + db_member, member_types.MemberResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return member_types.MemberRootResponse(member=result) @wsme_pecan.wsexpose(member_types.MembersRootResponse, [wtypes.text], diff --git a/octavia/api/v2/controllers/pool.py b/octavia/api/v2/controllers/pool.py index cc06df750c..208a046d7c 100644 --- a/octavia/api/v2/controllers/pool.py +++ b/octavia/api/v2/controllers/pool.py @@ -45,8 +45,9 @@ class PoolsController(base.BaseController): super(PoolsController, self).__init__() self.handler = self.handler.pool - @wsme_pecan.wsexpose(pool_types.PoolRootResponse, wtypes.text) - def get(self, id): + @wsme_pecan.wsexpose(pool_types.PoolRootResponse, wtypes.text, + [wtypes.text], ignore_extra_args=True) + def get(self, id, fields=None): """Gets a pool's details.""" context = pecan.request.context.get('octavia_context') db_pool = self._get_db_pool(context.session, id) @@ -55,6 +56,8 @@ class PoolsController(base.BaseController): constants.RBAC_GET_ONE) result = self._convert_db_to_type(db_pool, pool_types.PoolResponse) + if fields is not None: + result = self._filter_fields([result], fields)[0] return pool_types.PoolRootResponse(pool=result) @wsme_pecan.wsexpose(pool_types.PoolsRootResponse, wtypes.text, diff --git a/octavia/tests/functional/api/v2/test_amphora.py b/octavia/tests/functional/api/v2/test_amphora.py index 4e1e315afd..f8d101e114 100644 --- a/octavia/tests/functional/api/v2/test_amphora.py +++ b/octavia/tests/functional/api/v2/test_amphora.py @@ -285,9 +285,17 @@ class TestAmphora(base.BaseAPITest): amps = self.get(self.AMPHORAE_PATH, params={ 'fields': ['id', 'role']}).json for amp in amps['amphorae']: - self.assertIn(u'id', amp.keys()) - self.assertIn(u'role', amp.keys()) - self.assertNotIn(u'ha_port_id', amp.keys()) + self.assertIn(u'id', amp) + self.assertIn(u'role', amp) + self.assertNotIn(u'ha_port_id', amp) + + def test_get_one_fields_filter(self): + amp = self.get( + self.AMPHORA_PATH.format(amphora_id=self.amp_id), + params={'fields': ['id', 'role']}).json.get(self.root_tag) + self.assertIn(u'id', amp) + self.assertIn(u'role', amp) + self.assertNotIn(u'ha_port_id', amp) def test_get_all_filter(self): self._create_additional_amp() diff --git a/octavia/tests/functional/api/v2/test_health_monitor.py b/octavia/tests/functional/api/v2/test_health_monitor.py index 02e9faa0d0..cd579358eb 100644 --- a/octavia/tests/functional/api/v2/test_health_monitor.py +++ b/octavia/tests/functional/api/v2/test_health_monitor.py @@ -512,9 +512,30 @@ class TestHealthMonitor(base.BaseAPITest): hms = self.get(self.HMS_PATH, params={ 'fields': ['id', 'project_id']}).json for hm in hms['healthmonitors']: - self.assertIn(u'id', hm.keys()) - self.assertIn(u'project_id', hm.keys()) - self.assertNotIn(u'description', hm.keys()) + self.assertIn(u'id', hm) + self.assertIn(u'project_id', hm) + self.assertNotIn(u'description', hm) + + def test_get_one_fields_filter(self): + pool1 = self.create_pool( + self.lb_id, + constants.PROTOCOL_HTTP, + constants.LB_ALGORITHM_ROUND_ROBIN, + name='pool1').get('pool') + self.set_lb_status(self.lb_id) + + self.set_lb_status(self.lb_id) + hm1 = self.create_health_monitor( + pool1.get('id'), constants.HEALTH_MONITOR_HTTP, + 1, 1, 1, 1, name='hm1').get(self.root_tag) + self.set_lb_status(self.lb_id) + + hm = self.get( + self.HM_PATH.format(healthmonitor_id=hm1.get('id')), + params={'fields': ['id', 'project_id']}).json.get(self.root_tag) + self.assertIn(u'id', hm) + self.assertIn(u'project_id', hm) + self.assertNotIn(u'description', hm) def test_get_all_filter(self): pool1 = self.create_pool( diff --git a/octavia/tests/functional/api/v2/test_l7policy.py b/octavia/tests/functional/api/v2/test_l7policy.py index 3f22c28d9a..a86030f4ff 100644 --- a/octavia/tests/functional/api/v2/test_l7policy.py +++ b/octavia/tests/functional/api/v2/test_l7policy.py @@ -448,9 +448,22 @@ class TestL7Policy(base.BaseAPITest): l7pos = self.get(self.L7POLICIES_PATH, params={ 'fields': ['id', 'project_id']}).json for l7po in l7pos['l7policies']: - self.assertIn(u'id', l7po.keys()) - self.assertIn(u'project_id', l7po.keys()) - self.assertNotIn(u'description', l7po.keys()) + self.assertIn(u'id', l7po) + self.assertIn(u'project_id', l7po) + self.assertNotIn(u'description', l7po) + + def test_get_one_fields_filter(self): + l7p1 = self.create_l7policy( + self.listener_id, constants.L7POLICY_ACTION_REJECT, + name='policy1').get(self.root_tag) + self.set_lb_status(self.lb_id) + + l7po = self.get( + self.L7POLICY_PATH.format(l7policy_id=l7p1.get('id')), + params={'fields': ['id', 'project_id']}).json.get(self.root_tag) + self.assertIn(u'id', l7po) + self.assertIn(u'project_id', l7po) + self.assertNotIn(u'description', l7po) def test_get_all_filter(self): policy1 = self.create_l7policy( diff --git a/octavia/tests/functional/api/v2/test_l7rule.py b/octavia/tests/functional/api/v2/test_l7rule.py index 4992289ec1..14b1a7436a 100644 --- a/octavia/tests/functional/api/v2/test_l7rule.py +++ b/octavia/tests/functional/api/v2/test_l7rule.py @@ -311,9 +311,23 @@ class TestL7Rule(base.BaseAPITest): l7rus = self.get(self.l7rules_path, params={ 'fields': ['id', 'compare_type']}).json for l7ru in l7rus['rules']: - self.assertIn(u'id', l7ru.keys()) - self.assertIn(u'compare_type', l7ru.keys()) - self.assertNotIn(u'project_id', l7ru.keys()) + self.assertIn(u'id', l7ru) + self.assertIn(u'compare_type', l7ru) + self.assertNotIn(u'project_id', l7ru) + + def test_get_one_fields_filter(self): + l7r1 = self.create_l7rule( + self.l7policy_id, constants.L7RULE_TYPE_PATH, + constants.L7RULE_COMPARE_TYPE_STARTS_WITH, + '/api').get(self.root_tag) + self.set_lb_status(self.lb_id) + + l7ru = self.get( + self.l7rule_path.format(l7rule_id=l7r1.get('id')), + params={'fields': ['id', 'compare_type']}).json.get(self.root_tag) + self.assertIn(u'id', l7ru) + self.assertIn(u'compare_type', l7ru) + self.assertNotIn(u'project_id', l7ru) def test_get_all_filter(self): ru1 = self.create_l7rule( diff --git a/octavia/tests/functional/api/v2/test_listener.py b/octavia/tests/functional/api/v2/test_listener.py index 04b9259821..8eb1cc5842 100644 --- a/octavia/tests/functional/api/v2/test_listener.py +++ b/octavia/tests/functional/api/v2/test_listener.py @@ -324,9 +324,22 @@ class TestListener(base.BaseAPITest): lis = self.get(self.LISTENERS_PATH, params={ 'fields': ['id', 'project_id']}).json for li in lis['listeners']: - self.assertIn(u'id', li.keys()) - self.assertIn(u'project_id', li.keys()) - self.assertNotIn(u'description', li.keys()) + self.assertIn(u'id', li) + self.assertIn(u'project_id', li) + self.assertNotIn(u'description', li) + + def test_get_one_fields_filter(self): + listener1 = self.create_listener( + constants.PROTOCOL_HTTP, 80, self.lb_id, + name='listener1').get(self.root_tag) + self.set_lb_status(self.lb_id) + + li = self.get( + self.LISTENER_PATH.format(listener_id=listener1.get('id')), + params={'fields': ['id', 'project_id']}).json.get(self.root_tag) + self.assertIn(u'id', li) + self.assertIn(u'project_id', li) + self.assertNotIn(u'description', li) def test_get_all_filter(self): li1 = self.create_listener(constants.PROTOCOL_HTTP, diff --git a/octavia/tests/functional/api/v2/test_load_balancer.py b/octavia/tests/functional/api/v2/test_load_balancer.py index ee616fa7ee..0b10566d40 100644 --- a/octavia/tests/functional/api/v2/test_load_balancer.py +++ b/octavia/tests/functional/api/v2/test_load_balancer.py @@ -1034,9 +1034,21 @@ class TestLoadBalancer(base.BaseAPITest): lbs = self.get(self.LBS_PATH, params={ 'fields': ['id', 'project_id']}).json for lb in lbs['loadbalancers']: - self.assertIn(u'id', lb.keys()) - self.assertIn(u'project_id', lb.keys()) - self.assertNotIn(u'description', lb.keys()) + self.assertIn(u'id', lb) + self.assertIn(u'project_id', lb) + self.assertNotIn(u'description', lb) + + def test_get_one_fields_filter(self): + lb1 = self.create_load_balancer( + uuidutils.generate_uuid(), + name='lb1', project_id=self.project_id).get(self.root_tag) + + lb = self.get( + self.LB_PATH.format(lb_id=lb1.get('id')), + params={'fields': ['id', 'project_id']}).json.get(self.root_tag) + self.assertIn(u'id', lb) + self.assertIn(u'project_id', lb) + self.assertNotIn(u'description', lb) def test_get_all_admin_state_up_filter(self): self.create_load_balancer(uuidutils.generate_uuid(), diff --git a/octavia/tests/functional/api/v2/test_member.py b/octavia/tests/functional/api/v2/test_member.py index 0f54aba418..cbeeb12812 100644 --- a/octavia/tests/functional/api/v2/test_member.py +++ b/octavia/tests/functional/api/v2/test_member.py @@ -289,10 +289,23 @@ class TestMember(base.BaseAPITest): members = self.get(self.members_path, params={ 'fields': ['id', 'address']}).json for member in members['members']: - self.assertIn(u'id', member.keys()) - self.assertIn(u'address', member.keys()) - self.assertNotIn(u'name', member.keys()) - self.assertNotIn(u'monitor_address', member.keys()) + self.assertIn(u'id', member) + self.assertIn(u'address', member) + self.assertNotIn(u'name', member) + self.assertNotIn(u'monitor_address', member) + + def test_get_one_fields_filter(self): + member1 = self.create_member( + self.pool_id, '192.0.2.1', 80, name='member1').get(self.root_tag) + self.set_lb_status(self.lb_id) + + member = self.get( + self.member_path.format(member_id=member1.get('id')), + params={'fields': ['id', 'address']}).json.get(self.root_tag) + self.assertIn(u'id', member) + self.assertIn(u'address', member) + self.assertNotIn(u'name', member) + self.assertNotIn(u'monitor_address', member) def test_get_all_filter(self): mem1 = self.create_member(self.pool_id, diff --git a/octavia/tests/functional/api/v2/test_pool.py b/octavia/tests/functional/api/v2/test_pool.py index d57cee76c8..643fcd7701 100644 --- a/octavia/tests/functional/api/v2/test_pool.py +++ b/octavia/tests/functional/api/v2/test_pool.py @@ -472,9 +472,24 @@ class TestPool(base.BaseAPITest): pools = self.get(self.POOLS_PATH, params={ 'fields': ['id', 'project_id']}).json for pool in pools['pools']: - self.assertIn(u'id', pool.keys()) - self.assertIn(u'project_id', pool.keys()) - self.assertNotIn(u'description', pool.keys()) + self.assertIn(u'id', pool) + self.assertIn(u'project_id', pool) + self.assertNotIn(u'description', pool) + + def test_get_one_fields_filter(self): + pool1 = self.create_pool( + self.lb_id, + constants.PROTOCOL_HTTP, + constants.LB_ALGORITHM_ROUND_ROBIN, + name='pool1').get(self.root_tag) + self.set_lb_status(lb_id=self.lb_id) + + pool = self.get( + self.POOL_PATH.format(pool_id=pool1.get('id')), + params={'fields': ['id', 'project_id']}).json.get(self.root_tag) + self.assertIn(u'id', pool) + self.assertIn(u'project_id', pool) + self.assertNotIn(u'description', pool) def test_get_all_filter(self): po1 = self.create_pool(