~ubuntu-branches/ubuntu/wily/heat/wily-proposed

« back to all changes in this revision

Viewing changes to heat/tests/test_autoscaling.py

  • Committer: Package Import Robot
  • Author(s): James Page, Corey Bryant, James Page
  • Date: 2015-03-30 11:11:18 UTC
  • mfrom: (1.1.23)
  • Revision ID: package-import@ubuntu.com-20150330111118-2qpycylx6swu4yhj
Tags: 2015.1~b3-0ubuntu1
[ Corey Bryant ]
* New upstream milestone release for OpenStack kilo:
  - d/control: Align with upstream dependencies.
  - d/p/sudoers_patch.patch: Rebased.
  - d/p/fix-requirements.patch: Rebased.

[ James Page ]
* d/p/fixup-assert-regex.patch: Tweak test to use assertRegexpMatches.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#
2
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
3
 
#    not use this file except in compliance with the License. You may obtain
4
 
#    a copy of the License at
5
 
#
6
 
#         http://www.apache.org/licenses/LICENSE-2.0
7
 
#
8
 
#    Unless required by applicable law or agreed to in writing, software
9
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
 
#    License for the specific language governing permissions and limitations
12
 
#    under the License.
13
 
 
14
 
import copy
15
 
import datetime
16
 
 
17
 
import mock
18
 
import mox
19
 
from oslo.config import cfg
20
 
from oslo.utils import timeutils
21
 
import six
22
 
 
23
 
from heat.common import exception
24
 
from heat.common import grouputils
25
 
from heat.common import short_id
26
 
from heat.common import template_format
27
 
from heat.engine.clients.os import nova
28
 
from heat.engine.notification import autoscaling as notification
29
 
from heat.engine import parser
30
 
from heat.engine import resource
31
 
from heat.engine.resources.aws import autoscaling_group as asg
32
 
from heat.engine.resources import instance
33
 
from heat.engine.resources import loadbalancer
34
 
from heat.engine.resources.neutron import loadbalancer as neutron_lb
35
 
from heat.engine import rsrc_defn
36
 
from heat.engine import scheduler
37
 
from heat.tests.autoscaling import inline_templates
38
 
from heat.tests import common
39
 
from heat.tests import utils
40
 
 
41
 
 
42
 
as_template = inline_templates.as_template
43
 
 
44
 
as_template_bad_group = '''
45
 
{
46
 
  "AWSTemplateFormatVersion" : "2010-09-09",
47
 
  "Parameters" : {
48
 
  "ImageId": {"Type": "String"},
49
 
  "KeyName": {"Type": "String"}
50
 
  },
51
 
  "Resources" : {
52
 
    "WebServerScaleUpPolicy" : {
53
 
      "Type" : "AWS::AutoScaling::ScalingPolicy",
54
 
      "Properties" : {
55
 
        "AdjustmentType" : "ChangeInCapacity",
56
 
        "AutoScalingGroupName" : "not a real group",
57
 
        "Cooldown" : "60",
58
 
        "ScalingAdjustment" : "1"
59
 
      }
60
 
    }
61
 
  }
62
 
}
63
 
'''
64
 
 
65
 
 
66
 
class AutoScalingTest(common.HeatTestCase):
67
 
    dummy_instance_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
68
 
    params = {'KeyName': 'test', 'ImageId': 'foo'}
69
 
    params_HoT = {'flavor': 'test', 'image': 'foo'}
70
 
 
71
 
    def setUp(self):
72
 
        super(AutoScalingTest, self).setUp()
73
 
        cfg.CONF.set_default('heat_waitcondition_server_url',
74
 
                             'http://server.test:8000/v1/waitcondition')
75
 
        self.stub_keystoneclient()
76
 
        t = template_format.parse(as_template)
77
 
        stack = utils.parse_stack(t, params=self.params)
78
 
        self.defn = rsrc_defn.ResourceDefinition(
79
 
            'asg', 'AWS::AutoScaling::AutoScalingGroup',
80
 
            {'AvailabilityZones': ['nova'],
81
 
             'LaunchConfigurationName': 'config',
82
 
             'MaxSize': 5,
83
 
             'MinSize': 1,
84
 
             'DesiredCapacity': 2})
85
 
        self.asg = asg.AutoScalingGroup('asg', self.defn, stack)
86
 
 
87
 
    def create_scaling_group(self, t, stack, resource_name):
88
 
        # create the launch configuration resource
89
 
        conf = stack['LaunchConfig']
90
 
        self.assertIsNone(conf.validate())
91
 
        scheduler.TaskRunner(conf.create)()
92
 
        self.assertEqual((conf.CREATE, conf.COMPLETE), conf.state)
