Reject boot request for unsupported images
Nova has never supported direct booting of an image of an encrypted volume uploaded to Glance via the Cinder upload-volume-to-image process, but instead of rejecting such a request, an 'active' but unusable instance is created. This patch allows Nova to use image metadata to detect such an image and reject the boot request. Change-Id: Idf84ccff254d26fa13473fe9741ddac21cbcf321 Related-bug: #1852106 Closes-bug: #1863611 (cherry picked from commit963fd8c0f9
) (cherry picked from commit240d030902
) (cherry picked from commit6e71909b0b
) (cherry picked from commit02551d6d6a
)
This commit is contained in:
parent
8290f746d8
commit
418b9450e4
|
@ -582,6 +582,7 @@ class ServersController(wsgi.Controller):
|
||||||
exception.ImageNotActive,
|
exception.ImageNotActive,
|
||||||
exception.ImageBadRequest,
|
exception.ImageBadRequest,
|
||||||
exception.ImageNotAuthorized,
|
exception.ImageNotAuthorized,
|
||||||
|
exception.ImageUnacceptable,
|
||||||
exception.FixedIpNotFoundForAddress,
|
exception.FixedIpNotFoundForAddress,
|
||||||
exception.FlavorNotFound,
|
exception.FlavorNotFound,
|
||||||
exception.FlavorDiskTooSmall,
|
exception.FlavorDiskTooSmall,
|
||||||
|
|
|
@ -557,6 +557,31 @@ class API(base.Base):
|
||||||
# reason, we rely on the DB to cast True to a String.
|
# reason, we rely on the DB to cast True to a String.
|
||||||
return True if bool_val else ''
|
return True if bool_val else ''
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _detect_nonbootable_image_from_properties(image_id, image):
|
||||||
|
"""Check image for a property indicating it's nonbootable.
|
||||||
|
|
||||||
|
This is called from the API service to ensure that there are
|
||||||
|
no known image properties indicating that this image is of a
|
||||||
|
type that we do not support booting from.
|
||||||
|
|
||||||
|
Currently the only such property is 'cinder_encryption_key_id'.
|
||||||
|
|
||||||
|
:param image_id: UUID of the image
|
||||||
|
:param image: a dict representation of the image including properties
|
||||||
|
:raises: ImageUnacceptable if the image properties indicate
|
||||||
|
that booting this image is not supported
|
||||||
|
"""
|
||||||
|
if not image:
|
||||||
|
return
|
||||||
|
|
||||||
|
image_properties = image.get('properties', {})
|
||||||
|
if image_properties.get('cinder_encryption_key_id'):
|
||||||
|
reason = _('Direct booting of an image uploaded from an '
|
||||||
|
'encrypted volume is unsupported.')
|
||||||
|
raise exception.ImageUnacceptable(image_id=image_id,
|
||||||
|
reason=reason)
|
||||||
|
|
||||||
def _check_requested_image(self, context, image_id, image,
|
def _check_requested_image(self, context, image_id, image,
|
||||||
instance_type, root_bdm):
|
instance_type, root_bdm):
|
||||||
if not image:
|
if not image:
|
||||||
|
@ -773,6 +798,7 @@ class API(base.Base):
|
||||||
self._check_injected_file_quota(context, files_to_inject)
|
self._check_injected_file_quota(context, files_to_inject)
|
||||||
self._check_requested_image(context, image_id, image,
|
self._check_requested_image(context, image_id, image,
|
||||||
instance_type, root_bdm)
|
instance_type, root_bdm)
|
||||||
|
self._detect_nonbootable_image_from_properties(image_id, image)
|
||||||
|
|
||||||
def _validate_and_build_base_options(self, context, instance_type,
|
def _validate_and_build_base_options(self, context, instance_type,
|
||||||
boot_meta, image_href, image_id,
|
boot_meta, image_href, image_id,
|
||||||
|
|
|
@ -3050,6 +3050,22 @@ class ServersControllerCreateTest(test.TestCase):
|
||||||
"Flavor's disk is too small for requested image."):
|
"Flavor's disk is too small for requested image."):
|
||||||
self.controller.create(self.req, body=self.body)
|
self.controller.create(self.req, body=self.body)
|
||||||
|
|
||||||
|
@mock.patch.object(fake._FakeImageService, 'show',
|
||||||
|
return_value=dict(
|
||||||
|
id='76fa36fc-c930-4bf3-8c8a-ea2a2420deb6',
|
||||||
|
status='active',
|
||||||
|
properties=dict(
|
||||||
|
cinder_encryption_key_id=fakes.FAKE_UUID)))
|
||||||
|
def test_create_server_image_nonbootable(self, mock_show):
|
||||||
|
self.req.body = jsonutils.dump_as_bytes(self.body)
|
||||||
|
|
||||||
|
expected_msg = ("Image {} is unacceptable: Direct booting of an image "
|
||||||
|
"uploaded from an encrypted volume is unsupported.")
|
||||||
|
with testtools.ExpectedException(
|
||||||
|
webob.exc.HTTPBadRequest,
|
||||||
|
expected_msg.format(self.image_uuid)):
|
||||||
|
self.controller.create(self.req, body=self.body)
|
||||||
|
|
||||||
def test_create_instance_with_image_non_uuid(self):
|
def test_create_instance_with_image_non_uuid(self):
|
||||||
self.body['server']['imageRef'] = 'not-uuid'
|
self.body['server']['imageRef'] = 'not-uuid'
|
||||||
self.assertRaises(exception.ValidationError,
|
self.assertRaises(exception.ValidationError,
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
The Compute service has never supported direct booting of an instance from
|
||||||
|
an image that was created by the Block Storage service from an encrypted
|
||||||
|
volume. Previously, this operation would result in an ACTIVE instance that
|
||||||
|
was unusable. Beginning with this release, an attempt to boot from such an
|
||||||
|
image will result in the Compute API returning a 400 (Bad Request)
|
||||||
|
response.
|
Loading…
Reference in New Issue