Browse Source

Merge "Reject live migration and suspend on SEV guests"

changes/47/678447/13
Zuul 1 week ago
parent
commit
5ad53470ff

+ 2
- 0
nova/api/openstack/compute/migrate_server.py View File

@@ -171,6 +171,8 @@ class MigrateServerController(wsgi.Controller):
171 171
                               "'%(ex)s'", {'ex': ex})
172 172
             else:
173 173
                 raise exc.HTTPBadRequest(explanation=ex.format_message())
174
+        except exception.OperationNotSupportedForSEV as e:
175
+            raise exc.HTTPConflict(explanation=e.format_message())
174 176
         except exception.InstanceIsLocked as e:
175 177
             raise exc.HTTPConflict(explanation=e.format_message())
176 178
         except exception.ComputeHostNotFound as e:

+ 2
- 1
nova/api/openstack/compute/suspend_server.py View File

@@ -38,7 +38,8 @@ class SuspendServerController(wsgi.Controller):
38 38
                         target={'user_id': server.user_id,
39 39
                                 'project_id': server.project_id})
40 40
             self.compute_api.suspend(context, server)
41
-        except exception.InstanceIsLocked as e:
41
+        except (exception.OperationNotSupportedForSEV,
42
+                exception.InstanceIsLocked) as e:
42 43
             raise exc.HTTPConflict(explanation=e.format_message())
43 44
         except exception.InstanceInvalidState as state_error:
44 45
             common.raise_http_conflict_for_instance_invalid_state(state_error,

+ 20
- 0
nova/compute/api.py View File

@@ -205,6 +205,24 @@ def check_instance_lock(function):
205 205
     return inner
206 206
 
207 207
 
208
+def reject_sev_instances(operation):
209
+    """Decorator.  Raise OperationNotSupportedForSEV if instance has SEV
210
+    enabled.
211
+    """
212
+
213
+    def outer(f):
214
+        @six.wraps(f)
215
+        def inner(self, context, instance, *args, **kw):
216
+            if hardware.get_mem_encryption_constraint(instance.flavor,
217
+                                                      instance.image_meta):
218
+                raise exception.OperationNotSupportedForSEV(
219
+                    instance_uuid=instance.uuid,
220
+                    operation=operation)
221
+            return f(self, context, instance, *args, **kw)
222
+        return inner
223
+    return outer
224
+
225
+
208 226
 def _diff_dict(orig, new):
209 227
     """Return a dict describing how to change orig to new.  The keys
210 228
     correspond to values that have changed; the value will be a list
@@ -3785,6 +3803,7 @@ class API(base.Base):
3785 3803
         return self.compute_rpcapi.get_instance_diagnostics(context,
3786 3804
                                                             instance=instance)
3787 3805
 
3806
+    @reject_sev_instances(instance_actions.SUSPEND)
3788 3807
     @check_instance_lock
3789 3808
     @check_instance_state(vm_state=[vm_states.ACTIVE])
3790 3809
     def suspend(self, context, instance):
@@ -4448,6 +4467,7 @@ class API(base.Base):
4448 4467
                                                      diff=diff)
4449 4468
         return _metadata
4450 4469
 
4470
+    @reject_sev_instances(instance_actions.LIVE_MIGRATION)
4451 4471
     @check_instance_lock
4452 4472
     @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED])
4453 4473
     def live_migrate(self, context, instance, block_migration,

+ 6
- 0
nova/exception.py View File

@@ -533,6 +533,12 @@ class UnableToMigrateToSelf(Invalid):
533 533
                 "to current host (%(host)s).")
534 534
 
535 535
 
536
+class OperationNotSupportedForSEV(NovaException):
537
+    msg_fmt = _("Operation '%(operation)s' not supported for SEV-enabled "
538
+                "instance (%(instance_uuid)s).")
539
+    code = 409
540
+
541
+
536 542
 class InvalidHypervisorType(Invalid):
537 543
     msg_fmt = _("The supplied hypervisor type of is invalid.")
538 544
 

+ 14
- 0
nova/tests/unit/api/openstack/compute/test_migrate_server.py View File

@@ -609,6 +609,20 @@ class MigrateServerTestsV268(MigrateServerTestsV256):
609 609
                            method_translations=method_translations,
610 610
                            args_map=args_map)
611 611
 
612
+    @mock.patch('nova.virt.hardware.get_mem_encryption_constraint',
613
+                new=mock.Mock(return_value=True))
614
+    @mock.patch.object(objects.instance.Instance, 'image_meta')
615
+    def test_live_migrate_sev_rejected(self, mock_image):
616
+        instance = self._stub_instance_get()
617
+        body = {'os-migrateLive': {'host': 'hostname',
618
+                                   'block_migration': 'auto'}}
619
+        ex = self.assertRaises(webob.exc.HTTPConflict,
620
+                               self.controller._migrate_live,
621
+                               self.req, fakes.FAKE_UUID, body=body)
622
+        self.assertIn("Operation 'live-migration' not supported for "
623
+                      "SEV-enabled instance (%s)" % instance.uuid,
624
+                      six.text_type(ex))
625
+
612 626
     def test_live_migrate_with_forced_host(self):
613 627
         body = {'os-migrateLive': {'host': 'hostname',
614 628
                                    'block_migration': 'auto',

+ 14
- 0
nova/tests/unit/api/openstack/compute/test_suspend_server.py View File

@@ -13,10 +13,13 @@
13 13
 #    under the License.
14 14
 
15 15
 import mock
16
+import six
17
+import webob
16 18
 
17 19
 from nova.api.openstack.compute import suspend_server as \
18 20
     suspend_server_v21
19 21
 from nova import exception
22
+from nova import objects
20 23
 from nova import test
21 24
 from nova.tests.unit.api.openstack.compute import admin_only_action_common
22 25
 from nova.tests.unit.api.openstack import fakes
@@ -39,6 +42,17 @@ class SuspendServerTestsV21(admin_only_action_common.CommonTests):
39 42
     def test_suspend_resume(self):
40 43
         self._test_actions(['_suspend', '_resume'])
41 44
 
45
+    @mock.patch('nova.virt.hardware.get_mem_encryption_constraint',
46
+                new=mock.Mock(return_value=True))
47
+    @mock.patch.object(objects.instance.Instance, 'image_meta')
48
+    def test_suspend_sev_rejected(self, mock_image):
49
+        instance = self._stub_instance_get()
50
+        ex = self.assertRaises(webob.exc.HTTPConflict,
51
+                               self.controller._suspend,
52
+                               self.req, fakes.FAKE_UUID, body={})
53
+        self.assertIn("Operation 'suspend' not supported for SEV-enabled "
54
+                      "instance (%s)" % instance.uuid, six.text_type(ex))
55
+
42 56
     def test_suspend_resume_with_non_existed_instance(self):
43 57
         self._test_actions_with_non_existed_instance(['_suspend', '_resume'])
44 58
 

Loading…
Cancel
Save