93
 
        # check bdm in configuration
94
 
        self.assertIsNotNone(conf.properties['BlockDeviceMappings'])
95
 
 
96
 
        # create the group resource
97
 
        rsrc = stack[resource_name]
98
 
        self.assertIsNone(rsrc.validate())
99
 
        scheduler.TaskRunner(rsrc.create)()
100
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
101
 
        # check bdm in instance_definition
102
 
        instance_definition = rsrc._get_instance_definition()
103
 
        self.assertIn('BlockDeviceMappings',
104
 
                      instance_definition['Properties'])
105
 
 
106
 
        return rsrc
107
 
 
108
 
    def create_scaling_policy(self, t, stack, resource_name):
109
 
        rsrc = stack[resource_name]
110
 
        self.assertIsNone(rsrc.validate())
111
 
        scheduler.TaskRunner(rsrc.create)()
112
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
113
 
        return rsrc
114
 
 
115
 
    def _stub_create(self, num, with_error=None, with_lcn=True):
116
 
        self.m.StubOutWithMock(instance.Instance, 'handle_create')
117
 
        self.m.StubOutWithMock(instance.Instance, 'check_create_complete')
118
 
        self.stub_ImageConstraint_validate()
119
 
        self.stub_FlavorConstraint_validate()
120
 
        # create with launch config name, need to stub snapshot constraint
121
 
        if with_lcn:
122
 
            self.stub_SnapshotConstraint_validate()
123
 
        if with_error:
124
 
            instance.Instance.handle_create().AndRaise(
125
 
                exception.Error(with_error))
126
 
            return
127
 
        cookie = object()
128
 
        for x in range(num):
129
 
            instance.Instance.handle_create().AndReturn(cookie)
130
 
        instance.Instance.check_create_complete(cookie).AndReturn(False)
131
 
        instance.Instance.check_create_complete(
132
 
            cookie).MultipleTimes().AndReturn(True)
133
 
 
134
 
    def _stub_delete(self, num):
135
 
        self.m.StubOutWithMock(instance.Instance, 'handle_delete')
136
 
        self.m.StubOutWithMock(instance.Instance, 'check_delete_complete')
137
 
        task = object()
138
 
        for x in range(num):
139
 
            instance.Instance.handle_delete().AndReturn(task)
140
 
        instance.Instance.check_delete_complete(task).AndReturn(False)
141
 
        instance.Instance.check_delete_complete(
142
 
            task).MultipleTimes().AndReturn(True)
143
 
 
144
 
    def _stub_suspend(self, cookies=None, with_error=None):
145
 
        cookies = cookies or []
146
 
        self.m.StubOutWithMock(instance.Instance, 'handle_suspend')
147
 
        self.m.StubOutWithMock(instance.Instance, 'check_suspend_complete')
148
 
        if with_error:
149
 
            instance.Instance.handle_suspend().AndRaise(
150
 
                exception.Error(with_error))
151
 
            return
152
 
        inst_cookies = cookies or [(object(), object(), object())]
153
 
        for cookie in inst_cookies:
154
 
            instance.Instance.handle_suspend().InAnyOrder().AndReturn(cookie)
155
 
            instance.Instance.check_suspend_complete(
156
 
                cookie).InAnyOrder().AndReturn(False)
157
 
            instance.Instance.check_suspend_complete(
158
 
                cookie).InAnyOrder().AndReturn(True)
159
 
 
160
 
    def _stub_resume(self, cookies=None, with_error=None):
161
 
        cookies = cookies or []
162
 
        self.m.StubOutWithMock(instance.Instance, 'handle_resume')
163
 
        self.m.StubOutWithMock(instance.Instance, 'check_resume_complete')
164
 
        if with_error:
165
 
            instance.Instance.handle_resume().AndRaise(
166
 
                exception.Error(with_error))
167
 
            return
168
 
        inst_cookies = cookies or [(object(), object(), object())]
169
 
        for cookie in inst_cookies:
170
 
            instance.Instance.handle_resume().InAnyOrder().AndReturn(cookie)
171
 
            instance.Instance.check_resume_complete(
172
 
                cookie).InAnyOrder().AndReturn(False)
173
 
            instance.Instance.check_resume_complete(
174
 
                cookie).InAnyOrder().AndReturn(True)
175
 
 
176
 
    def _stub_lb_reload(self, num, unset=True, nochange=False):
177
 
        expected_list = [self.dummy_instance_id] * num
178
 
        if unset:
179
 
            self.m.VerifyAll()
180
 
            self.m.UnsetStubs()
181
 
        if num > 0:
