diff --git a/api-ref/source/v2/examples/availabilityzoneprofile-create-request.json b/api-ref/source/v2/examples/availabilityzoneprofile-create-request.json index ec81e81b33..3bfb81d2e5 100644 --- a/api-ref/source/v2/examples/availabilityzoneprofile-create-request.json +++ b/api-ref/source/v2/examples/availabilityzoneprofile-create-request.json @@ -3,6 +3,6 @@ { "name": "some_az", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"az1\"}" + "availability_zone_data": "{\"compute_zone\": \"az1\", \"volume_zone\": \"az1\"}" } } diff --git a/api-ref/source/v2/examples/availabilityzoneprofile-create-response.json b/api-ref/source/v2/examples/availabilityzoneprofile-create-response.json index 780837497d..6ca6cd68fd 100644 --- a/api-ref/source/v2/examples/availabilityzoneprofile-create-response.json +++ b/api-ref/source/v2/examples/availabilityzoneprofile-create-response.json @@ -4,6 +4,6 @@ "id": "5712097e-0092-45dc-bff0-ab68b61ad51a", "name": "some_az", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"az1\"}" + "availability_zone_data": "{\"compute_zone\": \"az1\", \"volume_zone\": \"az1\"}" } } diff --git a/api-ref/source/v2/examples/availabilityzoneprofile-list-response.json b/api-ref/source/v2/examples/availabilityzoneprofile-list-response.json index f666629f50..6541e1b4bd 100644 --- a/api-ref/source/v2/examples/availabilityzoneprofile-list-response.json +++ b/api-ref/source/v2/examples/availabilityzoneprofile-list-response.json @@ -4,7 +4,7 @@ "id": "5712097e-0092-45dc-bff0-ab68b61ad51a", "name": "some_az", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"az1\"}" + "availability_zone_data": "{\"compute_zone\": \"az1\", \"volume_zone\": \"az2\"}" } ] } diff --git a/api-ref/source/v2/examples/availabilityzoneprofile-show-response.json b/api-ref/source/v2/examples/availabilityzoneprofile-show-response.json index 780837497d..6ca6cd68fd 100644 --- a/api-ref/source/v2/examples/availabilityzoneprofile-show-response.json +++ b/api-ref/source/v2/examples/availabilityzoneprofile-show-response.json @@ -4,6 +4,6 @@ "id": "5712097e-0092-45dc-bff0-ab68b61ad51a", "name": "some_az", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"az1\"}" + "availability_zone_data": "{\"compute_zone\": \"az1\", \"volume_zone\": \"az1\"}" } } diff --git a/api-ref/source/v2/examples/availabilityzoneprofile-update-request.json b/api-ref/source/v2/examples/availabilityzoneprofile-update-request.json index 0f0c7982e5..eed7b0a7e0 100644 --- a/api-ref/source/v2/examples/availabilityzoneprofile-update-request.json +++ b/api-ref/source/v2/examples/availabilityzoneprofile-update-request.json @@ -3,6 +3,6 @@ { "name": "other_az", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"az2\"}" + "availability_zone_data": "{\"compute_zone\": \"az2\", \"volume_zone\": \"az2\"}" } } diff --git a/api-ref/source/v2/examples/availabilityzoneprofile-update-response.json b/api-ref/source/v2/examples/availabilityzoneprofile-update-response.json index ac81a82623..e92ebe2afb 100644 --- a/api-ref/source/v2/examples/availabilityzoneprofile-update-response.json +++ b/api-ref/source/v2/examples/availabilityzoneprofile-update-response.json @@ -4,6 +4,6 @@ "id": "5712097e-0092-45dc-bff0-ab68b61ad51a", "name": "other_az", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"az2\"}" + "availability_zone_data": "{\"compute_zone\": \"az2\", \"volume_zone\": \"az2\"}" } } diff --git a/api-ref/source/v2/examples/provider-availability-zone-capability-show-response.json b/api-ref/source/v2/examples/provider-availability-zone-capability-show-response.json index e929e14a64..2517eeec4f 100644 --- a/api-ref/source/v2/examples/provider-availability-zone-capability-show-response.json +++ b/api-ref/source/v2/examples/provider-availability-zone-capability-show-response.json @@ -3,6 +3,10 @@ { "name": "compute_zone", "description": "The compute availability zone." + }, + { + "name": "volume_zone", + "description": "The volume availability zone." } ] } diff --git a/doc/source/contributor/guides/providers.rst b/doc/source/contributor/guides/providers.rst index d524255d37..76951856a3 100644 --- a/doc/source/contributor/guides/providers.rst +++ b/doc/source/contributor/guides/providers.rst @@ -1782,6 +1782,7 @@ description. For example: .. code-block:: python {"compute_zone": "The compute availability zone to use for this loadbalancer.", + "volume_zone": "The volume availability zone to use for this loadbalancer.", "management_network": "The management network ID for the loadbalancer.", "valid_vip_networks": "List of network IDs that are allowed for VIP use. This overrides/replaces the list of allowed networks configured in `octavia.conf`."} diff --git a/octavia/api/drivers/amphora_driver/availability_zone_schema.py b/octavia/api/drivers/amphora_driver/availability_zone_schema.py index 34bc8fabe5..a1288d0ee2 100644 --- a/octavia/api/drivers/amphora_driver/availability_zone_schema.py +++ b/octavia/api/drivers/amphora_driver/availability_zone_schema.py @@ -39,6 +39,10 @@ SUPPORTED_AVAILABILITY_ZONE_SCHEMA = { "type": "string", "description": "The compute availability zone." }, + consts.VOLUME_ZONE: { + "type": "string", + "description": "The volume availability zone." + }, consts.MANAGEMENT_NETWORK: { "type": "string", "description": "The management network ID for the amphora." diff --git a/octavia/api/drivers/amphora_driver/v2/driver.py b/octavia/api/drivers/amphora_driver/v2/driver.py index e8ad4eb8a0..e4d502c144 100644 --- a/octavia/api/drivers/amphora_driver/v2/driver.py +++ b/octavia/api/drivers/amphora_driver/v2/driver.py @@ -582,6 +582,18 @@ class AmphoraProviderDriver(driver_base.ProviderDriver): # when the octavia-lib supports it. compute_driver.validate_availability_zone(compute_zone) + volume_zone = availability_zone_dict.get(consts.VOLUME_ZONE, None) + if volume_zone: + volume_driver = stevedore_driver.DriverManager( + namespace='octavia.volume.drivers', + name=CONF.controller_worker.volume_driver, + invoke_on_load=True + ).driver + + # TODO(johnsom) Fix this to raise a NotFound error + # when the octavia-lib supports it. + volume_driver.validate_availability_zone(volume_zone) + check_nets = availability_zone_dict.get( consts.VALID_VIP_NETWORKS, []) management_net = availability_zone_dict.get( diff --git a/octavia/api/drivers/noop_driver/driver.py b/octavia/api/drivers/noop_driver/driver.py index 20b2d4c5b2..48f6cf246b 100644 --- a/octavia/api/drivers/noop_driver/driver.py +++ b/octavia/api/drivers/noop_driver/driver.py @@ -260,6 +260,8 @@ class NoopManager: self.__class__.__name__) return {"compute_zone": "The compute availability zone to use for " + "this loadbalancer.", + "volume_zone": "The volume availability zone to use for " "this loadbalancer."} def validate_availability_zone(self, availability_zone_metadata): diff --git a/octavia/common/constants.py b/octavia/common/constants.py index ec9a8f1b31..9325850d0a 100644 --- a/octavia/common/constants.py +++ b/octavia/common/constants.py @@ -332,6 +332,7 @@ CODE = 'code' COMPUTE_ID = 'compute_id' COMPUTE_OBJ = 'compute_obj' COMPUTE_ZONE = 'compute_zone' +VOLUME_ZONE = 'volume_zone' CONN_MAX_RETRIES = 'conn_max_retries' CONN_RETRY_INTERVAL = 'conn_retry_interval' CREATED_AT = 'created_at' diff --git a/octavia/compute/compute_base.py b/octavia/compute/compute_base.py index d99bdb79db..9097384d7f 100644 --- a/octavia/compute/compute_base.py +++ b/octavia/compute/compute_base.py @@ -42,7 +42,7 @@ class ComputeBase(metaclass=abc.ABCMeta): well or a string :param server_group_id: Optional server group id(uuid) which is used for anti_affinity feature - :param availability_zone: Name of the compute availability zone. + :param availability_zone: Availability zone data dict :raises ComputeBuildException: if compute failed to build amphora :returns: UUID of amphora diff --git a/octavia/compute/drivers/nova_driver.py b/octavia/compute/drivers/nova_driver.py index 84d661117f..47070e03dc 100644 --- a/octavia/compute/drivers/nova_driver.py +++ b/octavia/compute/drivers/nova_driver.py @@ -90,8 +90,7 @@ class VirtualMachineManager(compute_base.ComputeBase): well or a string :param server_group_id: Optional server group id(uuid) which is used for anti_affinity feature - :param availability_zone: Name of the compute availability zone. - + :param availability_zone: Availability zone data dict :raises ComputeBuildException: if nova failed to build virtual machine :returns: UUID of amphora @@ -109,7 +108,12 @@ class VirtualMachineManager(compute_base.ComputeBase): server_group = None if server_group_id is None else { "group": server_group_id} - az_name = availability_zone or CONF.nova.availability_zone + + if availability_zone: + az_name = availability_zone.get(constants.COMPUTE_ZONE, + CONF.nova.availability_zone) + else: + az_name = CONF.nova.availability_zone image_id = self.image_driver.get_image_id_by_tag( image_tag, image_owner) @@ -127,7 +131,7 @@ class VirtualMachineManager(compute_base.ComputeBase): LOG.debug('Creating volume for amphora from image %s', image_id) volume_id = self.volume_driver.create_volume_from_image( - image_id) + image_id, availability_zone) LOG.debug('Created boot volume %s for amphora', volume_id) # If use volume based, does not require image ID anymore image_id = None diff --git a/octavia/controller/worker/v2/tasks/compute_tasks.py b/octavia/controller/worker/v2/tasks/compute_tasks.py index 462faa5b40..522a65d1cb 100644 --- a/octavia/controller/worker/v2/tasks/compute_tasks.py +++ b/octavia/controller/worker/v2/tasks/compute_tasks.py @@ -107,13 +107,9 @@ class ComputeCreate(BaseComputeTask): amp_image_tag = CONF.controller_worker.amp_image_tag if availability_zone: - amp_availability_zone = availability_zone.get( - constants.COMPUTE_ZONE) amp_network = availability_zone.get(constants.MANAGEMENT_NETWORK) if amp_network: network_ids = [amp_network] - else: - amp_availability_zone = None try: if CONF.haproxy_amphora.build_rate_limit != -1: self.rate_limit.add_to_build_request_queue( @@ -146,7 +142,7 @@ class ComputeCreate(BaseComputeTask): config_drive_files=config_drive_files, user_data=user_data, server_group_id=server_group_id, - availability_zone=amp_availability_zone) + availability_zone=availability_zone) LOG.info("Server created with id: %s for amphora id: %s", compute_id, amphora_id) diff --git a/octavia/tests/functional/api/v2/test_provider.py b/octavia/tests/functional/api/v2/test_provider.py index 3072f1794e..50c69f0cef 100644 --- a/octavia/tests/functional/api/v2/test_provider.py +++ b/octavia/tests/functional/api/v2/test_provider.py @@ -186,9 +186,14 @@ class TestAvailabilityZoneCapabilities(base.BaseAPITest): provider='bogus'), status=400) def test_noop_provider(self): - ref_capabilities = [{'description': 'The compute availability zone to ' - 'use for this loadbalancer.', - 'name': constants.COMPUTE_ZONE}] + ref_capabilities = [ + {'description': 'The compute availability zone to ' + 'use for this loadbalancer.', + 'name': constants.COMPUTE_ZONE}, + {'description': 'The volume availability zone to ' + 'use for this loadbalancer.', + 'name': constants.VOLUME_ZONE}, + ] result = self.get( self.AVAILABILITY_ZONE_CAPABILITIES_PATH.format( @@ -220,9 +225,14 @@ class TestAvailabilityZoneCapabilities(base.BaseAPITest): provider='noop_driver'), status=501) def test_authorized(self): - ref_capabilities = [{'description': 'The compute availability zone to ' - 'use for this loadbalancer.', - 'name': constants.COMPUTE_ZONE}] + ref_capabilities = [ + {'description': 'The compute availability zone to ' + 'use for this loadbalancer.', + 'name': constants.COMPUTE_ZONE}, + {'description': 'The volume availability zone to ' + 'use for this loadbalancer.', + 'name': constants.VOLUME_ZONE}, + ] 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) diff --git a/octavia/tests/unit/api/drivers/amphora_driver/v2/test_driver.py b/octavia/tests/unit/api/drivers/amphora_driver/v2/test_driver.py index 0fea5182f7..9bbb88e938 100644 --- a/octavia/tests/unit/api/drivers/amphora_driver/v2/test_driver.py +++ b/octavia/tests/unit/api/drivers/amphora_driver/v2/test_driver.py @@ -853,6 +853,16 @@ class TestAmphoraDriver(base.TestRpc): m_driver.validate_availability_zone.return_value = None ref_dict = {consts.COMPUTE_ZONE: 'my_compute_zone'} self.amp_driver.validate_availability_zone(ref_dict) + m_driver.validate_availability_zone.assert_called_once_with( + 'my_compute_zone') + + # Test volume zone + with mock.patch('stevedore.driver.DriverManager.driver') as m_driver: + m_driver.validate_availability_zone.return_value = None + ref_dict = {consts.VOLUME_ZONE: 'my_volume_zone'} + self.amp_driver.validate_availability_zone(ref_dict) + m_driver.validate_availability_zone.assert_called_once_with( + 'my_volume_zone') with mock.patch('octavia.common.utils.get_network_driver') as m_driver: # Test vip networks diff --git a/octavia/tests/unit/api/drivers/noop_driver/test_driver.py b/octavia/tests/unit/api/drivers/noop_driver/test_driver.py index 1023153395..cc123be4df 100644 --- a/octavia/tests/unit/api/drivers/noop_driver/test_driver.py +++ b/octavia/tests/unit/api/drivers/noop_driver/test_driver.py @@ -145,7 +145,9 @@ class TestNoopProviderDriver(base.TestCase): "to use for this load balancer."} self.ref_availability_zone_metadata = { "compute_zone": "The compute availability zone to use for this " - "loadbalancer."} + "loadbalancer.", + "volume_zone": "The volume availability zone to use for this " + "loadbalancer."} def test_create_vip_port(self): vip_dict, additional_vip_dicts = self.driver.create_vip_port( diff --git a/octavia/tests/unit/compute/drivers/test_nova_driver.py b/octavia/tests/unit/compute/drivers/test_nova_driver.py index d1670f05c4..dff64d1d43 100644 --- a/octavia/tests/unit/compute/drivers/test_nova_driver.py +++ b/octavia/tests/unit/compute/drivers/test_nova_driver.py @@ -144,7 +144,7 @@ class TestNovaClient(base.TestCase): config_drive_files='Files Blah') self.assertEqual(self.amphora.compute_id, amphora_id) - mock_driver.create_volume_from_image.assert_called_with(1) + mock_driver.create_volume_from_image.assert_called_with(1, None) self.manager.manager.create.assert_called_with( name="amphora_name", nics=[{'net-id': 1}, {'port-id': 2}], @@ -161,7 +161,9 @@ class TestNovaClient(base.TestCase): ) def test_build_with_availability_zone(self): - FAKE_AZ = "my_availability_zone" + FAKE_AZ_NAME = "my_availability_zone" + self.conf.config(group="nova", availability_zone=FAKE_AZ_NAME) + FAKE_AZ = {constants.COMPUTE_ZONE: FAKE_AZ_NAME} amphora_id = self.manager.build(amphora_flavor=1, image_tag='malt', key_name=1, @@ -185,10 +187,50 @@ class TestNovaClient(base.TestCase): userdata='Blah', config_drive=True, scheduler_hints=None, - availability_zone=FAKE_AZ, + availability_zone=FAKE_AZ_NAME, block_device_mapping={} ) + @mock.patch('stevedore.driver.DriverManager.driver') + def test_build_with_availability_zone_and_volume(self, mock_driver): + FAKE_AZ_NAME = "my_availability_zone" + self.conf.config(group="controller_worker", + volume_driver='volume_cinder_driver') + self.conf.config(group="nova", availability_zone=FAKE_AZ_NAME) + self.conf.config(group="cinder", availability_zone=FAKE_AZ_NAME) + FAKE_AZ = {constants.COMPUTE_ZONE: FAKE_AZ_NAME, + constants.VOLUME_ZONE: FAKE_AZ_NAME} + + self.manager.volume_driver = mock_driver + mock_driver.create_volume_from_image.return_value = 1 + + amphora_id = self.manager.build(amphora_flavor=1, image_tag='pilsner', + key_name=1, + sec_groups=1, + network_ids=[1], + port_ids=[2], + user_data='Blah', + config_drive_files='Files Blah', + availability_zone=FAKE_AZ) + + self.assertEqual(self.amphora.compute_id, amphora_id) + + self.manager.manager.create.assert_called_with( + name="amphora_name", + nics=[{'net-id': 1}, {'port-id': 2}], + image=None, + flavor=1, + key_name=1, + security_groups=1, + files='Files Blah', + userdata='Blah', + config_drive=True, + scheduler_hints=None, + availability_zone=FAKE_AZ_NAME, + block_device_mapping={'vda': '1:::true'} + ) + mock_driver.create_volume_from_image.assert_called_with(1, FAKE_AZ) + def test_build_with_availability_zone_config(self): FAKE_AZ = "my_availability_zone" self.conf.config(group="nova", availability_zone=FAKE_AZ) diff --git a/octavia/tests/unit/controller/worker/v2/tasks/test_compute_tasks.py b/octavia/tests/unit/controller/worker/v2/tasks/test_compute_tasks.py index 28ace32755..2c688fd5c4 100644 --- a/octavia/tests/unit/controller/worker/v2/tasks/test_compute_tasks.py +++ b/octavia/tests/unit/controller/worker/v2/tasks/test_compute_tasks.py @@ -269,7 +269,7 @@ class TestComputeTasks(base.TestCase): '/etc/rsyslog.d/10-rsyslog.conf': 'FAKE CFG'}, user_data='user_data_conf', server_group_id=SERVER_GRPOUP_ID, - availability_zone=compute_zone) + availability_zone=az_dict) # Make sure it returns the expected compute_id self.assertEqual(COMPUTE_ID, compute_id) diff --git a/octavia/tests/unit/volume/drivers/noop_driver/test_driver.py b/octavia/tests/unit/volume/drivers/noop_driver/test_driver.py index 49087c6062..de6858cef7 100644 --- a/octavia/tests/unit/volume/drivers/noop_driver/test_driver.py +++ b/octavia/tests/unit/volume/drivers/noop_driver/test_driver.py @@ -13,10 +13,10 @@ from oslo_config import cfg from oslo_utils import uuidutils +from octavia.common import constants import octavia.tests.unit.base as base from octavia.volume.drivers.noop_driver import driver - CONF = cfg.CONF @@ -33,7 +33,16 @@ class TestNoopVolumeDriver(base.TestCase): def test_create_volume_from_image(self): self.driver.create_volume_from_image(self.image_id) - self.assertEqual((self.image_id, 'create_volume_from_image'), + self.assertEqual((self.image_id, None, 'create_volume_from_image'), + self.driver.driver.volumeconfig[( + self.image_id + )]) + + def test_create_volume_from_image_with_availability_zone(self): + az_name = "some_az" + az_data = {constants.VOLUME_ZONE: az_name} + self.driver.create_volume_from_image(self.image_id, az_data) + self.assertEqual((self.image_id, az_data, 'create_volume_from_image'), self.driver.driver.volumeconfig[( self.image_id )]) diff --git a/octavia/tests/unit/volume/drivers/test_cinder_driver.py b/octavia/tests/unit/volume/drivers/test_cinder_driver.py index b23c343add..d32d1eb025 100644 --- a/octavia/tests/unit/volume/drivers/test_cinder_driver.py +++ b/octavia/tests/unit/volume/drivers/test_cinder_driver.py @@ -16,6 +16,7 @@ from oslo_config import cfg from oslo_config import fixture as oslo_fixture from oslo_utils import uuidutils +from octavia.common import constants from octavia.common import exceptions import octavia.tests.unit.base as base import octavia.volume.drivers.cinder_driver as cinder_common @@ -42,6 +43,7 @@ class TestCinderClient(base.TestCase): self.manager.manager.get.return_value.status = 'available' self.manager.manager.create.return_value = self.cinder_response + self.manager.availability_zone_manager = mock.MagicMock() self.image_id = fake_uuid2 self.volume_id = fake_uuid3 @@ -58,6 +60,36 @@ class TestCinderClient(base.TestCase): availability_zone=None, imageRef=self.image_id) + def test_create_volume_from_image_with_availability_zone(self): + self.conf.config(group="controller_worker", + volume_driver='volume_cinder_driver') + self.conf.config(group="cinder", volume_create_retry_interval=0) + self.conf.config(group="cinder", availability_zone="no_zone") + + az_name = "some_zone" + az_data = {constants.VOLUME_ZONE: az_name} + + self.manager.create_volume_from_image(self.image_id, az_data) + self.manager.manager.create.assert_called_with( + size=16, + volume_type=None, + availability_zone=az_name, + imageRef=self.image_id) + + def test_create_volume_from_image_with_availability_zone_from_conf(self): + az_name = "some_az" + self.conf.config(group="controller_worker", + volume_driver='volume_cinder_driver') + self.conf.config(group="cinder", volume_create_retry_interval=0) + self.conf.config(group="cinder", availability_zone=az_name) + + self.manager.create_volume_from_image(self.image_id) + self.manager.manager.create.assert_called_with( + size=16, + volume_type=None, + availability_zone=az_name, + imageRef=self.image_id) + def test_create_volume_from_image_error(self): self.conf.config(group="controller_worker", volume_driver='volume_cinder_driver') @@ -97,3 +129,18 @@ class TestCinderClient(base.TestCase): self.assertRaises(exceptions.VolumeGetException, self.manager.get_image_from_volume, self.volume_id) + + def test_validate_availability_zone(self): + az_name = "some_az" + mock_az = mock.Mock() + mock_az.zoneName = az_name + self.manager.availability_zone_manager.list.return_value = [mock_az] + self.manager.validate_availability_zone(az_name) + self.manager.availability_zone_manager.list.assert_called_with( + detailed=False) + + def test_validate_availability_zone_with_exception(self): + self.manager.availability_zone_manager.list.return_value = [] + self.assertRaises(exceptions.InvalidSubresource, + self.manager.validate_availability_zone, + "bogus") diff --git a/octavia/volume/drivers/cinder_driver.py b/octavia/volume/drivers/cinder_driver.py index 5244844ffd..ec681746a7 100644 --- a/octavia/volume/drivers/cinder_driver.py +++ b/octavia/volume/drivers/cinder_driver.py @@ -43,20 +43,29 @@ class VolumeManager(volume_base.VolumeBase): cacert=CONF.cinder.ca_certificates_file ) self.manager = self._cinder_client.volumes + self.availability_zone_manager = self._cinder_client.availability_zones @retry(reraise=True, stop=stop_after_attempt(CONF.cinder.volume_create_max_retries)) - def create_volume_from_image(self, image_id): + def create_volume_from_image(self, image_id, availability_zone=None): """Create cinder volume :param image_id: ID of amphora image + :param availability_zone: Availability zone data dict :return volume id """ + + if availability_zone: + az_name = availability_zone.get( + constants.VOLUME_ZONE, CONF.cinder.availability_zone) + else: + az_name = CONF.cinder.availability_zone + volume = self.manager.create( size=CONF.cinder.volume_size, volume_type=CONF.cinder.volume_type, - availability_zone=CONF.cinder.availability_zone, + availability_zone=az_name, imageRef=image_id) resource_status = self.manager.get(volume.id).status @@ -121,3 +130,24 @@ class VolumeManager(volume_base.VolumeBase): LOG.error("Volume %s has no image metadata", volume_id) image_id = None return image_id + + def validate_availability_zone(self, availability_zone): + """Validates that an availability zone exists in cinder. + + :param availability_zone: Name of the availability zone to lookup. + :raises: NotFound + :returns: None + """ + try: + volume_zones = [ + a.zoneName for a in self.availability_zone_manager.list( + detailed=False)] + if availability_zone not in volume_zones: + LOG.info('Availability zone %s was not found in cinder. %s', + availability_zone, volume_zones) + raise exceptions.InvalidSubresource( + resource='Cinder availability zone', id=availability_zone) + except Exception as e: + LOG.exception('Cinder reports a failure getting listing ' + 'availability zones: %s', str(e)) + raise diff --git a/octavia/volume/drivers/noop_driver/driver.py b/octavia/volume/drivers/noop_driver/driver.py index 65b2821064..32502d835e 100644 --- a/octavia/volume/drivers/noop_driver/driver.py +++ b/octavia/volume/drivers/noop_driver/driver.py @@ -23,10 +23,12 @@ class NoopManager: super().__init__() self.volumeconfig = {} - def create_volume_from_image(self, image_id): - LOG.debug("Volume %s no-op, image id %s", - self.__class__.__name__, image_id) - self.volumeconfig[image_id] = (image_id, 'create_volume_from_image') + def create_volume_from_image(self, image_id, availability_zone=None): + LOG.debug("Volume %s no-op, image id %s, availability zone %s", + self.__class__.__name__, image_id, availability_zone) + self.volumeconfig[image_id] = (image_id, + availability_zone, + 'create_volume_from_image') volume_id = uuidutils.generate_uuid() return volume_id @@ -42,14 +44,21 @@ class NoopManager: image_id = uuidutils.generate_uuid() return image_id + def validate_availability_zone(self, availability_zone): + LOG.debug("Volume %s no-op, validate_availability_zone name %s", + self.__class__.__name__, availability_zone) + self.volumeconfig[availability_zone] = ( + availability_zone, 'validate_availability_zone') + class NoopVolumeDriver(driver_base.VolumeBase): def __init__(self): super().__init__() self.driver = NoopManager() - def create_volume_from_image(self, image_id): - volume_id = self.driver.create_volume_from_image(image_id) + def create_volume_from_image(self, image_id, availability_zone=None): + volume_id = self.driver.create_volume_from_image( + image_id, availability_zone) return volume_id def delete_volume(self, volume_id): @@ -58,3 +67,6 @@ class NoopVolumeDriver(driver_base.VolumeBase): def get_image_from_volume(self, volume_id): image_id = self.driver.get_image_from_volume(volume_id) return image_id + + def validate_availability_zone(self, availability_zone): + self.driver.validate_availability_zone(availability_zone) diff --git a/octavia/volume/volume_base.py b/octavia/volume/volume_base.py index d71e8d01d6..f164c56671 100644 --- a/octavia/volume/volume_base.py +++ b/octavia/volume/volume_base.py @@ -18,10 +18,11 @@ import abc class VolumeBase(metaclass=abc.ABCMeta): @abc.abstractmethod - def create_volume_from_image(self, image_id): + def create_volume_from_image(self, image_id, availability_zone=None): """Create volume for instance :param image_id: ID of amphora image + :param availability_zone: Availability zone data dict :return volume id """ @@ -41,3 +42,13 @@ class VolumeBase(metaclass=abc.ABCMeta): :return image id """ + + @abc.abstractmethod + def validate_availability_zone(self, availability_zone): + """Validates that a volume availability zone exists. + + :param availability_zone: Name of the availability zone to lookup. + :returns: None + :raises: NotFound + :raises: NotImplementedError + """ diff --git a/releasenotes/notes/add_volume_avalability_zone-cc03ba448960bef4.yaml b/releasenotes/notes/add_volume_avalability_zone-cc03ba448960bef4.yaml new file mode 100644 index 0000000000..030a477da4 --- /dev/null +++ b/releasenotes/notes/add_volume_avalability_zone-cc03ba448960bef4.yaml @@ -0,0 +1,6 @@ +--- + +features: + - | + Added volume zone to availability zone profile for amphorae backend for + creating loadbalancer with specific volume availability zone