# Copyright 2014 Rackspace # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from octavia_lib.api.drivers import data_models as driver_dm from oslo_config import cfg from oslo_config import fixture as oslo_fixture from oslo_utils import uuidutils from octavia.api.drivers import utils as driver_utils from octavia.common import constants import octavia.common.context from octavia.common import data_models from octavia.common import exceptions from octavia.db import repositories from octavia.network import base as network_base from octavia.tests.functional.api.v2 import base class TestMember(base.BaseAPITest): root_tag = 'member' root_tag_list = 'members' root_tag_links = 'members_links' def setUp(self): super().setUp() vip_subnet_id = uuidutils.generate_uuid() self.lb = self.create_load_balancer(vip_subnet_id) self.lb_id = self.lb.get('loadbalancer').get('id') self.project_id = self.lb.get('loadbalancer').get('project_id') self.set_lb_status(self.lb_id) self.listener = self.create_listener( constants.PROTOCOL_HTTP, 80, lb_id=self.lb_id) self.listener_id = self.listener.get('listener').get('id') self.set_lb_status(self.lb_id) self.pool = self.create_pool(self.lb_id, constants.PROTOCOL_HTTP, constants.LB_ALGORITHM_ROUND_ROBIN) self.pool_id = self.pool.get('pool').get('id') self.set_lb_status(self.lb_id) self.pool_with_listener = self.create_pool( self.lb_id, constants.PROTOCOL_HTTP, constants.LB_ALGORITHM_ROUND_ROBIN, listener_id=self.listener_id) self.pool_with_listener_id = ( self.pool_with_listener.get('pool').get('id')) self.set_lb_status(self.lb_id) self.members_path = self.MEMBERS_PATH.format( pool_id=self.pool_id) self.member_path = self.members_path + '/{member_id}' self.members_path_listener = self.MEMBERS_PATH.format( pool_id=self.pool_with_listener_id) self.member_path_listener = self.members_path_listener + '/{member_id}' self.pool_repo = repositories.PoolRepository() def test_get(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) response = self.get(self.member_path.format( member_id=api_member.get('id'))).json.get(self.root_tag) self.assertEqual(api_member, response) self.assertEqual(api_member.get('name'), '') self.assertEqual([], api_member['tags']) def test_get_authorized(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) 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', self.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': self.project_id} with mock.patch( "oslo_context.context.RequestContext.to_policy_values", return_value=override_credentials): response = self.get(self.member_path.format( member_id=api_member.get('id'))).json.get(self.root_tag) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(api_member, response) self.assertEqual(api_member.get('name'), '') def test_get_not_authorized(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) 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()): response = self.get(self.member_path.format( member_id=api_member.get('id')), status=403).json self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response) def test_get_deleted_gives_404(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) self.set_object_status(self.member_repo, api_member.get('id'), provisioning_status=constants.DELETED) self.get(self.member_path.format(member_id=api_member.get('id')), status=404) def test_bad_get(self): self.get(self.member_path.format(member_id=uuidutils.generate_uuid()), status=404) def test_get_all(self): api_m_1 = self.create_member( self.pool_id, '192.0.2.1', 80, tags=['test_tag1']).get(self.root_tag) self.set_lb_status(self.lb_id) api_m_2 = self.create_member( self.pool_id, '192.0.2.2', 80, tags=['test_tag2']).get(self.root_tag) self.set_lb_status(self.lb_id) # Original objects didn't have the updated operating/provisioning # status that exists in the DB. for m in [api_m_1, api_m_2]: m['operating_status'] = constants.ONLINE m['provisioning_status'] = constants.ACTIVE m.pop('updated_at') response = self.get(self.members_path).json.get(self.root_tag_list) self.assertIsInstance(response, list) self.assertEqual(2, len(response)) for m in response: m.pop('updated_at') for m in [api_m_1, api_m_2]: self.assertIn(m, response) def test_get_all_hides_deleted(self): api_member = self.create_member( self.pool_id, '10.0.0.1', 80).get(self.root_tag) response = self.get(self.members_path) objects = response.json.get(self.root_tag_list) self.assertEqual(len(objects), 1) self.set_object_status(self.member_repo, api_member.get('id'), provisioning_status=constants.DELETED) response = self.get(self.members_path) objects = response.json.get(self.root_tag_list) self.assertEqual(len(objects), 0) def test_get_all_authorized(self): api_m_1 = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) api_m_2 = self.create_member( self.pool_id, '192.0.2.2', 80).get(self.root_tag) self.set_lb_status(self.lb_id) # Original objects didn't have the updated operating/provisioning # status that exists in the DB. for m in [api_m_1, api_m_2]: m['operating_status'] = constants.ONLINE m['provisioning_status'] = constants.ACTIVE m.pop('updated_at') 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', self.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': self.project_id} with mock.patch( "oslo_context.context.RequestContext.to_policy_values", return_value=override_credentials): response = self.get(self.members_path) response = response.json.get(self.root_tag_list) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertIsInstance(response, list) self.assertEqual(2, len(response)) for m in response: m.pop('updated_at') for m in [api_m_1, api_m_2]: self.assertIn(m, response) def test_get_all_unscoped_token(self): api_m_1 = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) api_m_2 = self.create_member( self.pool_id, '192.0.2.2', 80).get(self.root_tag) self.set_lb_status(self.lb_id) # Original objects didn't have the updated operating/provisioning # status that exists in the DB. for m in [api_m_1, api_m_2]: m['operating_status'] = constants.ONLINE m['provisioning_status'] = constants.ACTIVE m.pop('updated_at') 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', None): 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': None} with mock.patch( "oslo_context.context.RequestContext.to_policy_values", return_value=override_credentials): result = self.get(self.members_path, status=403).json self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, result) def test_get_all_not_authorized(self): api_m_1 = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) api_m_2 = self.create_member( self.pool_id, '192.0.2.2', 80).get(self.root_tag) self.set_lb_status(self.lb_id) # Original objects didn't have the updated operating/provisioning # status that exists in the DB. for m in [api_m_1, api_m_2]: m['operating_status'] = constants.ONLINE m['provisioning_status'] = constants.ACTIVE m.pop('updated_at') 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()): response = self.get(self.members_path, status=403) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json) def test_get_all_sorted(self): self.create_member(self.pool_id, '192.0.2.1', 80, name='member1') self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.2', 80, name='member2') self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.3', 80, name='member3') self.set_lb_status(self.lb_id) response = self.get(self.members_path, params={'sort': 'name:desc'}) members_desc = response.json.get(self.root_tag_list) response = self.get(self.members_path, params={'sort': 'name:asc'}) members_asc = response.json.get(self.root_tag_list) self.assertEqual(3, len(members_desc)) self.assertEqual(3, len(members_asc)) member_id_names_desc = [(member.get('id'), member.get('name')) for member in members_desc] member_id_names_asc = [(member.get('id'), member.get('name')) for member in members_asc] self.assertEqual(member_id_names_asc, list(reversed(member_id_names_desc))) def test_get_all_limited(self): self.create_member(self.pool_id, '192.0.2.1', 80, name='member1') self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.2', 80, name='member2') self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.3', 80, name='member3') self.set_lb_status(self.lb_id) # First two -- should have 'next' link first_two = self.get(self.members_path, params={'limit': 2}).json objs = first_two[self.root_tag_list] links = first_two[self.root_tag_links] self.assertEqual(2, len(objs)) self.assertEqual(1, len(links)) self.assertEqual('next', links[0]['rel']) # Third + off the end -- should have previous link third = self.get(self.members_path, params={ 'limit': 2, 'marker': first_two[self.root_tag_list][1]['id']}).json objs = third[self.root_tag_list] links = third[self.root_tag_links] self.assertEqual(1, len(objs)) self.assertEqual(1, len(links)) self.assertEqual('previous', links[0]['rel']) # Middle -- should have both links middle = self.get(self.members_path, params={ 'limit': 1, 'marker': first_two[self.root_tag_list][0]['id']}).json objs = middle[self.root_tag_list] links = middle[self.root_tag_links] self.assertEqual(1, len(objs)) self.assertEqual(2, len(links)) self.assertCountEqual(['previous', 'next'], [link['rel'] for link in links]) def test_get_all_fields_filter(self): self.create_member(self.pool_id, '192.0.2.1', 80, name='member1') self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.2', 80, name='member2') self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.3', 80, name='member3') self.set_lb_status(self.lb_id) members = self.get(self.members_path, params={ 'fields': ['id', 'address']}).json for member in members['members']: 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, '192.0.2.1', 80, name='member1').get(self.root_tag) self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.2', 80, name='member2').get(self.root_tag) self.set_lb_status(self.lb_id) self.create_member(self.pool_id, '192.0.2.3', 80, name='member3').get(self.root_tag) self.set_lb_status(self.lb_id) members = self.get(self.members_path, params={ 'id': mem1['id']}).json self.assertEqual(1, len(members['members'])) self.assertEqual(mem1['id'], members['members'][0]['id']) def test_get_all_tags_filter(self): mem1 = self.create_member( self.pool_id, '192.0.2.1', 80, name='member1', tags=['test_tag1', 'test_tag2'] ).get(self.root_tag) self.set_lb_status(self.lb_id) mem2 = self.create_member( self.pool_id, '192.0.2.2', 80, name='member2', tags=['test_tag2', 'test_tag3'] ).get(self.root_tag) self.set_lb_status(self.lb_id) mem3 = self.create_member( self.pool_id, '192.0.2.3', 80, name='member3', tags=['test_tag4', 'test_tag5'] ).get(self.root_tag) self.set_lb_status(self.lb_id) mems = self.get( self.members_path, params={'tags': 'test_tag2'} ).json.get(self.root_tag_list) self.assertIsInstance(mems, list) self.assertEqual(2, len(mems)) self.assertEqual( [mem1.get('id'), mem2.get('id')], [mem.get('id') for mem in mems] ) mems = self.get( self.members_path, params={'tags': ['test_tag2', 'test_tag3']} ).json.get(self.root_tag_list) self.assertIsInstance(mems, list) self.assertEqual(1, len(mems)) self.assertEqual( [mem2.get('id')], [mem.get('id') for mem in mems] ) mems = self.get( self.members_path, params={'tags-any': 'test_tag2'} ).json.get(self.root_tag_list) self.assertIsInstance(mems, list) self.assertEqual(2, len(mems)) self.assertEqual( [mem1.get('id'), mem2.get('id')], [mem.get('id') for mem in mems] ) mems = self.get( self.members_path, params={'not-tags': 'test_tag2'} ).json.get(self.root_tag_list) self.assertIsInstance(mems, list) self.assertEqual(1, len(mems)) self.assertEqual( [mem3.get('id')], [mem.get('id') for mem in mems] ) mems = self.get( self.members_path, params={'not-tags-any': ['test_tag2', 'test_tag4']} ).json.get(self.root_tag_list) self.assertIsInstance(mems, list) self.assertEqual(0, len(mems)) mems = self.get( self.members_path, params={'tags': 'test_tag2', 'tags-any': ['test_tag1', 'test_tag3']} ).json.get(self.root_tag_list) self.assertIsInstance(mems, list) self.assertEqual(2, len(mems)) self.assertEqual( [mem1.get('id'), mem2.get('id')], [mem.get('id') for mem in mems] ) mems = self.get( self.members_path, params={'tags': 'test_tag2', 'not-tags': 'test_tag2'} ).json.get(self.root_tag_list) self.assertIsInstance(mems, list) self.assertEqual(0, len(mems)) def test_empty_get_all(self): response = self.get(self.members_path).json.get(self.root_tag_list) self.assertIsInstance(response, list) self.assertEqual(0, len(response)) def test_create_sans_listener(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) self.assertEqual('192.0.2.1', api_member['address']) self.assertEqual(80, api_member['protocol_port']) self.assertIsNotNone(api_member['created_at']) self.assertIsNone(api_member['updated_at']) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.ACTIVE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_CREATE, member_op_status=constants.NO_MONITOR) self.set_lb_status(self.lb_id) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_id, member_id=api_member.get('id')) def test_create_authorized(self): 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', self.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': self.project_id} with mock.patch( "oslo_context.context.RequestContext.to_policy_values", return_value=override_credentials): api_member = self.create_member( self.pool_id, '192.0.2.1', 80, tags=['test_tag']).get(self.root_tag) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual('192.0.2.1', api_member['address']) self.assertEqual(80, api_member['protocol_port']) self.assertEqual(['test_tag'], api_member['tags']) self.assertIsNotNone(api_member['created_at']) self.assertIsNone(api_member['updated_at']) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.ACTIVE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_CREATE, member_op_status=constants.NO_MONITOR) self.set_lb_status(self.lb_id) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_id, member_id=api_member.get('id')) def test_create_not_authorized(self): 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', self.project_id): api_member = self.create_member( self.pool_id, '192.0.2.1', 80, status=403) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, api_member) def test_create_pool_in_error(self): project_id = uuidutils.generate_uuid() lb1 = self.create_load_balancer(uuidutils.generate_uuid(), name='lb1', project_id=project_id) lb1_id = lb1.get('loadbalancer').get('id') self.set_lb_status(lb1_id) pool1 = self.create_pool( lb1_id, constants.PROTOCOL_HTTP, constants.LB_ALGORITHM_ROUND_ROBIN).get('pool') pool1_id = pool1.get('id') self.set_lb_status(lb1_id) self.set_object_status(self.pool_repo, pool1_id, provisioning_status=constants.ERROR) api_member = self.create_member(pool1_id, '192.0.2.1', 80, status=409) ref_msg = 'Pool %s is immutable and cannot be updated.' % pool1_id self.assertEqual(ref_msg, api_member.get('faultstring')) # TODO(rm_work) Remove after deprecation of project_id in POST (R series) def test_create_with_project_id_is_ignored(self): pid = uuidutils.generate_uuid() api_member = self.create_member( self.pool_id, '192.0.2.1', 80, project_id=pid).get(self.root_tag) self.assertEqual(self.project_id, api_member['project_id']) def test_create_backup(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80, backup=True).get(self.root_tag) self.assertTrue(api_member['backup']) self.set_lb_status(self.lb_id) api_member = self.create_member( self.pool_id, '192.0.2.1', 81, backup=False).get(self.root_tag) self.assertFalse(api_member['backup']) def test_bad_create(self): member = {'name': 'test1'} self.post(self.members_path, self._build_body(member), status=400) @mock.patch('octavia.api.drivers.utils.call_provider') def test_create_with_bad_provider(self, mock_provider): mock_provider.side_effect = exceptions.ProviderDriverError( prov='bad_driver', user_msg='broken') response = self.create_member(self.pool_id, '192.0.2.1', 80, status=500) self.assertIn('Provider \'bad_driver\' reports error: broken', response.get('faultstring')) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_full_batch_members(self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver member1 = {'address': '192.0.2.1', 'protocol_port': 80, 'project_id': self.project_id} member2 = {'address': '192.0.2.2', 'protocol_port': 80, 'project_id': self.project_id} member3 = {'address': '192.0.2.3', 'protocol_port': 80, 'project_id': self.project_id} member4 = {'address': '192.0.2.4', 'protocol_port': 80, 'project_id': self.project_id} member5 = {'address': '192.0.2.5', 'protocol_port': 80, 'project_id': self.project_id} member6 = {'address': '192.0.2.6', 'protocol_port': 80, 'project_id': self.project_id} members = [member1, member2, member3, member4] for m in members: self.create_member(pool_id=self.pool_id, **m) self.set_lb_status(self.lb_id) # We are only concerned about the batch update, so clear out the # create members calls above. mock_provider.reset_mock() req_dict = [member1, member2, member5, member6] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) self.put(path, body, status=202) returned_members = self.get( self.MEMBERS_PATH.format(pool_id=self.pool_id) ).json.get(self.root_tag_list) expected_members = [ ('192.0.2.1', 80, 'PENDING_UPDATE'), ('192.0.2.2', 80, 'PENDING_UPDATE'), ('192.0.2.3', 80, 'PENDING_DELETE'), ('192.0.2.4', 80, 'PENDING_DELETE'), ('192.0.2.5', 80, 'PENDING_CREATE'), ('192.0.2.6', 80, 'PENDING_CREATE'), ] provider_creates = [] provider_updates = [] for rm in returned_members: self.assertIn( (rm['address'], rm['protocol_port'], rm['provisioning_status']), expected_members) provider_dict = driver_utils.member_dict_to_provider_dict(rm) # Adjust for API response provider_dict['pool_id'] = self.pool_id if rm['provisioning_status'] == 'PENDING_UPDATE': del provider_dict['name'] del provider_dict['subnet_id'] provider_updates.append(driver_dm.Member(**provider_dict)) elif rm['provisioning_status'] == 'PENDING_CREATE': provider_dict['name'] = None provider_creates.append(driver_dm.Member(**provider_dict)) # Order matters here provider_creates += provider_updates mock_provider.assert_called_once_with(u'noop_driver', mock_driver.member_batch_update, self.pool_id, provider_creates) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_create_batch_members(self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver member5 = {'address': '192.0.2.5', 'protocol_port': 80, 'tags': ['test_tag1']} member6 = {'address': '192.0.2.6', 'protocol_port': 80, 'tags': ['test_tag2']} req_dict = [member5, member6] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) self.put(path, body, status=202) returned_members = self.get( self.MEMBERS_PATH.format(pool_id=self.pool_id) ).json.get(self.root_tag_list) expected_members = [ ('192.0.2.5', 80, 'PENDING_CREATE', ['test_tag1']), ('192.0.2.6', 80, 'PENDING_CREATE', ['test_tag2']), ] provider_members = [] for rm in returned_members: self.assertIn( (rm['address'], rm['protocol_port'], rm['provisioning_status'], rm['tags']), expected_members) provider_dict = driver_utils.member_dict_to_provider_dict(rm) # Adjust for API response provider_dict['pool_id'] = self.pool_id provider_dict['name'] = None provider_members.append(driver_dm.Member(**provider_dict)) mock_provider.assert_called_once_with(u'noop_driver', mock_driver.member_batch_update, self.pool_id, provider_members) def test_create_batch_members_with_bad_subnet(self): subnet_id = uuidutils.generate_uuid() member5 = {'address': '10.0.0.5', 'protocol_port': 80, 'subnet_id': subnet_id} req_dict = [member5] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) with mock.patch( 'octavia.common.utils.get_network_driver') as net_mock: net_mock.return_value.get_subnet = mock.Mock( side_effect=network_base.SubnetNotFound('Subnet not found')) response = self.put(path, body, status=400).json err_msg = 'Subnet ' + subnet_id + ' not found.' self.assertEqual(response.get('faultstring'), err_msg) def test_create_batch_members_with_invalid_address(self): # 169.254.169.254 is the default invalid member address member5 = {'address': '169.254.169.254', 'protocol_port': 80} req_dict = [member5] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) response = self.put(path, body, status=400).json err_msg = ("169.254.169.254 is not a valid option for member address") self.assertEqual(err_msg, response.get('faultstring')) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_additive_only_batch_members(self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver member1 = {'address': '192.0.2.1', 'protocol_port': 80} member2 = {'address': '192.0.2.2', 'protocol_port': 80} member3 = {'address': '192.0.2.3', 'protocol_port': 80} member4 = {'address': '192.0.2.4', 'protocol_port': 80} member5 = {'address': '192.0.2.5', 'protocol_port': 80} member6 = {'address': '192.0.2.6', 'protocol_port': 80} members = [member1, member2, member3, member4] for m in members: self.create_member(pool_id=self.pool_id, **m) self.set_lb_status(self.lb_id) # We are only concerned about the batch update, so clear out the # create members calls above. mock_provider.reset_mock() req_dict = [member1, member2, member5, member6] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) path = "{}?additive_only=True".format(path) self.put(path, body, status=202) returned_members = self.get( self.MEMBERS_PATH.format(pool_id=self.pool_id) ).json.get(self.root_tag_list) # Members 1+2 should be updated, 3+4 left alone, and 5+6 created expected_members = [ ('192.0.2.1', 80, 'PENDING_UPDATE'), ('192.0.2.2', 80, 'PENDING_UPDATE'), ('192.0.2.3', 80, 'ACTIVE'), ('192.0.2.4', 80, 'ACTIVE'), ('192.0.2.5', 80, 'PENDING_CREATE'), ('192.0.2.6', 80, 'PENDING_CREATE'), ] provider_creates = [] provider_updates = [] provider_ignored = [] for rm in returned_members: self.assertIn( (rm['address'], rm['protocol_port'], rm['provisioning_status']), expected_members) provider_dict = driver_utils.member_dict_to_provider_dict(rm) # Adjust for API response provider_dict['pool_id'] = self.pool_id if rm['provisioning_status'] == 'PENDING_UPDATE': del provider_dict['name'] del provider_dict['subnet_id'] provider_updates.append(driver_dm.Member(**provider_dict)) elif rm['provisioning_status'] == 'PENDING_CREATE': provider_dict['name'] = None provider_creates.append(driver_dm.Member(**provider_dict)) elif rm['provisioning_status'] == 'ACTIVE': provider_dict['name'] = None provider_ignored.append(driver_dm.Member(**provider_dict)) # Order matters here provider_creates += provider_updates provider_creates += provider_ignored mock_provider.assert_called_once_with(u'noop_driver', mock_driver.member_batch_update, self.pool_id, provider_creates) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_update_batch_members(self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver member1 = {'address': '192.0.2.1', 'protocol_port': 80, 'project_id': self.project_id} member2 = {'address': '192.0.2.2', 'protocol_port': 80, 'project_id': self.project_id} members = [member1, member2] for m in members: self.create_member(pool_id=self.pool_id, **m) self.set_lb_status(self.lb_id) # We are only concerned about the batch update, so clear out the # create members calls above. mock_provider.reset_mock() req_dict = [member1, member2] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) self.put(path, body, status=202) returned_members = self.get( self.MEMBERS_PATH.format(pool_id=self.pool_id) ).json.get(self.root_tag_list) expected_members = [ ('192.0.2.1', 80, 'PENDING_UPDATE'), ('192.0.2.2', 80, 'PENDING_UPDATE'), ] provider_members = [] for rm in returned_members: self.assertIn( (rm['address'], rm['protocol_port'], rm['provisioning_status']), expected_members) provider_dict = driver_utils.member_dict_to_provider_dict(rm) # Adjust for API response provider_dict['pool_id'] = self.pool_id del provider_dict['name'] del provider_dict['subnet_id'] provider_members.append(driver_dm.Member(**provider_dict)) mock_provider.assert_called_once_with(u'noop_driver', mock_driver.member_batch_update, self.pool_id, provider_members) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_update_members_subnet_duplicate( self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver subnet_id = uuidutils.generate_uuid() member1 = {'address': '192.0.2.1', 'protocol_port': 80, 'project_id': self.project_id, 'subnet_id': subnet_id} member2 = {'address': '192.0.2.2', 'protocol_port': 80, 'project_id': self.project_id, 'subnet_id': subnet_id} req_dict = [member1, member2] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) with mock.patch("octavia.common.validate." "subnet_exists") as m_subnet_exists: m_subnet_exists.return_value = True self.put(path, body, status=202) m_subnet_exists.assert_called_once_with( member1['subnet_id'], context=mock.ANY) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_update_members_subnet_not_found( self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver fake_subnet_id = uuidutils.generate_uuid() member1 = {'address': '192.0.2.1', 'protocol_port': 80, 'project_id': self.project_id, 'subnet_id': fake_subnet_id} req_dict = [member1] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) with mock.patch("octavia.common.validate." "subnet_exists") as m_subnet_exists: m_subnet_exists.return_value = False self.put(path, body, status=404) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_delete_batch_members(self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver member3 = {'address': '192.0.2.3', 'protocol_port': 80} member4 = {'address': '192.0.2.4', 'protocol_port': 80} members = [member3, member4] for m in members: self.create_member(pool_id=self.pool_id, **m) self.set_lb_status(self.lb_id) # We are only concerned about the batch update, so clear out the # create members calls above. mock_provider.reset_mock() req_dict = [] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) self.put(path, body, status=202) returned_members = self.get( self.MEMBERS_PATH.format(pool_id=self.pool_id) ).json.get(self.root_tag_list) expected_members = [ ('192.0.2.3', 80, 'PENDING_DELETE'), ('192.0.2.4', 80, 'PENDING_DELETE'), ] provider_members = [] for rm in returned_members: self.assertIn( (rm['address'], rm['protocol_port'], rm['provisioning_status']), expected_members) mock_provider.assert_called_once_with(u'noop_driver', mock_driver.member_batch_update, self.pool_id, provider_members) @mock.patch('octavia.api.drivers.driver_factory.get_driver') @mock.patch('octavia.api.drivers.utils.call_provider') def test_delete_batch_members_already_empty(self, mock_provider, mock_get_driver): mock_driver = mock.MagicMock() mock_driver.name = 'noop_driver' mock_get_driver.return_value = mock_driver req_dict = [] body = {self.root_tag_list: req_dict} path = self.MEMBERS_PATH.format(pool_id=self.pool_id) self.put(path, body, status=202) returned_members = self.get( self.MEMBERS_PATH.format(pool_id=self.pool_id) ).json.get(self.root_tag_list) self.assertEqual([], returned_members) mock_provider.assert_not_called() def test_create_with_attached_listener(self): api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80).get(self.root_tag) self.assertEqual('192.0.2.1', api_member['address']) self.assertEqual(80, api_member['protocol_port']) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.PENDING_UPDATE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_CREATE, member_op_status=constants.NO_MONITOR) self.set_lb_status(self.lb_id) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id')) def test_create_with_monitor_address_and_port(self): api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80, monitor_address='192.0.2.3', monitor_port=80).get(self.root_tag) self.assertEqual('192.0.2.1', api_member['address']) self.assertEqual(80, api_member['protocol_port']) self.assertEqual('192.0.2.3', api_member['monitor_address']) self.assertEqual(80, api_member['monitor_port']) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.PENDING_UPDATE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_CREATE, member_op_status=constants.NO_MONITOR) self.set_lb_status(self.lb_id) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id')) def test_create_with_health_monitor(self): self.create_health_monitor(self.pool_with_listener_id, constants.HEALTH_MONITOR_HTTP, 1, 1, 1, 1) self.set_lb_status(self.lb_id) api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80).get(self.root_tag) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.PENDING_UPDATE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_CREATE, member_op_status=constants.OFFLINE) def test_duplicate_create(self): member = {'address': '192.0.2.1', 'protocol_port': 80, 'project_id': self.project_id} self.post(self.members_path, self._build_body(member)) self.set_lb_status(self.lb_id) self.post(self.members_path, self._build_body(member), status=409) def test_create_with_bad_subnet(self): with mock.patch( 'octavia.common.utils.get_network_driver') as net_mock: net_mock.return_value.get_subnet = mock.Mock( side_effect=network_base.SubnetNotFound('Subnet not found')) subnet_id = uuidutils.generate_uuid() response = self.create_member(self.pool_id, '192.0.2.1', 80, subnet_id=subnet_id, status=400) err_msg = 'Subnet ' + subnet_id + ' not found.' self.assertEqual(response.get('faultstring'), err_msg) def test_create_with_valid_subnet(self): with mock.patch( 'octavia.common.utils.get_network_driver') as net_mock: subnet_id = uuidutils.generate_uuid() net_mock.return_value.get_subnet.return_value = subnet_id response = self.create_member( self.pool_id, '192.0.2.1', 80, subnet_id=subnet_id).get(self.root_tag) self.assertEqual('192.0.2.1', response['address']) self.assertEqual(80, response['protocol_port']) self.assertEqual(subnet_id, response['subnet_id']) def test_create_bad_port_number(self): member = {'address': '192.0.2.3', 'protocol_port': constants.MIN_PORT_NUMBER - 1} resp = self.post(self.members_path, self._build_body(member), status=400) self.assertIn('Value should be greater or equal to', resp.json.get('faultstring')) member = {'address': '192.0.2.3', 'protocol_port': constants.MAX_PORT_NUMBER + 1} resp = self.post(self.members_path, self._build_body(member), status=400) self.assertIn('Value should be lower or equal to', resp.json.get('faultstring')) def test_create_over_quota(self): self.start_quota_mock(data_models.Member) member = {'address': '192.0.2.3', 'protocol_port': 81} self.post(self.members_path, self._build_body(member), status=403) def test_update_with_attached_listener(self): old_name = "name1" new_name = "name2" api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80, name=old_name).get(self.root_tag) self.set_lb_status(self.lb_id) new_member = {'name': new_name} response = self.put( self.member_path_listener.format(member_id=api_member.get('id')), self._build_body(new_member)).json.get(self.root_tag) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.PENDING_UPDATE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_UPDATE) self.set_lb_status(self.lb_id) self.assertEqual(new_name, response.get('name')) self.assertEqual(api_member.get('created_at'), response.get('created_at')) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id')) def test_update_authorized(self): old_name = "name1" new_name = "name2" api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80, name=old_name, tags=['old_tag']).get(self.root_tag) self.set_lb_status(self.lb_id) new_member = {'name': new_name, 'tags': ['new_tag']} 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', self.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': self.project_id} with mock.patch( "oslo_context.context.RequestContext.to_policy_values", return_value=override_credentials): member_path = self.member_path_listener.format( member_id=api_member.get('id')) response = self.put( member_path, self._build_body(new_member)).json.get(self.root_tag) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.PENDING_UPDATE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_UPDATE) self.set_lb_status(self.lb_id) self.assertEqual(new_name, response.get('name')) self.assertEqual(['new_tag'], response['tags']) self.assertEqual(api_member.get('created_at'), response.get('created_at')) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id')) def test_update_not_authorized(self): old_name = "name1" new_name = "name2" api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80, name=old_name).get(self.root_tag) self.set_lb_status(self.lb_id) new_member = {'name': new_name} 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', self.project_id): member_path = self.member_path_listener.format( member_id=api_member.get('id')) response = self.put( member_path, self._build_body(new_member), status=403) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assertEqual(self.NOT_AUTHORIZED_BODY, response.json) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=api_member.get('id'), lb_prov_status=constants.ACTIVE, listener_prov_status=constants.ACTIVE, pool_prov_status=constants.ACTIVE, member_prov_status=constants.ACTIVE) def test_update_sans_listener(self): old_name = "name1" new_name = "name2" api_member = self.create_member( self.pool_id, '192.0.2.1', 80, name=old_name).get(self.root_tag) self.set_lb_status(self.lb_id) member_path = self.member_path.format( member_id=api_member.get('id')) new_member = {'name': new_name} response = self.put( member_path, self._build_body(new_member)).json.get(self.root_tag) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_id, member_id=api_member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.ACTIVE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_UPDATE) self.set_lb_status(self.lb_id) self.assertEqual(new_name, response.get('name')) self.assertEqual(api_member.get('created_at'), response.get('created_at')) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_id, member_id=api_member.get('id')) def test_bad_update(self): api_member = self.create_member( self.pool_id, '192.0.2.1', 80).get(self.root_tag) new_member = {'protocol_port': 'ten'} self.put(self.member_path.format(member_id=api_member.get('id')), self._build_body(new_member), status=400) @mock.patch('octavia.api.drivers.utils.call_provider') def test_update_with_bad_provider(self, mock_provider): api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80, name="member1").get(self.root_tag) self.set_lb_status(self.lb_id) new_member = {'name': "member2"} mock_provider.side_effect = exceptions.ProviderDriverError( prov='bad_driver', user_msg='broken') response = self.put(self.member_path_listener.format( member_id=api_member.get('id')), self._build_body(new_member), status=500) self.assertIn('Provider \'bad_driver\' reports error: broken', response.json.get('faultstring')) def test_update_unset_defaults(self): old_name = "name1" api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80, name=old_name, backup=True, monitor_address='192.0.2.2', monitor_port=8888, weight=10).get(self.root_tag) self.set_lb_status(self.lb_id) unset_params = {'name': None, 'backup': None, 'monitor_address': None, 'monitor_port': None, 'weight': None} member_path = self.member_path_listener.format( member_id=api_member.get('id')) response = self.put(member_path, self._build_body(unset_params)) response = response.json.get(self.root_tag) self.assertFalse(response['backup']) self.assertIsNone(response['monitor_address']) self.assertIsNone(response['monitor_port']) self.assertEqual('', response['name']) self.assertEqual(constants.DEFAULT_WEIGHT, response['weight']) def test_delete(self): api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) member = self.get(self.member_path_listener.format( member_id=api_member.get('id'))).json.get(self.root_tag) api_member['provisioning_status'] = constants.ACTIVE api_member['operating_status'] = constants.ONLINE self.assertIsNone(api_member.pop('updated_at')) self.assertIsNotNone(member.pop('updated_at')) self.assertEqual(api_member, member) self.delete(self.member_path_listener.format( member_id=api_member.get('id'))) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.PENDING_UPDATE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_DELETE) self.set_lb_status(self.lb_id) member = self.get(self.member_path_listener.format( member_id=api_member.get('id')), status=404) def test_delete_authorized(self): api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) member = self.get(self.member_path_listener.format( member_id=api_member.get('id'))).json.get(self.root_tag) api_member['provisioning_status'] = constants.ACTIVE api_member['operating_status'] = constants.ONLINE self.assertIsNone(api_member.pop('updated_at')) self.assertIsNotNone(member.pop('updated_at')) self.assertEqual(api_member, member) 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', self.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': self.project_id} with mock.patch( "oslo_context.context.RequestContext.to_policy_values", return_value=override_credentials): self.delete(self.member_path_listener.format( member_id=api_member.get('id'))) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=member.get('id'), lb_prov_status=constants.PENDING_UPDATE, listener_prov_status=constants.PENDING_UPDATE, pool_prov_status=constants.PENDING_UPDATE, member_prov_status=constants.PENDING_DELETE) self.set_lb_status(self.lb_id) member = self.get(self.member_path_listener.format( member_id=api_member.get('id')), status=404) def test_delete_not_authorized(self): api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) member = self.get(self.member_path_listener.format( member_id=api_member.get('id'))).json.get(self.root_tag) api_member['provisioning_status'] = constants.ACTIVE api_member['operating_status'] = constants.ONLINE self.assertIsNone(api_member.pop('updated_at')) self.assertIsNotNone(member.pop('updated_at')) self.assertEqual(api_member, member) 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', self.project_id): self.delete(self.member_path_listener.format( member_id=api_member.get('id')), status=403) self.conf.config(group='api_settings', auth_strategy=auth_strategy) self.assert_correct_status( lb_id=self.lb_id, listener_id=self.listener_id, pool_id=self.pool_with_listener_id, member_id=member.get('id'), lb_prov_status=constants.ACTIVE, listener_prov_status=constants.ACTIVE, pool_prov_status=constants.ACTIVE, member_prov_status=constants.ACTIVE) def test_bad_delete(self): self.delete(self.member_path.format( member_id=uuidutils.generate_uuid()), status=404) def test_delete_mismatch_pool(self): # Create a pool that will not have the member, but is valid. self.pool = self.create_pool(self.lb_id, constants.PROTOCOL_HTTP, constants.LB_ALGORITHM_ROUND_ROBIN) bad_pool_id = self.pool.get('pool').get('id') self.set_lb_status(self.lb_id) # Create a member on our reference pool api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) # Attempt to delete the member using the wrong pool in the path member_path = self.MEMBERS_PATH.format( pool_id=bad_pool_id) + '/' + api_member['id'] result = self.delete(member_path, status=404).json ref_msg = 'Member %s not found.' % api_member['id'] self.assertEqual(ref_msg, result.get('faultstring')) @mock.patch('octavia.api.drivers.utils.call_provider') def test_delete_with_bad_provider(self, mock_provider): api_member = self.create_member( self.pool_with_listener_id, '192.0.2.1', 80).get(self.root_tag) self.set_lb_status(self.lb_id) member = self.get(self.member_path_listener.format( member_id=api_member.get('id'))).json.get(self.root_tag) api_member['provisioning_status'] = constants.ACTIVE api_member['operating_status'] = constants.ONLINE self.assertIsNone(api_member.pop('updated_at')) self.assertIsNotNone(member.pop('updated_at')) self.assertEqual(api_member, member) mock_provider.side_effect = exceptions.ProviderDriverError( prov='bad_driver', user_msg='broken') self.delete(self.member_path_listener.format( member_id=api_member.get('id')), status=500) def test_create_when_lb_pending_update(self): self.create_member(self.pool_id, address="192.0.2.2", protocol_port=80) self.set_lb_status(self.lb_id) self.put(self.LB_PATH.format(lb_id=self.lb_id), body={'loadbalancer': {'name': 'test_name_change'}}) member = {'address': '192.0.2.1', 'protocol_port': 80, 'project_id': self.project_id} self.post(self.members_path, body=self._build_body(member), status=409) def test_update_when_lb_pending_update(self): member = self.create_member( self.pool_id, address="192.0.2.1", protocol_port=80, name="member1").get(self.root_tag) self.set_lb_status(self.lb_id) self.put(self.LB_PATH.format(lb_id=self.lb_id), body={'loadbalancer': {'name': 'test_name_change'}}) self.put( self.member_path.format(member_id=member.get('id')), body=self._build_body({'name': "member2"}), status=409) def test_delete_when_lb_pending_update(self): member = self.create_member( self.pool_id, address="192.0.2.1", protocol_port=80).get(self.root_tag) self.set_lb_status(self.lb_id) self.put(self.LB_PATH.format(lb_id=self.lb_id), body={'loadbalancer': {'name': 'test_name_change'}}) self.delete(self.member_path.format( member_id=member.get('id')), status=409) def test_create_when_lb_pending_delete(self): self.create_member(self.pool_id, address="192.0.2.1", protocol_port=80) self.set_lb_status(self.lb_id) self.delete(self.LB_PATH.format(lb_id=self.lb_id), params={'cascade': "true"}) member = {'address': '192.0.2.2', 'protocol_port': 88, 'project_id': self.project_id} self.post(self.members_path, body=self._build_body(member), status=409) def test_update_when_lb_pending_delete(self): member = self.create_member( self.pool_id, address="192.0.2.1", protocol_port=80, name="member1").get(self.root_tag) self.set_lb_status(self.lb_id) self.delete(self.LB_PATH.format(lb_id=self.lb_id), params={'cascade': "true"}) self.put(self.member_path.format(member_id=member.get('id')), body=self._build_body({'name': "member2"}), status=409) def test_update_when_deleted(self): member = self.create_member( self.pool_id, address="10.0.0.1", protocol_port=80).get(self.root_tag) self.set_lb_status(self.lb_id, status=constants.DELETED) self.put(self.member_path.format(member_id=member.get('id')), body=self._build_body({'name': "member2"}), status=404) def test_delete_when_lb_pending_delete(self): member = self.create_member( self.pool_id, address="192.0.2.1", protocol_port=80).get(self.root_tag) self.set_lb_status(self.lb_id) self.delete(self.LB_PATH.format(lb_id=self.lb_id), params={'cascade': "true"}) self.delete(self.member_path.format( member_id=member.get('id')), status=409) def test_delete_already_deleted(self): member = self.create_member( self.pool_id, address="192.0.2.1", protocol_port=80).get(self.root_tag) self.set_lb_status(self.lb_id, status=constants.DELETED) self.delete(self.member_path.format( member_id=member.get('id')), status=404)