182
 
            self.m.StubOutWithMock(instance.Instance, 'FnGetRefId')
183
 
            instance.Instance.FnGetRefId().MultipleTimes().AndReturn(
184
 
                self.dummy_instance_id)
185
 
 
186
 
        if not nochange:
187
 
            self.m.StubOutWithMock(loadbalancer.LoadBalancer, 'handle_update')
188
 
            loadbalancer.LoadBalancer.handle_update(
189
 
                mox.IgnoreArg(), mox.IgnoreArg(),
190
 
                {'Instances': expected_list}).AndReturn(None)
191
 
 
192
 
    def _stub_scale_notification(self,
193
 
                                 adjust,
194
 
                                 groupname,
195
 
                                 start_capacity,
196
 
                                 adjust_type='ChangeInCapacity',
197
 
                                 end_capacity=None,
198
 
                                 with_error=None):
199
 
 
200
 
        self.m.StubOutWithMock(notification, 'send')
201
 
        notification.send(stack=mox.IgnoreArg(),
202
 
                          adjustment=adjust,
203
 
                          adjustment_type=adjust_type,
204
 
                          capacity=start_capacity,
205
 
                          groupname=mox.IgnoreArg(),
206
 
                          suffix='start',
207
 
                          message="Start resizing the group %s"
208
 
                          % groupname,
209
 
                          ).AndReturn(False)
210
 
        if with_error:
211
 
            notification.send(stack=mox.IgnoreArg(),
212
 
                              adjustment=adjust,
213
 
                              capacity=start_capacity,
214
 
                              adjustment_type=adjust_type,
215
 
                              groupname=mox.IgnoreArg(),
216
 
                              message='Nested stack update failed:'
217
 
                                      ' Error: %s' % with_error,
218
 
                              suffix='error',
219
 
                              ).AndReturn(False)
220
 
        else:
221
 
            notification.send(stack=mox.IgnoreArg(),
222
 
                              adjustment=adjust,
223
 
                              adjustment_type=adjust_type,
224
 
                              capacity=end_capacity,
225
 
                              groupname=mox.IgnoreArg(),
226
 
                              message="End resizing the group %s"
227
 
                              % groupname,
228
 
                              suffix='end',
229
 
                              ).AndReturn(False)
230
 
 
231
 
    def _stub_meta_expected(self, now, data, nmeta=1):
232
 
        # Stop time at now
233
 
        timeutils.set_time_override(now)
234
 
        self.addCleanup(timeutils.clear_time_override)
235
 
 
236
 
        # Then set a stub to ensure the metadata update is as
237
 
        # expected based on the timestamp and data
238
 
        self.m.StubOutWithMock(resource.Resource, 'metadata_set')
239
 
        expected = {timeutils.strtime(now): data}
240
 
        # Note for ScalingPolicy, we expect to get a metadata
241
 
        # update for the policy and autoscaling group, so pass nmeta=2
242
 
        for x in range(nmeta):
243
 
            resource.Resource.metadata_set(expected).AndReturn(None)
244
 
 
245
 
    def test_scaling_group_suspend(self):
246
 
        t = template_format.parse(as_template)
247
 
        stack = utils.parse_stack(t, params=self.params)
248
 
 
249
 
        self._stub_lb_reload(1)
250
 
        now = timeutils.utcnow()
251
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
252
 
        self._stub_create(1)
253
 
        self.m.ReplayAll()
254
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
255
 
        self.assertEqual(utils.PhysName(stack.name, rsrc.name),
256
 
                         rsrc.FnGetRefId())
257
 
        self.assertEqual(1, len(grouputils.get_member_names(rsrc)))
258
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
259
 
 
260
 
        self.m.VerifyAll()
261
 
        self.m.UnsetStubs()
262
 
 
263
 
        self._stub_suspend()
264
 
        self.m.ReplayAll()
265
 
 
266
 
        scheduler.TaskRunner(rsrc.suspend)()
267
 
        self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state)
268
 
 
269
 
        rsrc.delete()
270
 
        self.m.VerifyAll()
271
 
 
272
 
    def test_scaling_group_resume(self):
273
 
        t = template_format.parse(as_template)
274
 
        stack = utils.parse_stack(t, params=self.params)
275
 
 
276
 
        self._stub_lb_reload(1)
277
 
        now = timeutils.utcnow()
278
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
279
 
        self._stub_create(1)
280
 
        self.m.ReplayAll()
281
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
282
 
        self.assertEqual(utils.PhysName(stack.name, rsrc.name),
283
 
                         rsrc.FnGetRefId())
284
 
        self.assertEqual(1, len(grouputils.get_member_names(rsrc)))
