From c1b4b98a40113378115d6897a37cc67b4fe52e00 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Wed, 26 Jun 2013 17:39:46 +0100 Subject: [PATCH] Add suspend support to InstanceGroup Change-Id: I46baf7fd17e6701e2ce07f823fff4c8fb4dd3dfe blueprint: stack-suspend-resume --- heat/engine/resources/autoscaling.py | 14 ++++ heat/tests/test_autoscaling.py | 99 ++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/heat/engine/resources/autoscaling.py b/heat/engine/resources/autoscaling.py index f19204dc..bb77a7c1 100644 --- a/heat/engine/resources/autoscaling.py +++ b/heat/engine/resources/autoscaling.py @@ -148,6 +148,20 @@ class InstanceGroup(resource.Resource): logger.debug('handle_delete %s' % inst.name) inst.destroy() + def handle_suspend(self): + cookie_list = [] + for inst in self._instances(): + logger.debug('handle_suspend %s' % inst.name) + inst_cookie = inst.handle_suspend() + cookie_list.append((inst, inst_cookie)) + return cookie_list + + def check_suspend_complete(self, cookie_list): + for inst, inst_cookie in cookie_list: + if not inst.check_suspend_complete(inst_cookie): + return False + return True + @scheduler.wrappertask def _scale(self, instance_task, indices): group = scheduler.PollingTaskGroup.from_task_with_args(instance_task, diff --git a/heat/tests/test_autoscaling.py b/heat/tests/test_autoscaling.py index a959371f..a8bd05ac 100644 --- a/heat/tests/test_autoscaling.py +++ b/heat/tests/test_autoscaling.py @@ -174,6 +174,105 @@ class AutoScalingTest(HeatTestCase): rsrc.delete() self.m.VerifyAll() + def test_scaling_group_suspend(self): + t = template_format.parse(as_template) + stack = parse_stack(t) + + self._stub_lb_reload(['WebServerGroup-0']) + now = timeutils.utcnow() + self._stub_meta_expected(now, 'ExactCapacity : 1') + self._stub_create(1) + self.m.ReplayAll() + rsrc = self.create_scaling_group(t, stack, 'WebServerGroup') + self.assertEqual('WebServerGroup', rsrc.FnGetRefId()) + self.assertEqual('WebServerGroup-0', rsrc.resource_id) + self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE)) + + self.m.VerifyAll() + self.m.UnsetStubs() + + self.m.StubOutWithMock(scheduler.TaskRunner, '_sleep') + self.m.StubOutWithMock(instance.Instance, 'handle_suspend') + self.m.StubOutWithMock(instance.Instance, 'check_suspend_complete') + inst_cookie = (object(), object(), object()) + instance.Instance.handle_suspend().AndReturn(inst_cookie) + instance.Instance.check_suspend_complete(inst_cookie).AndReturn(False) + instance.Instance.check_suspend_complete(inst_cookie).AndReturn(True) + scheduler.TaskRunner._sleep(mox.IsA(int)).AndReturn(None) + self.m.ReplayAll() + + scheduler.TaskRunner(rsrc.suspend)() + self.assertEqual(rsrc.state, (rsrc.SUSPEND, rsrc.COMPLETE)) + + rsrc.delete() + self.m.VerifyAll() + + def test_scaling_group_suspend_multiple(self): + t = template_format.parse(as_template) + properties = t['Resources']['WebServerGroup']['Properties'] + properties['DesiredCapacity'] = '2' + stack = parse_stack(t) + + self._stub_lb_reload(['WebServerGroup-0', 'WebServerGroup-1']) + now = timeutils.utcnow() + self._stub_meta_expected(now, 'ExactCapacity : 2') + self._stub_create(2) + self.m.ReplayAll() + rsrc = self.create_scaling_group(t, stack, 'WebServerGroup') + self.assertEqual('WebServerGroup', rsrc.FnGetRefId()) + self.assertEqual('WebServerGroup-0,WebServerGroup-1', rsrc.resource_id) + self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE)) + + self.m.VerifyAll() + self.m.UnsetStubs() + + self.m.StubOutWithMock(instance.Instance, 'handle_suspend') + self.m.StubOutWithMock(instance.Instance, 'check_suspend_complete') + inst_cookie1 = ('foo1', 'foo2', 'foo3') + inst_cookie2 = ('bar1', 'bar2', 'bar3') + instance.Instance.handle_suspend().AndReturn(inst_cookie1) + instance.Instance.handle_suspend().AndReturn(inst_cookie2) + instance.Instance.check_suspend_complete(inst_cookie1).AndReturn(True) + instance.Instance.check_suspend_complete(inst_cookie2).AndReturn(True) + self.m.ReplayAll() + + scheduler.TaskRunner(rsrc.suspend)() + self.assertEqual(rsrc.state, (rsrc.SUSPEND, rsrc.COMPLETE)) + + rsrc.delete() + self.m.VerifyAll() + + def test_scaling_group_suspend_fail(self): + t = template_format.parse(as_template) + stack = parse_stack(t) + + self._stub_lb_reload(['WebServerGroup-0']) + now = timeutils.utcnow() + self._stub_meta_expected(now, 'ExactCapacity : 1') + self._stub_create(1) + self.m.ReplayAll() + rsrc = self.create_scaling_group(t, stack, 'WebServerGroup') + self.assertEqual('WebServerGroup', rsrc.FnGetRefId()) + self.assertEqual('WebServerGroup-0', rsrc.resource_id) + self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE)) + + self.m.VerifyAll() + self.m.UnsetStubs() + + self.m.StubOutWithMock(instance.Instance, 'handle_suspend') + self.m.StubOutWithMock(instance.Instance, 'check_suspend_complete') + inst_cookie = (object(), object(), object()) + instance.Instance.handle_suspend().AndRaise(Exception('oops')) + self.m.ReplayAll() + + sus_task = scheduler.TaskRunner(rsrc.suspend) + self.assertRaises(exception.ResourceFailure, sus_task, ()) + self.assertEqual(rsrc.state, (rsrc.SUSPEND, rsrc.FAILED)) + self.assertEqual(rsrc.status_reason, 'Exception: oops') + + rsrc.delete() + self.m.VerifyAll() + def test_scaling_group_create_error(self): t = template_format.parse(as_template) stack = parse_stack(t) -- 2.45.2