]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Add resume support to Instance
authorSteven Hardy <shardy@redhat.com>
Fri, 28 Jun 2013 13:43:04 +0000 (14:43 +0100)
committerSteven Hardy <shardy@redhat.com>
Mon, 1 Jul 2013 10:35:50 +0000 (11:35 +0100)
Change-Id: Id02c74243ff5fcd67ec8f6200ee7969b34f2faa1
blueprint: stack-suspend-resume

heat/engine/resources/instance.py
heat/tests/test_instance.py
heat/tests/v1_1/fakes.py

index ce4965ec95b0eb2e7afa1e17d7ba048341a25d14..6f90944f115b618d1fd006ba192a4d8ad73fd580 100644 (file)
@@ -348,16 +348,20 @@ class Instance(resource.Resource):
             if server is not None:
                 self.resource_id_set(server.id)
 
+        return server, scheduler.TaskRunner(self._attach_volumes_task())
+
+    def _attach_volumes_task(self):
         attach_tasks = (volume.VolumeAttachTask(self.stack,
                                                 self.resource_id,
                                                 volume_id,
                                                 device)
                         for volume_id, device in self.volumes())
-        attach_volumes_task = scheduler.PollingTaskGroup(attach_tasks)
-
-        return server, scheduler.TaskRunner(attach_volumes_task)
+        return scheduler.PollingTaskGroup(attach_tasks)
 
     def check_create_complete(self, cookie):
+        return self._check_active(cookie)
+
+    def _check_active(self, cookie):
         server, volume_attach = cookie
 
         if not volume_attach.started():
@@ -563,6 +567,29 @@ class Instance(resource.Resource):
         else:
             return volumes_runner.step()
 
+    def handle_resume(self):
+        '''
+        Resume an instance - note we do not wait for the ACTIVE state,
+        this is polled for by check_resume_complete in a similar way to the
+        create logic so we can take advantage of coroutines
+        '''
+        if self.resource_id is None:
+            raise exception.Error(_('Cannot resume %s, resource_id not set') %
+                                  self.name)
+
+        try:
+            server = self.nova().servers.get(self.resource_id)
+        except clients.novaclient.exceptions.NotFound:
+            raise exception.NotFound(_('Failed to find instance %s') %
+                                     self.resource_id)
+        else:
+            logger.debug("resuming instance %s" % self.resource_id)
+            server.resume()
+            return server, scheduler.TaskRunner(self._attach_volumes_task())
+
+    def check_resume_complete(self, cookie):
+        return self._check_active(cookie)
+
 
 def resource_mapping():
     return {
index 8f090d80204cacad8c47a0606b9e6cc8c4fd7f76..617b7dc4e3a2b9b726c0c09bdf91c84e30b1d3d1 100644 (file)
@@ -308,6 +308,28 @@ class instancesTest(HeatTestCase):
 
         self.m.VerifyAll()
 
+    def test_instance_status_resume_immediate(self):
+        return_server = self.fc.servers.list()[1]
+        instance = self._create_test_instance(return_server,
+                                              'test_instance_resume')
+
+        instance.resource_id = 1234
+        self.m.ReplayAll()
+
+        # Override the get_servers_1234 handler status to SUSPENDED
+        d = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
+        d['server']['status'] = 'ACTIVE'
+        self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
+        get = self.fc.client.get_servers_1234
+        get().AndReturn((200, d))
+        mox.Replay(get)
+        instance.state_set(instance.SUSPEND, instance.COMPLETE)
+
+        scheduler.TaskRunner(instance.resume)()
+        self.assertEqual(instance.state, (instance.RESUME, instance.COMPLETE))
+
+        self.m.VerifyAll()
+
     def test_instance_status_suspend_wait(self):
         return_server = self.fc.servers.list()[1]
         instance = self._create_test_instance(return_server,
@@ -336,6 +358,36 @@ class instancesTest(HeatTestCase):
 
         self.m.VerifyAll()
 
+    def test_instance_status_resume_wait(self):
+        return_server = self.fc.servers.list()[1]
+        instance = self._create_test_instance(return_server,
+                                              'test_instance_resume')
+
+        instance.resource_id = 1234
+        self.m.ReplayAll()
+
+        # Override the get_servers_1234 handler status to ACTIVE, but
+        # return the SUSPENDED state first (twice, so we sleep)
+        d1 = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
+        d2 = copy.deepcopy(d1)
+        d1['server']['status'] = 'SUSPENDED'
+        d2['server']['status'] = 'ACTIVE'
+        self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
+        get = self.fc.client.get_servers_1234
+        get().AndReturn((200, d1))
+        get().AndReturn((200, d1))
+        self.m.StubOutWithMock(scheduler.TaskRunner, '_sleep')
+        scheduler.TaskRunner._sleep(mox.IsA(int)).AndReturn(None)
+        get().AndReturn((200, d2))
+        self.m.ReplayAll()
+
+        instance.state_set(instance.SUSPEND, instance.COMPLETE)
+
+        scheduler.TaskRunner(instance.resume)()
+        self.assertEqual(instance.state, (instance.RESUME, instance.COMPLETE))
+
+        self.m.VerifyAll()
+
     def test_instance_suspend_volumes_step(self):
         return_server = self.fc.servers.list()[1]
         instance = self._create_test_instance(return_server,
@@ -365,6 +417,40 @@ class instancesTest(HeatTestCase):
 
         self.m.VerifyAll()
 
+    def test_instance_resume_volumes_step(self):
+        return_server = self.fc.servers.list()[1]
+        instance = self._create_test_instance(return_server,
+                                              'test_instance_resume')
+
+        instance.resource_id = 1234
+        self.m.ReplayAll()
+
+        # Override the get_servers_1234 handler status to ACTIVE
+        d = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
+        d['server']['status'] = 'ACTIVE'
+
+        # Return a dummy PollingTaskGroup to make check_resume_complete step
+        def dummy_attach():
+            yield
+        dummy_tg = scheduler.PollingTaskGroup([dummy_attach, dummy_attach])
+        self.m.StubOutWithMock(instance, '_attach_volumes_task')
+        instance._attach_volumes_task().AndReturn(dummy_tg)
+
+        self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
+        get = self.fc.client.get_servers_1234
+        get().AndReturn((200, d))
+
+        self.m.StubOutWithMock(scheduler.TaskRunner, '_sleep')
+        scheduler.TaskRunner._sleep(mox.IsA(int)).AndReturn(None)
+        self.m.ReplayAll()
+
+        instance.state_set(instance.SUSPEND, instance.COMPLETE)
+
+        scheduler.TaskRunner(instance.resume)()
+        self.assertEqual(instance.state, (instance.RESUME, instance.COMPLETE))
+
+        self.m.VerifyAll()
+
     def test_instance_status_build_spawning(self):
         self._test_instance_status_not_build_active('BUILD(SPAWNING)')
 
index f18c4b6ba08be43e503a646ac207cdad0db546cd..4aaef6db75a64784a900ce2c87c5a55f1cf0e75b 100644 (file)
@@ -360,6 +360,8 @@ class FakeHTTPClient(base_client.HTTPClient):
             assert body[action] is None
         elif action == 'suspend':
             assert body[action] is None
+        elif action == 'resume':
+            assert body[action] is None
         elif action == 'addFixedIp':
             assert body[action].keys() == ['networkId']
         elif action == 'removeFixedIp':