285
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
286
 
 
287
 
        self.m.VerifyAll()
288
 
        self.m.UnsetStubs()
289
 
 
290
 
        self._stub_resume()
291
 
        self.m.ReplayAll()
292
 
 
293
 
        rsrc.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
294
 
        for i in rsrc.nested().values():
295
 
            i.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
296
 
 
297
 
        scheduler.TaskRunner(rsrc.resume)()
298
 
        self.assertEqual((rsrc.RESUME, rsrc.COMPLETE), rsrc.state)
299
 
 
300
 
        rsrc.delete()
301
 
        self.m.VerifyAll()
302
 
 
303
 
    def test_scaling_group_suspend_multiple(self):
304
 
        t = template_format.parse(as_template)
305
 
        properties = t['Resources']['WebServerGroup']['Properties']
306
 
        properties['DesiredCapacity'] = '2'
307
 
        stack = utils.parse_stack(t, params=self.params)
308
 
 
309
 
        self._stub_lb_reload(2)
310
 
        now = timeutils.utcnow()
311
 
        self._stub_meta_expected(now, 'ExactCapacity : 2')
312
 
        self._stub_create(2)
313
 
        self.m.ReplayAll()
314
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
315
 
        self.assertEqual(utils.PhysName(stack.name, rsrc.name),
316
 
                         rsrc.FnGetRefId())
317
 
        self.assertEqual(2, len(grouputils.get_member_names(rsrc)))
318
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
319
 
 
320
 
        self.m.VerifyAll()
321
 
        self.m.UnsetStubs()
322
 
 
323
 
        self._stub_suspend(cookies=[('foo1', 'foo2', 'foo3'),
324
 
                                    ('bar1', 'bar2', 'bar3')])
325
 
        self.m.ReplayAll()
326
 
 
327
 
        scheduler.TaskRunner(rsrc.suspend)()
328
 
        self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state)
329
 
 
330
 
        rsrc.delete()
331
 
        self.m.VerifyAll()
332
 
 
333
 
    def test_scaling_group_resume_multiple(self):
334
 
        t = template_format.parse(as_template)
335
 
        properties = t['Resources']['WebServerGroup']['Properties']
336
 
        properties['DesiredCapacity'] = '2'
337
 
        stack = utils.parse_stack(t, params=self.params)
338
 
 
339
 
        self._stub_lb_reload(2)
340
 
        now = timeutils.utcnow()
341
 
        self._stub_meta_expected(now, 'ExactCapacity : 2')
342
 
        self._stub_create(2)
343
 
        self.m.ReplayAll()
344
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
345
 
        self.assertEqual(utils.PhysName(stack.name, rsrc.name),
346
 
                         rsrc.FnGetRefId())
347
 
        self.assertEqual(2, len(grouputils.get_member_names(rsrc)))
348
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
349
 
 
350
 
        self.m.VerifyAll()
351
 
        self.m.UnsetStubs()
352
 
 
353
 
        self._stub_resume(cookies=[('foo1', 'foo2', 'foo3'),
354
 
                                   ('bar1', 'bar2', 'bar3')])
355
 
        self.m.ReplayAll()
356
 
 
357
 
        rsrc.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
358
 
        for i in rsrc.nested().values():
359
 
            i.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
360
 
 
361
 
        scheduler.TaskRunner(rsrc.resume)()
362
 
        self.assertEqual((rsrc.RESUME, rsrc.COMPLETE), rsrc.state)
363
 
 
364
 
        rsrc.delete()
365
 
        self.m.VerifyAll()
366
 
 
367
 
    def test_scaling_group_suspend_fail(self):
368
 
        t = template_format.parse(as_template)
369
 
        stack = utils.parse_stack(t, params=self.params)
370
 
 
371
 
        self._stub_lb_reload(1)
372
 
        now = timeutils.utcnow()
373
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
374
 
        self._stub_create(1)
375
 
        self.m.ReplayAll()
376
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
377
 
        self.assertEqual(utils.PhysName(stack.name, rsrc.name),
378
 
                         rsrc.FnGetRefId())
379
 
        self.assertEqual(1, len(grouputils.get_member_names(rsrc)))
380
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
381
 
 
382
 
        self.m.VerifyAll()
383
 
        self.m.UnsetStubs()
384
 
 
385
 
        self._stub_suspend(with_error='oops')
386
 
        self.m.ReplayAll()
387
 
 
388
 
        sus_task = scheduler.TaskRunner(rsrc.suspend)
389
 
        self.assertRaises(exception.ResourceFailure, sus_task, ())
390
 
        self.assertEqual((rsrc.SUSPEND, rsrc.FAILED), rsrc.state)
