Merge "Check type match on create from source/snap"
This commit is contained in:
		@@ -956,10 +956,14 @@ class VolumeTestCase(BaseVolumeTestCase):
 | 
				
			|||||||
        """Test volume create from snapshot with types including mistmatch."""
 | 
					        """Test volume create from snapshot with types including mistmatch."""
 | 
				
			||||||
        volume_api = cinder.volume.api.API()
 | 
					        volume_api = cinder.volume.api.API()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        db.volume_type_create(context.get_admin_context(),
 | 
					        db.volume_type_create(
 | 
				
			||||||
                              {'name': 'foo', 'extra_specs': {}})
 | 
					            context.get_admin_context(),
 | 
				
			||||||
        db.volume_type_create(context.get_admin_context(),
 | 
					            {'name': 'foo',
 | 
				
			||||||
                              {'name': 'biz', 'extra_specs': {}})
 | 
					             'extra_specs': {'volume_backend_name': 'dev_1'}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db.volume_type_create(
 | 
				
			||||||
 | 
					            context.get_admin_context(),
 | 
				
			||||||
 | 
					            {'name': 'biz', 'extra_specs': {'volume_backend_name': 'dev_2'}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foo_type = db.volume_type_get_by_name(context.get_admin_context(),
 | 
					        foo_type = db.volume_type_get_by_name(context.get_admin_context(),
 | 
				
			||||||
                                              'foo')
 | 
					                                              'foo')
 | 
				
			||||||
@@ -973,6 +977,9 @@ class VolumeTestCase(BaseVolumeTestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Make sure the case of specifying a type that
 | 
					        # Make sure the case of specifying a type that
 | 
				
			||||||
        # doesn't match the snapshots type fails
 | 
					        # doesn't match the snapshots type fails
 | 
				
			||||||
 | 
					        with mock.patch.object(cinder.volume.volume_types,
 | 
				
			||||||
 | 
					                               'get_volume_type') as mock_get_type:
 | 
				
			||||||
 | 
					            mock_get_type.return_value = biz_type
 | 
				
			||||||
            self.assertRaises(exception.InvalidInput,
 | 
					            self.assertRaises(exception.InvalidInput,
 | 
				
			||||||
                              volume_api.create,
 | 
					                              volume_api.create,
 | 
				
			||||||
                              self.context,
 | 
					                              self.context,
 | 
				
			||||||
@@ -1009,10 +1016,14 @@ class VolumeTestCase(BaseVolumeTestCase):
 | 
				
			|||||||
        """Test volume create from source with types including mistmatch."""
 | 
					        """Test volume create from source with types including mistmatch."""
 | 
				
			||||||
        volume_api = cinder.volume.api.API()
 | 
					        volume_api = cinder.volume.api.API()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        db.volume_type_create(context.get_admin_context(),
 | 
					        db.volume_type_create(
 | 
				
			||||||
                              {'name': 'foo', 'extra_specs': {}})
 | 
					            context.get_admin_context(),
 | 
				
			||||||
        db.volume_type_create(context.get_admin_context(),
 | 
					            {'name': 'foo',
 | 
				
			||||||
                              {'name': 'biz', 'extra_specs': {}})
 | 
					             'extra_specs': {'volume_backend_name': 'dev_1'}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        db.volume_type_create(
 | 
				
			||||||
 | 
					            context.get_admin_context(),
 | 
				
			||||||
 | 
					            {'name': 'biz', 'extra_specs': {'volume_backend_name': 'dev_2'}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foo_type = db.volume_type_get_by_name(context.get_admin_context(),
 | 
					        foo_type = db.volume_type_get_by_name(context.get_admin_context(),
 | 
				
			||||||
                                              'foo')
 | 
					                                              'foo')
 | 
				
			||||||
@@ -1025,8 +1036,9 @@ class VolumeTestCase(BaseVolumeTestCase):
 | 
				
			|||||||
                      'volume_type': biz_type,
 | 
					                      'volume_type': biz_type,
 | 
				
			||||||
                      'volume_type_id': biz_type['id']}
 | 
					                      'volume_type_id': biz_type['id']}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Make sure the case of specifying a type that
 | 
					        with mock.patch.object(cinder.volume.volume_types,
 | 
				
			||||||
        # doesn't match the snapshots type fails
 | 
					                               'get_volume_type') as mock_get_type:
 | 
				
			||||||
 | 
					            mock_get_type.return_value = biz_type
 | 
				
			||||||
            self.assertRaises(exception.InvalidInput,
 | 
					            self.assertRaises(exception.InvalidInput,
 | 
				
			||||||
                              volume_api.create,
 | 
					                              volume_api.create,
 | 
				
			||||||
                              self.context,
 | 
					                              self.context,
 | 
				
			||||||
@@ -1060,6 +1072,111 @@ class VolumeTestCase(BaseVolumeTestCase):
 | 
				
			|||||||
            db.volume_type_destroy(context.get_admin_context(),
 | 
					            db.volume_type_destroy(context.get_admin_context(),
 | 
				
			||||||
                                   biz_type['id'])
 | 
					                                   biz_type['id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mock.patch('cinder.volume.flows.api.create_volume.get_flow')
 | 
				
			||||||
 | 
					    def test_create_volume_from_source_with_same_backend(self, _get_flow):
 | 
				
			||||||
 | 
					        """Test volume create from source with type mismatch same backend."""
 | 
				
			||||||
 | 
					        volume_api = cinder.volume.api.API()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foo_type = {
 | 
				
			||||||
 | 
					            'name': 'foo',
 | 
				
			||||||
 | 
					            'qos_specs_id': None,
 | 
				
			||||||
 | 
					            'deleted': False,
 | 
				
			||||||
 | 
					            'created_at': datetime.datetime(2015, 5, 8, 0, 40, 5, 408232),
 | 
				
			||||||
 | 
					            'updated_at': None,
 | 
				
			||||||
 | 
					            'extra_specs': {'volume_backend_name': 'dev_1'},
 | 
				
			||||||
 | 
					            'is_public': True,
 | 
				
			||||||
 | 
					            'deleted_at': None,
 | 
				
			||||||
 | 
					            'id': '29e43b50-2cd7-4d0c-8ddd-2119daab3a38',
 | 
				
			||||||
 | 
					            'description': None}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        biz_type = {
 | 
				
			||||||
 | 
					            'name': 'biz',
 | 
				
			||||||
 | 
					            'qos_specs_id': None,
 | 
				
			||||||
 | 
					            'deleted': False,
 | 
				
			||||||
 | 
					            'created_at': datetime.datetime(2015, 5, 8, 0, 20, 5, 408232),
 | 
				
			||||||
 | 
					            'updated_at': None,
 | 
				
			||||||
 | 
					            'extra_specs': {'volume_backend_name': 'dev_1'},
 | 
				
			||||||
 | 
					            'is_public': True,
 | 
				
			||||||
 | 
					            'deleted_at': None,
 | 
				
			||||||
 | 
					            'id': '34e54c31-3bc8-5c1d-9fff-2225bcce4b59',
 | 
				
			||||||
 | 
					            'description': None}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        source_vol = {'id': 1234,
 | 
				
			||||||
 | 
					                      'status': 'available',
 | 
				
			||||||
 | 
					                      'volume_size': 10,
 | 
				
			||||||
 | 
					                      'volume_type': biz_type,
 | 
				
			||||||
 | 
					                      'volume_type_id': biz_type['id']}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with mock.patch.object(cinder.volume.volume_types,
 | 
				
			||||||
 | 
					                               'get_volume_type') as mock_get_type:
 | 
				
			||||||
 | 
					            mock_get_type.return_value = biz_type
 | 
				
			||||||
 | 
					            volume_api.create(self.context,
 | 
				
			||||||
 | 
					                              size=1,
 | 
				
			||||||
 | 
					                              name='fake_name',
 | 
				
			||||||
 | 
					                              description='fake_desc',
 | 
				
			||||||
 | 
					                              volume_type=foo_type,
 | 
				
			||||||
 | 
					                              source_volume=source_vol)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mock.patch('cinder.volume.flows.api.create_volume.get_flow')
 | 
				
			||||||
 | 
					    def test_create_from_source_and_snap_only_one_backend(self, _get_flow):
 | 
				
			||||||
 | 
					        """Test create from source and snap with type mismatch one backend."""
 | 
				
			||||||
 | 
					        volume_api = cinder.volume.api.API()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foo_type = {
 | 
				
			||||||
 | 
					            'name': 'foo',
 | 
				
			||||||
 | 
					            'qos_specs_id': None,
 | 
				
			||||||
 | 
					            'deleted': False,
 | 
				
			||||||
 | 
					            'created_at': datetime.datetime(2015, 5, 8, 0, 40, 5, 408232),
 | 
				
			||||||
 | 
					            'updated_at': None,
 | 
				
			||||||
 | 
					            'extra_specs': {'some_key': 3},
 | 
				
			||||||
 | 
					            'is_public': True,
 | 
				
			||||||
 | 
					            'deleted_at': None,
 | 
				
			||||||
 | 
					            'id': '29e43b50-2cd7-4d0c-8ddd-2119daab3a38',
 | 
				
			||||||
 | 
					            'description': None}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        biz_type = {
 | 
				
			||||||
 | 
					            'name': 'biz',
 | 
				
			||||||
 | 
					            'qos_specs_id': None,
 | 
				
			||||||
 | 
					            'deleted': False,
 | 
				
			||||||
 | 
					            'created_at': datetime.datetime(2015, 5, 8, 0, 20, 5, 408232),
 | 
				
			||||||
 | 
					            'updated_at': None,
 | 
				
			||||||
 | 
					            'extra_specs': {'some_other_key': 4},
 | 
				
			||||||
 | 
					            'is_public': True,
 | 
				
			||||||
 | 
					            'deleted_at': None,
 | 
				
			||||||
 | 
					            'id': '34e54c31-3bc8-5c1d-9fff-2225bcce4b59',
 | 
				
			||||||
 | 
					            'description': None}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        source_vol = {'id': 1234,
 | 
				
			||||||
 | 
					                      'status': 'available',
 | 
				
			||||||
 | 
					                      'volume_size': 10,
 | 
				
			||||||
 | 
					                      'volume_type': biz_type,
 | 
				
			||||||
 | 
					                      'volume_type_id': biz_type['id']}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        snapshot = {'id': 1234,
 | 
				
			||||||
 | 
					                    'status': 'available',
 | 
				
			||||||
 | 
					                    'volume_size': 10,
 | 
				
			||||||
 | 
					                    'volume_type_id': biz_type['id']}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with mock.patch.object(db,
 | 
				
			||||||
 | 
					                               'service_get_all_by_topic') as mock_get_service, \
 | 
				
			||||||
 | 
					            mock.patch.object(volume_api,
 | 
				
			||||||
 | 
					                              'list_availability_zones') as mock_get_azs:
 | 
				
			||||||
 | 
					            mock_get_service.return_value = ['foo']
 | 
				
			||||||
 | 
					            mock_get_azs.return_value = {}
 | 
				
			||||||
 | 
					            volume_api.create(self.context,
 | 
				
			||||||
 | 
					                              size=1,
 | 
				
			||||||
 | 
					                              name='fake_name',
 | 
				
			||||||
 | 
					                              description='fake_desc',
 | 
				
			||||||
 | 
					                              volume_type=foo_type,
 | 
				
			||||||
 | 
					                              source_volume=source_vol)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            volume_api.create(self.context,
 | 
				
			||||||
 | 
					                              size=1,
 | 
				
			||||||
 | 
					                              name='fake_name',
 | 
				
			||||||
 | 
					                              description='fake_desc',
 | 
				
			||||||
 | 
					                              volume_type=foo_type,
 | 
				
			||||||
 | 
					                              snapshot=snapshot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_snapshot_driver_not_initialized(self):
 | 
					    def test_create_snapshot_driver_not_initialized(self):
 | 
				
			||||||
        volume_src = tests_utils.create_volume(self.context,
 | 
					        volume_src = tests_utils.create_volume(self.context,
 | 
				
			||||||
                                               **self.volume_params)
 | 
					                                               **self.volume_params)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -165,6 +165,26 @@ class API(base.Base):
 | 
				
			|||||||
        LOG.info(_LI("Availability Zones retrieved successfully."))
 | 
					        LOG.info(_LI("Availability Zones retrieved successfully."))
 | 
				
			||||||
        return tuple(azs)
 | 
					        return tuple(azs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _retype_is_possible(self, context,
 | 
				
			||||||
 | 
					                            first_type_id, second_type_id,
 | 
				
			||||||
 | 
					                            first_type=None, second_type=None):
 | 
				
			||||||
 | 
					        safe = False
 | 
				
			||||||
 | 
					        if len(self.db.service_get_all_by_topic(context,
 | 
				
			||||||
 | 
					                                                'cinder-volume',
 | 
				
			||||||
 | 
					                                                disabled=True)) == 1:
 | 
				
			||||||
 | 
					            safe = True
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            type_a = first_type or volume_types.get_volume_type(
 | 
				
			||||||
 | 
					                context,
 | 
				
			||||||
 | 
					                first_type_id)
 | 
				
			||||||
 | 
					            type_b = second_type or volume_types.get_volume_type(
 | 
				
			||||||
 | 
					                context,
 | 
				
			||||||
 | 
					                second_type_id)
 | 
				
			||||||
 | 
					            if(volume_utils.matching_backend_name(type_a['extra_specs'],
 | 
				
			||||||
 | 
					                                                  type_b['extra_specs'])):
 | 
				
			||||||
 | 
					                safe = True
 | 
				
			||||||
 | 
					        return safe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create(self, context, size, name, description, snapshot=None,
 | 
					    def create(self, context, size, name, description, snapshot=None,
 | 
				
			||||||
               image_id=None, volume_type=None, metadata=None,
 | 
					               image_id=None, volume_type=None, metadata=None,
 | 
				
			||||||
               availability_zone=None, source_volume=None,
 | 
					               availability_zone=None, source_volume=None,
 | 
				
			||||||
@@ -202,9 +222,14 @@ class API(base.Base):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if source_volume and volume_type:
 | 
					        if source_volume and volume_type:
 | 
				
			||||||
            if volume_type['id'] != source_volume['volume_type_id']:
 | 
					            if volume_type['id'] != source_volume['volume_type_id']:
 | 
				
			||||||
 | 
					                if not self._retype_is_possible(
 | 
				
			||||||
 | 
					                        context,
 | 
				
			||||||
 | 
					                        volume_type['id'],
 | 
				
			||||||
 | 
					                        source_volume['volume_type_id'],
 | 
				
			||||||
 | 
					                        volume_type):
 | 
				
			||||||
                    msg = _("Invalid volume_type provided: %s (requested type "
 | 
					                    msg = _("Invalid volume_type provided: %s (requested type "
 | 
				
			||||||
                        "must match source volume, "
 | 
					                            "is not compatible; either match source volume, "
 | 
				
			||||||
                        "or be omitted).") % volume_type
 | 
					                            "or omit type argument).") % volume_type['id']
 | 
				
			||||||
                    raise exception.InvalidInput(reason=msg)
 | 
					                    raise exception.InvalidInput(reason=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # When cloning replica (for testing), volume type must be omitted
 | 
					        # When cloning replica (for testing), volume type must be omitted
 | 
				
			||||||
@@ -215,9 +240,13 @@ class API(base.Base):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if snapshot and volume_type:
 | 
					        if snapshot and volume_type:
 | 
				
			||||||
            if volume_type['id'] != snapshot['volume_type_id']:
 | 
					            if volume_type['id'] != snapshot['volume_type_id']:
 | 
				
			||||||
 | 
					                if not self._retype_is_possible(context,
 | 
				
			||||||
 | 
					                                                volume_type['id'],
 | 
				
			||||||
 | 
					                                                snapshot['volume_type_id'],
 | 
				
			||||||
 | 
					                                                volume_type):
 | 
				
			||||||
                    msg = _("Invalid volume_type provided: %s (requested "
 | 
					                    msg = _("Invalid volume_type provided: %s (requested "
 | 
				
			||||||
                        "type must match source snapshot, or be "
 | 
					                            "type is not compatible; recommend omitting "
 | 
				
			||||||
                        "omitted).") % volume_type
 | 
					                            "the type argument).") % volume_type['id']
 | 
				
			||||||
                    raise exception.InvalidInput(reason=msg)
 | 
					                    raise exception.InvalidInput(reason=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Determine the valid availability zones that the volume could be
 | 
					        # Determine the valid availability zones that the volume could be
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -494,3 +494,12 @@ def append_host(host, pool):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    new_host = "#".join([host, pool])
 | 
					    new_host = "#".join([host, pool])
 | 
				
			||||||
    return new_host
 | 
					    return new_host
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def matching_backend_name(src_volume_type, volume_type):
 | 
				
			||||||
 | 
					    if src_volume_type.get('volume_backend_name') and \
 | 
				
			||||||
 | 
					            volume_type.get('volume_backend_name'):
 | 
				
			||||||
 | 
					        return src_volume_type.get('volume_backend_name') == \
 | 
				
			||||||
 | 
					            volume_type.get('volume_backend_name')
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user