summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-07-13 00:34:28 +0000
committerGerrit Code Review <review@openstack.org>2016-07-13 00:34:29 +0000
commit8a276a4483cf5f0cfce2ab3f4484289a910902f7 (patch)
tree670c17ad7f4947e698de4fbf643f044f1e20e375 /contrib
parentbcdf7a049fd374e0a5d9ed0b16285daa61ac56e1 (diff)
parentd8fefed1773133bbdf58a3392bafbc12f82e4fe2 (diff)
downloadheat-8a276a4483cf5f0cfce2ab3f4484289a910902f7.tar.gz
Merge "Fix Cloud Server image/flavor combination validation"
Diffstat (limited to 'contrib')
-rw-r--r--contrib/rackspace/rackspace/resources/cloud_server.py23
-rw-r--r--contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py60
2 files changed, 78 insertions, 5 deletions
diff --git a/contrib/rackspace/rackspace/resources/cloud_server.py b/contrib/rackspace/rackspace/resources/cloud_server.py
index 4583008d0..710a5835e 100644
--- a/contrib/rackspace/rackspace/resources/cloud_server.py
+++ b/contrib/rackspace/rackspace/resources/cloud_server.py
@@ -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)
diff --git a/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py b/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py
index 5cb1bb855..941b1f6f0 100644
--- a/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py
+++ b/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py
@@ -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())