391
 
        self.assertEqual('Error: Resource SUSPEND failed: Error: oops',
392
 
                         rsrc.status_reason)
393
 
 
394
 
        rsrc.delete()
395
 
        self.m.VerifyAll()
396
 
 
397
 
    def test_scaling_group_resume_fail(self):
398
 
        t = template_format.parse(as_template)
399
 
        stack = utils.parse_stack(t, params=self.params)
400
 
 
401
 
        self._stub_lb_reload(1)
402
 
        now = timeutils.utcnow()
403
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
404
 
        self._stub_create(1)
405
 
        self.m.ReplayAll()
406
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
407
 
        self.assertEqual(utils.PhysName(stack.name, rsrc.name),
408
 
                         rsrc.FnGetRefId())
409
 
        self.assertEqual(1, len(grouputils.get_member_names(rsrc)))
410
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
411
 
 
412
 
        self.m.VerifyAll()
413
 
        self.m.UnsetStubs()
414
 
 
415
 
        self._stub_resume(with_error='oops')
416
 
        self.m.ReplayAll()
417
 
 
418
 
        rsrc.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
419
 
        for i in rsrc.nested().values():
420
 
            i.state_set(rsrc.SUSPEND, rsrc.COMPLETE)
421
 
 
422
 
        sus_task = scheduler.TaskRunner(rsrc.resume)
423
 
        self.assertRaises(exception.ResourceFailure, sus_task, ())
424
 
        self.assertEqual((rsrc.RESUME, rsrc.FAILED), rsrc.state)
425
 
        self.assertEqual('Error: Resource RESUME failed: Error: oops',
426
 
                         rsrc.status_reason)
427
 
 
428
 
        rsrc.delete()
429
 
        self.m.VerifyAll()
430
 
 
431
 
    def test_scaling_group_create_error(self):
432
 
        t = template_format.parse(as_template)
433
 
        stack = utils.parse_stack(t, params=self.params)
434
 
 
435
 
        self.m.StubOutWithMock(instance.Instance, 'handle_create')
436
 
        self.m.StubOutWithMock(instance.Instance, 'check_create_complete')
437
 
        instance.Instance.handle_create().AndRaise(Exception)
438
 
        self.stub_ImageConstraint_validate()
439
 
        self.stub_FlavorConstraint_validate()
440
 
        self.stub_SnapshotConstraint_validate()
441
 
 
442
 
        self.m.ReplayAll()
443
 
 
444
 
        conf = stack['LaunchConfig']
445
 
        self.assertIsNone(conf.validate())
446
 
        scheduler.TaskRunner(conf.create)()
447
 
        self.assertEqual((conf.CREATE, conf.COMPLETE), conf.state)
448
 
 
449
 
        rsrc = stack['WebServerGroup']
450
 
        self.assertIsNone(rsrc.validate())
451
 
        self.assertRaises(exception.ResourceFailure,
452
 
                          scheduler.TaskRunner(rsrc.create))
453
 
        self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
454
 
 
455
 
        self.assertEqual([], grouputils.get_members(rsrc))
456
 
 
457
 
        self.m.VerifyAll()
458
 
 
459
 
    def test_update_in_failed(self):
460
 
        self.asg.state_set('CREATE', 'FAILED')
461
 
        # to update the failed asg
462
 
        self.asg.adjust = mock.Mock(return_value=None)
463
 
 
464
 
        self.asg.handle_update(self.defn, None, None)
465
 
        self.asg.adjust.assert_called_once_with(
466
 
            2, adjustment_type='ExactCapacity')
467
 
 
468
 
    def test_lb_reload_static_resolve(self):
469
 
        t = template_format.parse(as_template)
470
 
        properties = t['Resources']['ElasticLoadBalancer']['Properties']
471
 
        properties['AvailabilityZones'] = {'Fn::GetAZs': ''}
472
 
 
473
 
        self.m.StubOutWithMock(parser.Stack, 'get_availability_zones')
474
 
        parser.Stack.get_availability_zones().MultipleTimes().AndReturn(
475
 
            ['abc', 'xyz'])
476
 
 
477
 
        # Check that the Fn::GetAZs is correctly resolved
478
 
        expected = {u'Type': u'AWS::ElasticLoadBalancing::LoadBalancer',
479
 
                    u'Properties': {'Instances': ['aaaabbbbcccc'],
480
 
                                    u'Listeners': [{u'InstancePort': u'80',
481
 
                                                    u'LoadBalancerPort': u'80',
482
 
                                                    u'Protocol': u'HTTP'}],
483
 
                                    u'AvailabilityZones': ['abc', 'xyz']}}
