Reject live migration and suspend on SEV guests
As per the spec[0], live migration and suspend are not (yet) supported for SEV guests, so reject them at the API level with an HTTP 409 (Conflict). [0] http://specs.openstack.org/openstack/nova-specs/specs/train/approved/amd-sev-libvirt-support.html#limitations blueprint: amd-sev-libvirt-support Change-Id: I69b6e153324a3e5680e096cd714e5d4dd05bae34
This commit is contained in:
parent
cedc850e4e
commit
84db8b3f3d
@ -171,6 +171,8 @@ class MigrateServerController(wsgi.Controller):
|
||||
"'%(ex)s'", {'ex': ex})
|
||||
else:
|
||||
raise exc.HTTPBadRequest(explanation=ex.format_message())
|
||||
except exception.OperationNotSupportedForSEV as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
except exception.InstanceIsLocked as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
except exception.ComputeHostNotFound as e:
|
||||
|
@ -38,7 +38,8 @@ class SuspendServerController(wsgi.Controller):
|
||||
target={'user_id': server.user_id,
|
||||
'project_id': server.project_id})
|
||||
self.compute_api.suspend(context, server)
|
||||
except exception.InstanceIsLocked as e:
|
||||
except (exception.OperationNotSupportedForSEV,
|
||||
exception.InstanceIsLocked) as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
except exception.InstanceInvalidState as state_error:
|
||||
common.raise_http_conflict_for_instance_invalid_state(state_error,
|
||||
|
@ -205,6 +205,24 @@ def check_instance_lock(function):
|
||||
return inner
|
||||
|
||||
|
||||
def reject_sev_instances(operation):
|
||||
"""Decorator. Raise OperationNotSupportedForSEV if instance has SEV
|
||||
enabled.
|
||||
"""
|
||||
|
||||
def outer(f):
|
||||
@six.wraps(f)
|
||||
def inner(self, context, instance, *args, **kw):
|
||||
if hardware.get_mem_encryption_constraint(instance.flavor,
|
||||
instance.image_meta):
|
||||
raise exception.OperationNotSupportedForSEV(
|
||||
instance_uuid=instance.uuid,
|
||||
operation=operation)
|
||||
return f(self, context, instance, *args, **kw)
|
||||
return inner
|
||||
return outer
|
||||
|
||||
|
||||
def _diff_dict(orig, new):
|
||||
"""Return a dict describing how to change orig to new. The keys
|
||||
correspond to values that have changed; the value will be a list
|
||||
@ -3785,6 +3803,7 @@ class API(base.Base):
|
||||
return self.compute_rpcapi.get_instance_diagnostics(context,
|
||||
instance=instance)
|
||||
|
||||
@reject_sev_instances(instance_actions.SUSPEND)
|
||||
@check_instance_lock
|
||||
@check_instance_state(vm_state=[vm_states.ACTIVE])
|
||||
def suspend(self, context, instance):
|
||||
@ -4448,6 +4467,7 @@ class API(base.Base):
|
||||
diff=diff)
|
||||
return _metadata
|
||||
|
||||
@reject_sev_instances(instance_actions.LIVE_MIGRATION)
|
||||
@check_instance_lock
|
||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED])
|
||||
def live_migrate(self, context, instance, block_migration,
|
||||
|
@ -533,6 +533,12 @@ class UnableToMigrateToSelf(Invalid):
|
||||
"to current host (%(host)s).")
|
||||
|
||||
|
||||
class OperationNotSupportedForSEV(NovaException):
|
||||
msg_fmt = _("Operation '%(operation)s' not supported for SEV-enabled "
|
||||
"instance (%(instance_uuid)s).")
|
||||
code = 409
|
||||
|
||||
|
||||
class InvalidHypervisorType(Invalid):
|
||||
msg_fmt = _("The supplied hypervisor type of is invalid.")
|
||||
|
||||
|
@ -609,6 +609,20 @@ class MigrateServerTestsV268(MigrateServerTestsV256):
|
||||
method_translations=method_translations,
|
||||
args_map=args_map)
|
||||
|
||||
@mock.patch('nova.virt.hardware.get_mem_encryption_constraint',
|
||||
new=mock.Mock(return_value=True))
|
||||
@mock.patch.object(objects.instance.Instance, 'image_meta')
|
||||
def test_live_migrate_sev_rejected(self, mock_image):
|
||||
instance = self._stub_instance_get()
|
||||
body = {'os-migrateLive': {'host': 'hostname',
|
||||
'block_migration': 'auto'}}
|
||||
ex = self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.controller._migrate_live,
|
||||
self.req, fakes.FAKE_UUID, body=body)
|
||||
self.assertIn("Operation 'live-migration' not supported for "
|
||||
"SEV-enabled instance (%s)" % instance.uuid,
|
||||
six.text_type(ex))
|
||||
|
||||
def test_live_migrate_with_forced_host(self):
|
||||
body = {'os-migrateLive': {'host': 'hostname',
|
||||
'block_migration': 'auto',
|
||||
|
@ -13,10 +13,13 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import six
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute import suspend_server as \
|
||||
suspend_server_v21
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack.compute import admin_only_action_common
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
@ -39,6 +42,17 @@ class SuspendServerTestsV21(admin_only_action_common.CommonTests):
|
||||
def test_suspend_resume(self):
|
||||
self._test_actions(['_suspend', '_resume'])
|
||||
|
||||
@mock.patch('nova.virt.hardware.get_mem_encryption_constraint',
|
||||
new=mock.Mock(return_value=True))
|
||||
@mock.patch.object(objects.instance.Instance, 'image_meta')
|
||||
def test_suspend_sev_rejected(self, mock_image):
|
||||
instance = self._stub_instance_get()
|
||||
ex = self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.controller._suspend,
|
||||
self.req, fakes.FAKE_UUID, body={})
|
||||
self.assertIn("Operation 'suspend' not supported for SEV-enabled "
|
||||
"instance (%s)" % instance.uuid, six.text_type(ex))
|
||||
|
||||
def test_suspend_resume_with_non_existed_instance(self):
|
||||
self._test_actions_with_non_existed_instance(['_suspend', '_resume'])
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user