Merge "Fix Cloud Server image/flavor combination validation"
This commit is contained in:
commit
8a276a4483
@ -68,6 +68,7 @@ class CloudServer(server.Server):
|
||||
'general1', 'memory1', 'performance2', 'performance1',
|
||||
'standard1', 'io1', 'onmetal', 'compute1',
|
||||
)
|
||||
BASE_IMAGE_REF = 'base_image_ref'
|
||||
|
||||
# flavor classes that can be booted ONLY from volume
|
||||
BFV_VOLUME_REQUIRED = {MEMORY1, COMPUTE1}
|
||||
@ -239,8 +240,22 @@ class CloudServer(server.Server):
|
||||
|
||||
return self._extend_networks(nets)
|
||||
|
||||
def _image_flavor_class_match(self, flavor_type, image_obj):
|
||||
flavor_class_string = image_obj.get(self.FLAVOR_CLASSES_KEY, '')
|
||||
def _base_image_obj(self, image):
|
||||
image_obj = self.client_plugin('glance').get_image(image)
|
||||
if self.BASE_IMAGE_REF in image_obj:
|
||||
base_image = image_obj[self.BASE_IMAGE_REF]
|
||||
return self.client_plugin('glance').get_image(base_image)
|
||||
return image_obj
|
||||
|
||||
def _image_flavor_class_match(self, flavor_type, image):
|
||||
base_image_obj = self._base_image_obj(image)
|
||||
flavor_class_string = base_image_obj.get(self.FLAVOR_CLASSES_KEY)
|
||||
|
||||
# If the flavor_class_string metadata does not exist or is
|
||||
# empty, do not validate image/flavor combo
|
||||
if not flavor_class_string:
|
||||
return True
|
||||
|
||||
flavor_class_excluded = "!{0}".format(flavor_type)
|
||||
flavor_classes_accepted = flavor_class_string.split(',')
|
||||
|
||||
@ -273,9 +288,7 @@ class CloudServer(server.Server):
|
||||
# is all the validation possible
|
||||
return
|
||||
|
||||
image_obj = self.client_plugin('glance').get_image(image)
|
||||
|
||||
if not self._image_flavor_class_match(flavor_type, image_obj):
|
||||
if not self._image_flavor_class_match(flavor_type, image):
|
||||
msg = _('Flavor %(flavor)s cannot be used with image '
|
||||
'%(image)s.') % {'image': image, 'flavor': flavor}
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
@ -545,6 +545,7 @@ class CloudServersValidationTests(common.HeatTestCase):
|
||||
|
||||
mock_image = mock.Mock(status='ACTIVE', min_ram=2, min_disk=1)
|
||||
mock_image.get.return_value = "memory1"
|
||||
mock_image.__iter__ = mock.Mock(return_value=iter([]))
|
||||
|
||||
mock_plugin().get_flavor.return_value = mock_flavor
|
||||
mock_plugin().get_image.return_value = mock_image
|
||||
@ -564,6 +565,7 @@ class CloudServersValidationTests(common.HeatTestCase):
|
||||
|
||||
mock_image = mock.Mock(status='ACTIVE', min_ram=2, min_disk=1)
|
||||
mock_image.get.return_value = "!standard1, *"
|
||||
mock_image.__iter__ = mock.Mock(return_value=iter([]))
|
||||
|
||||
mock_flavor = mock.Mock(ram=4, disk=4)
|
||||
mock_flavor.to_dict.return_value = {
|
||||
@ -588,6 +590,7 @@ class CloudServersValidationTests(common.HeatTestCase):
|
||||
|
||||
mock_image = mock.Mock(size=1, status='ACTIVE', min_ram=2, min_disk=2)
|
||||
mock_image.get.return_value = "standard1"
|
||||
mock_image.__iter__ = mock.Mock(return_value=iter([]))
|
||||
|
||||
mock_flavor = mock.Mock(ram=4, disk=4)
|
||||
mock_flavor.to_dict.return_value = {
|
||||
@ -601,3 +604,60 @@ class CloudServersValidationTests(common.HeatTestCase):
|
||||
mock_plugin().get_image.return_value = mock_image
|
||||
|
||||
self.assertIsNone(server.validate())
|
||||
|
||||
def test_validate_image_flavor_empty_metadata(self, mock_client,
|
||||
mock_plugin):
|
||||
server = cloud_server.CloudServer("test", self.rsrcdef, self.mockstack)
|
||||
|
||||
mock_image = mock.Mock(size=1, status='ACTIVE', min_ram=2, min_disk=2)
|
||||
mock_image.get.return_value = ""
|
||||
mock_image.__iter__ = mock.Mock(return_value=iter([]))
|
||||
|
||||
mock_flavor = mock.Mock(ram=4, disk=4)
|
||||
mock_flavor.to_dict.return_value = {
|
||||
'OS-FLV-WITH-EXT-SPECS:extra_specs': {
|
||||
'flavor_classes': '',
|
||||
},
|
||||
}
|
||||
|
||||
mock_plugin().get_flavor.return_value = mock_flavor
|
||||
mock_plugin().get_image.return_value = mock_image
|
||||
|
||||
self.assertIsNone(server.validate())
|
||||
|
||||
def test_validate_image_flavor_no_metadata(self, mock_client, mock_plugin):
|
||||
server = cloud_server.CloudServer("test", self.rsrcdef, self.mockstack)
|
||||
|
||||
mock_image = mock.Mock(size=1, status='ACTIVE', min_ram=2, min_disk=2)
|
||||
mock_image.get.return_value = None
|
||||
mock_image.__iter__ = mock.Mock(return_value=iter([]))
|
||||
|
||||
mock_flavor = mock.Mock(ram=4, disk=4)
|
||||
mock_flavor.to_dict.return_value = {}
|
||||
|
||||
mock_plugin().get_flavor.return_value = mock_flavor
|
||||
mock_plugin().get_image.return_value = mock_image
|
||||
|
||||
self.assertIsNone(server.validate())
|
||||
|
||||
def test_validate_image_flavor_not_base(self, mock_client, mock_plugin):
|
||||
server = cloud_server.CloudServer("test", self.rsrcdef, self.mockstack)
|
||||
|
||||
mock_image = mock.Mock(size=1, status='ACTIVE', min_ram=2, min_disk=2)
|
||||
mock_image.get.return_value = None
|
||||
mock_image.__iter__ = mock.Mock(return_value=iter(
|
||||
['base_image_ref']))
|
||||
mock_image.__getitem__ = mock.Mock(return_value='1234')
|
||||
|
||||
mock_base_image = mock.Mock(size=1, status='ACTIVE', min_ram=2,
|
||||
min_disk=2)
|
||||
mock_base_image.get.return_value = None
|
||||
mock_base_image.__iter__ = mock.Mock(return_value=iter([]))
|
||||
|
||||
mock_flavor = mock.Mock(ram=4, disk=4)
|
||||
mock_flavor.to_dict.return_value = {}
|
||||
|
||||
mock_plugin().get_flavor.return_value = mock_flavor
|
||||
mock_plugin().get_image.side_effect = [mock_image, mock_base_image]
|
||||
|
||||
self.assertIsNone(server.validate())
|
||||
|
Loading…
Reference in New Issue
Block a user