484
 
 
485
 
        self.m.StubOutWithMock(short_id, 'generate_id')
486
 
        short_id.generate_id().MultipleTimes().AndReturn('aaaabbbbcccc')
487
 
 
488
 
        now = timeutils.utcnow()
489
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
490
 
        self._stub_create(1)
491
 
        self.m.ReplayAll()
492
 
        stack = utils.parse_stack(t, params=self.params)
493
 
 
494
 
        lb = stack['ElasticLoadBalancer']
495
 
        self.m.StubOutWithMock(lb, 'handle_update')
496
 
        lb.handle_update(expected,
497
 
                         mox.IgnoreArg(),
498
 
                         mox.IgnoreArg()).AndReturn(None)
499
 
        self.m.ReplayAll()
500
 
 
501
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
502
 
        self.assertEqual(utils.PhysName(stack.name, rsrc.name),
503
 
                         rsrc.FnGetRefId())
504
 
        self.assertEqual(1, len(grouputils.get_member_names(rsrc)))
505
 
        props = copy.copy(rsrc.properties.data)
506
 
        props['Cooldown'] = '61'
507
 
        update_snippet = rsrc_defn.ResourceDefinition(rsrc.name,
508
 
                                                      rsrc.type(),
509
 
                                                      props)
510
 
        scheduler.TaskRunner(rsrc.update, update_snippet)()
511
 
 
512
 
        rsrc.delete()
513
 
        self.m.VerifyAll()
514
 
 
515
 
    def test_lb_reload_members(self):
516
 
        t = template_format.parse(as_template)
517
 
        t['Resources']['ElasticLoadBalancer'] = {
518
 
            'Type': 'OS::Neutron::LoadBalancer',
519
 
            'Properties': {
520
 
                'protocol_port': 8080,
521
 
                'pool_id': 'pool123'
522
 
            }
523
 
        }
524
 
 
525
 
        expected = {
526
 
            'Type': 'OS::Neutron::LoadBalancer',
527
 
            'Properties': {
528
 
                'protocol_port': 8080,
529
 
                'pool_id': 'pool123',
530
 
                'members': [u'aaaabbbbcccc']}
531
 
        }
532
 
 
533
 
        self.m.StubOutWithMock(short_id, 'generate_id')
534
 
        short_id.generate_id().MultipleTimes().AndReturn('aaaabbbbcccc')
535
 
 
536
 
        self.m.StubOutWithMock(neutron_lb.LoadBalancer, 'handle_update')
537
 
        neutron_lb.LoadBalancer.handle_update(expected,
538
 
                                              mox.IgnoreArg(),
539
 
                                              mox.IgnoreArg()).AndReturn(None)
540
 
 
541
 
        now = timeutils.utcnow()
542
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
543
 
        self._stub_create(1)
544
 
        self.m.ReplayAll()
545
 
        stack = utils.parse_stack(t, params=self.params)
546
 
        self.create_scaling_group(t, stack, 'WebServerGroup')
547
 
 
548
 
        self.m.VerifyAll()
549
 
 
550
 
    def test_scaling_policy_bad_group(self):
551
 
        t = template_format.parse(as_template_bad_group)
552
 
        stack = utils.parse_stack(t, params=self.params)
553
 
 
554
 
        self.m.ReplayAll()
555
 
        up_policy = self.create_scaling_policy(t, stack,
556
 
                                               'WebServerScaleUpPolicy')
557
 
 
558
 
        alarm_url = up_policy.FnGetAtt('AlarmUrl')
559
 
        self.assertIsNotNone(alarm_url)
560
 
        ex = self.assertRaises(exception.ResourceFailure, up_policy.signal)
561
 
        self.assertIn('Alarm WebServerScaleUpPolicy could '
562
 
                      'not find scaling group', six.text_type(ex))
563
 
 
564
 
        self.m.VerifyAll()
565
 
 
566
 
    def test_scaling_up_meta_update(self):
567
 
        t = template_format.parse(as_template)
568
 
 
569
 
        # Add CustomLB (just AWS::EC2::Instance) to template
570
 
        t['Resources']['MyCustomLB'] = {
571
 
            'Type': 'AWS::EC2::Instance',
572
 
            'ImageId': {'Ref': 'ImageId'},
573
 
            'InstanceType': 'bar',
574
 
            'Metadata': {
575
 
                'IPs': {'Fn::GetAtt': ['WebServerGroup', 'InstanceList']}
576
 
            }
577
 
        }
578
 
        stack = utils.parse_stack(t, params=self.params)
579
 
 
580
 
        # Create initial group
581
 
        self._stub_lb_reload(1)
582
 
        now = timeutils.utcnow()
