Skip to content

Commit

Permalink
Reject live migration and suspend on SEV guests
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Adam Spiers authored and stephenfin committed Sep 10, 2019
1 parent cedc850 commit 84db8b3
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 1 deletion.
2 changes: 2 additions & 0 deletions nova/api/openstack/compute/migrate_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ def _migrate_live(self, req, id, body):
"'%(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:
Expand Down
3 changes: 2 additions & 1 deletion nova/api/openstack/compute/suspend_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ def _suspend(self, req, id, body):
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,
Expand Down
20 changes: 20 additions & 0 deletions nova/compute/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,24 @@ def inner(self, context, instance, *args, **kwargs):
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
Expand Down Expand Up @@ -3785,6 +3803,7 @@ def get_instance_diagnostics(self, context, instance):
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):
Expand Down Expand Up @@ -4448,6 +4467,7 @@ def update_instance_metadata(self, context, instance,
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,
Expand Down
6 changes: 6 additions & 0 deletions nova/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.")

Expand Down
14 changes: 14 additions & 0 deletions nova/tests/unit/api/openstack/compute/test_migrate_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,20 @@ def test_live_migrate(self):
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',
Expand Down
14 changes: 14 additions & 0 deletions nova/tests/unit/api/openstack/compute/test_suspend_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -39,6 +42,17 @@ def setUp(self):
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'])

Expand Down

0 comments on commit 84db8b3

Please sign in to comment.