583
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
584
 
        self._stub_create(1)
585
 
 
586
 
        self.m.ReplayAll()
587
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
588
 
        stack.resources['WebServerGroup'] = rsrc
589
 
        self.assertEqual(1, len(grouputils.get_member_names(rsrc)))
590
 
 
591
 
        # Scale up one
592
 
        self._stub_lb_reload(2)
593
 
        self._stub_meta_expected(now, 'ChangeInCapacity : 1', 2)
594
 
        self._stub_create(1)
595
 
 
596
 
        self.m.ReplayAll()
597
 
        up_policy = self.create_scaling_policy(t, stack,
598
 
                                               'WebServerScaleUpPolicy')
599
 
 
600
 
        alarm_url = up_policy.FnGetAtt('AlarmUrl')
601
 
        self.assertIsNotNone(alarm_url)
602
 
        up_policy.signal()
603
 
        self.assertEqual(2, len(grouputils.get_member_names(rsrc)))
604
 
 
605
 
        # Check CustomLB metadata was updated
606
 
        self.m.StubOutWithMock(instance.Instance, '_ipaddress')
607
 
        instance.Instance._ipaddress().MultipleTimes().AndReturn(
608
 
            '127.0.0.1')
609
 
        self.m.ReplayAll()
610
 
 
611
 
        expected_meta = {'IPs': u'127.0.0.1,127.0.0.1'}
612
 
        self.assertEqual(expected_meta, stack['MyCustomLB'].metadata_get())
613
 
 
614
 
        rsrc.delete()
615
 
        self.m.VerifyAll()
616
 
 
617
 
    def test_scaling_policy_update(self):
618
 
        t = template_format.parse(as_template)
619
 
        stack = utils.parse_stack(t, params=self.params)
620
 
 
621
 
        # Create initial group
622
 
        self._stub_lb_reload(1)
623
 
        now = timeutils.utcnow()
624
 
        self._stub_meta_expected(now, 'ExactCapacity : 1')
625
 
        self._stub_create(1)
626
 
 
627
 
        self.m.ReplayAll()
628
 
        rsrc = self.create_scaling_group(t, stack, 'WebServerGroup')
629
 
        stack.resources['WebServerGroup'] = rsrc
630
 
        self.assertEqual(1, len(grouputils.get_member_names(rsrc)))
631
 
 
632
 
        # Create initial scaling policy
633
 
        up_policy = self.create_scaling_policy(t, stack,
634
 
                                               'WebServerScaleUpPolicy')
635
 
 
636
 
        # Scale up one
637
 
        self._stub_lb_reload(2)
638
 
        self._stub_meta_expected(now, 'ChangeInCapacity : 1', 2)
639
 
        self._stub_create(1)
640
 
 
641
 
        self.m.ReplayAll()
642
 
 
643
 
        # Trigger alarm
644
 
        up_policy.signal()
645
 
        self.assertEqual(2, len(grouputils.get_member_names(rsrc)))
646
 
 
647
 
        # Update scaling policy
648
 
        props = copy.copy(up_policy.properties.data)
649
 
        props['ScalingAdjustment'] = '2'
650
 
        update_snippet = rsrc_defn.ResourceDefinition(up_policy.name,
651
 
                                                      up_policy.type(),
652
 
                                                      props)
653
 
        scheduler.TaskRunner(up_policy.update, update_snippet)()
654
 
        self.assertEqual(2, up_policy.properties['ScalingAdjustment'])
655
 
 
656
 
        # Now move time on 61 seconds - Cooldown in template is 60
657
 
        # so this should trigger a scale-up
658
 
        previous_meta = {timeutils.strtime(now): 'ChangeInCapacity : 1'}
659
 
        self.m.VerifyAll()
660
 
        self.m.UnsetStubs()
661
 
 
662
 
        self.m.StubOutWithMock(resource.Resource, 'metadata_get')
663
 
        up_policy.metadata_get().AndReturn(previous_meta)
664
 
        rsrc.metadata_get().AndReturn(previous_meta)
665
 
 
666
 
        # stub for the metadata accesses while creating the two instances
667
 
        resource.Resource.metadata_get()
668
 
        resource.Resource.metadata_get()
669
 
 
670
 
        now = now + datetime.timedelta(seconds=61)
671
 
 
672
 
        self._stub_lb_reload(4, unset=False)
673
 
        self._stub_meta_expected(now, 'ChangeInCapacity : 2', 2)
674
 
        self._stub_create(2)
675
 
        self.m.ReplayAll()
676
 
 
677
 
        # Trigger alarm
678
 
        up_policy.signal()
679
 
        self.assertEqual(4, len(grouputils.get_member_names(rsrc)))
680
 
 
681
 
        rsrc.delete()
682
 
        self.m.VerifyAll()
683
 
 
684
 
    def _stub_nova_server_get(self, not_found=False):
685
 
        mock_server = mock.MagicMock()
686
 
        mock_server.image = {'id': 'dd619705-468a-4f7d-8a06-b84794b3561a'}
687
 
        mock_server.flavor = {'id': '1'}
688
 
        mock_server.key_name = 'test'
689
 
        mock_server.security_groups = [{u'name': u'hth_test'}]
690
 
        if not_found:
691
 
            self.patchobject(nova.NovaClientPlugin, 'get_server',
692
 
                             side_effect=exception.ServerNotFound(
693
 
                                 server='5678'))
694
 
        else:
695
 
            self.patchobject(nova.NovaClientPlugin, 'get_server',
696
 
                             return_value=mock_server)
697
 
 
698
 
    def test_validate_without_InstanceId_and_LaunchConfigurationName(self):
699
 
        t = template_format.parse(as_template)
700
 
        agp = t['Resources']['WebServerGroup']['Properties']
701
 
        agp.pop('LaunchConfigurationName')
702
 
        agp.pop('LoadBalancerNames')
703
 
        stack = utils.parse_stack(t, params=self.params)
704
 
        rsrc = stack['WebServerGroup']
705
 
        error_msg = ("Either 'InstanceId' or 'LaunchConfigurationName' "
706
 
                     "must be provided.")
707
 
        exc = self.assertRaises(exception.StackValidationFailed,
708
 
                                rsrc.validate)
709
 
        self.assertIn(error_msg, six.text_type(exc))
710
 
 
711
 
    def test_validate_with_InstanceId_and_LaunchConfigurationName(self):
712
 
        t = template_format.parse(as_template)
713
 
        agp = t['Resources']['WebServerGroup']['Properties']
714
 
        agp['InstanceId'] = '5678'
715
 
        stack = utils.parse_stack(t, params=self.params)
716
 
        rsrc = stack['WebServerGroup']
717
 
        error_msg = ("Either 'InstanceId' or 'LaunchConfigurationName' "
718
 
                     "must be provided.")
719
 
        exc = self.assertRaises(exception.StackValidationFailed,
720
 
                                rsrc.validate)
721
 
        self.assertIn(error_msg, six.text_type(exc))
722
 
 
723
 
    def test_scaling_group_create_with_instanceid(self):
724
 
        t = template_format.parse(as_template)
725
 
        agp = t['Resources']['WebServerGroup']['Properties']
726
 
        agp['InstanceId'] = '5678'
727
 
        agp.pop('LaunchConfigurationName')
728
 
        agp.pop('LoadBalancerNames')
729
 
        stack = utils.parse_stack(t, params=self.params)
730
 
        rsrc = stack['WebServerGroup']
731
 
        self.stub_KeypairConstraint_validate()
732
 
        self._stub_nova_server_get()
733
 
        self._stub_create(1, with_lcn=False)
734
 
        self.m.ReplayAll()
735
 
 
736
 
        scheduler.TaskRunner(rsrc.create)()
737
 
        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
738
 
        instance_definition = rsrc._get_instance_definition()
739
 
        ins_props = instance_definition['Properties']
740
 
        self.assertEqual('dd619705-468a-4f7d-8a06-b84794b3561a',
741
 
                         ins_props['ImageId'])
742
 
        self.assertEqual('test', ins_props['KeyName'])
743
 
        self.assertEqual(['hth_test'], ins_props['SecurityGroups'])
744
 
        self.assertEqual('1', ins_props['InstanceType'])
745
 
 
746
 
        self.m.VerifyAll()
747
 
 
748
 
    def test_scaling_group_create_with_instanceid_not_found(self):
749
 
        t = template_format.parse(as_template)
750
 
        agp = t['Resources']['WebServerGroup']['Properties']
751
 
        agp.pop('LaunchConfigurationName')
752
 
        agp['InstanceId'] = '5678'
753
 
        stack = utils.parse_stack(t, params=self.params)
754
 
        rsrc = stack['WebServerGroup']
755
 
        self._stub_nova_server_get(not_found=True)
756
 
        self.m.ReplayAll()
757
 
        msg = ("Property error : WebServerGroup: InstanceId Error validating "
758
 
               "value '5678': The server (5678) could not be found")
759
 
        exc = self.assertRaises(exception.StackValidationFailed,
760
 
                                rsrc.validate)
761
 
        self.assertIn(msg, six.text_type(exc))
762
 
 
763
 
        self.m.VerifyAll()