1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
# not use this file except in compliance with the License. You may obtain
5
# a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
# License for the specific language governing permissions and limitations
17
from testtools import skipIf
19
from heat.common import exception
20
from heat.common import template_format
21
from heat.engine import clients
22
from heat.engine import scheduler
23
from heat.engine.resources.neutron import loadbalancer
24
from heat.openstack.common.importutils import try_import
25
from heat.tests import fakes
26
from heat.tests import utils
27
from heat.tests.common import HeatTestCase
28
from heat.tests.v1_1 import fakes as nova_fakes
30
neutronclient = try_import('neutronclient.v2_0.client')
32
health_monitor_template = '''
34
"AWSTemplateFormatVersion" : "2010-09-09",
35
"Description" : "Template to test load balancer resources",
39
"Type": "OS::Neutron::HealthMonitor",
53
"AWSTemplateFormatVersion" : "2010-09-09",
54
"Description" : "Template to test load balancer resources",
58
"Type": "OS::Neutron::Pool",
61
"subnet_id": "sub123",
62
"lb_method": "ROUND_ROBIN",
74
"AWSTemplateFormatVersion" : "2010-09-09",
75
"Description" : "Template to test load balancer resources",
79
"Type": "OS::Neutron::LoadBalancer",
81
"protocol_port": 8080,
91
@skipIf(neutronclient is None, 'neutronclient unavailable')
92
class HealthMonitorTest(HeatTestCase):
95
super(HealthMonitorTest, self).setUp()
96
self.m.StubOutWithMock(neutronclient.Client, 'create_health_monitor')
97
self.m.StubOutWithMock(neutronclient.Client, 'delete_health_monitor')
98
self.m.StubOutWithMock(neutronclient.Client, 'show_health_monitor')
99
self.m.StubOutWithMock(neutronclient.Client, 'update_health_monitor')
100
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
101
utils.setup_dummy_db()
103
def create_health_monitor(self):
104
clients.OpenStackClients.keystone().AndReturn(
105
fakes.FakeKeystoneClient())
106
neutronclient.Client.create_health_monitor({
108
'delay': 3, 'max_retries': 5, 'type': u'HTTP',
109
'timeout': 10, 'admin_state_up': True}}
110
).AndReturn({'health_monitor': {'id': '5678'}})
112
snippet = template_format.parse(health_monitor_template)
113
stack = utils.parse_stack(snippet)
114
return loadbalancer.HealthMonitor(
115
'monitor', snippet['Resources']['monitor'], stack)
117
def test_create(self):
118
rsrc = self.create_health_monitor()
120
scheduler.TaskRunner(rsrc.create)()
121
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
124
def test_create_failed(self):
125
clients.OpenStackClients.keystone().AndReturn(
126
fakes.FakeKeystoneClient())
127
neutronclient.Client.create_health_monitor({
129
'delay': 3, 'max_retries': 5, 'type': u'HTTP',
130
'timeout': 10, 'admin_state_up': True}}
131
).AndRaise(loadbalancer.NeutronClientException())
134
snippet = template_format.parse(health_monitor_template)
135
stack = utils.parse_stack(snippet)
136
rsrc = loadbalancer.HealthMonitor(
137
'monitor', snippet['Resources']['monitor'], stack)
138
error = self.assertRaises(exception.ResourceFailure,
139
scheduler.TaskRunner(rsrc.create))
141
'NeutronClientException: An unknown exception occurred.',
143
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
146
def test_delete(self):
147
neutronclient.Client.delete_health_monitor('5678')
148
neutronclient.Client.show_health_monitor('5678').AndRaise(
149
loadbalancer.NeutronClientException(status_code=404))
151
rsrc = self.create_health_monitor()
153
scheduler.TaskRunner(rsrc.create)()
154
scheduler.TaskRunner(rsrc.delete)()
155
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
158
def test_delete_already_gone(self):
159
neutronclient.Client.delete_health_monitor('5678').AndRaise(
160
loadbalancer.NeutronClientException(status_code=404))
162
rsrc = self.create_health_monitor()
164
scheduler.TaskRunner(rsrc.create)()
165
scheduler.TaskRunner(rsrc.delete)()
166
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
169
def test_delete_failed(self):
170
neutronclient.Client.delete_health_monitor('5678').AndRaise(
171
loadbalancer.NeutronClientException(status_code=400))
173
rsrc = self.create_health_monitor()
175
scheduler.TaskRunner(rsrc.create)()
176
error = self.assertRaises(exception.ResourceFailure,
177
scheduler.TaskRunner(rsrc.delete))
179
'NeutronClientException: An unknown exception occurred.',
181
self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
184
def test_attribute(self):
185
rsrc = self.create_health_monitor()
186
neutronclient.Client.show_health_monitor('5678').MultipleTimes(
188
{'health_monitor': {'admin_state_up': True, 'delay': 3}})
190
scheduler.TaskRunner(rsrc.create)()
191
self.assertEqual(True, rsrc.FnGetAtt('admin_state_up'))
192
self.assertEqual(3, rsrc.FnGetAtt('delay'))
195
def test_attribute_failed(self):
196
rsrc = self.create_health_monitor()
198
scheduler.TaskRunner(rsrc.create)()
199
error = self.assertRaises(exception.InvalidTemplateAttribute,
200
rsrc.FnGetAtt, 'subnet_id')
202
'The Referenced Attribute (monitor subnet_id) is incorrect.',
206
def test_update(self):
207
rsrc = self.create_health_monitor()
208
neutronclient.Client.update_health_monitor(
209
'5678', {'health_monitor': {'delay': 10}})
211
scheduler.TaskRunner(rsrc.create)()
213
update_template = copy.deepcopy(rsrc.t)
214
update_template['Properties']['delay'] = 10
215
scheduler.TaskRunner(rsrc.update, update_template)()
220
@skipIf(neutronclient is None, 'neutronclient unavailable')
221
class PoolTest(HeatTestCase):
224
super(PoolTest, self).setUp()
225
self.m.StubOutWithMock(neutronclient.Client, 'create_pool')
226
self.m.StubOutWithMock(neutronclient.Client, 'delete_pool')
227
self.m.StubOutWithMock(neutronclient.Client, 'show_pool')
228
self.m.StubOutWithMock(neutronclient.Client, 'update_pool')
229
self.m.StubOutWithMock(neutronclient.Client,
230
'associate_health_monitor')
231
self.m.StubOutWithMock(neutronclient.Client,
232
'disassociate_health_monitor')
233
self.m.StubOutWithMock(neutronclient.Client, 'create_vip')
234
self.m.StubOutWithMock(neutronclient.Client, 'delete_vip')
235
self.m.StubOutWithMock(neutronclient.Client, 'show_vip')
236
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
237
utils.setup_dummy_db()
239
def create_pool(self):
240
clients.OpenStackClients.keystone().AndReturn(
241
fakes.FakeKeystoneClient())
242
neutronclient.Client.create_pool({
244
'subnet_id': 'sub123', 'protocol': u'HTTP',
245
'name': utils.PhysName('test_stack', 'pool'),
246
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
247
).AndReturn({'pool': {'id': '5678'}})
248
neutronclient.Client.create_vip({
250
'protocol': u'HTTP', 'name': 'pool.vip',
251
'admin_state_up': True, 'subnet_id': u'sub123',
252
'pool_id': '5678', 'protocol_port': 80}}
253
).AndReturn({'vip': {'id': 'xyz'}})
254
neutronclient.Client.show_pool('5678').AndReturn(
255
{'pool': {'status': 'ACTIVE'}})
256
neutronclient.Client.show_vip('xyz').AndReturn(
257
{'vip': {'status': 'ACTIVE'}})
259
snippet = template_format.parse(pool_template)
260
stack = utils.parse_stack(snippet)
261
return loadbalancer.Pool(
262
'pool', snippet['Resources']['pool'], stack)
264
def test_create(self):
265
rsrc = self.create_pool()
267
scheduler.TaskRunner(rsrc.create)()
268
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
271
def test_create_pending(self):
272
clients.OpenStackClients.keystone().AndReturn(
273
fakes.FakeKeystoneClient())
274
neutronclient.Client.create_pool({
276
'subnet_id': 'sub123', 'protocol': u'HTTP',
277
'name': utils.PhysName('test_stack', 'pool'),
278
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
279
).AndReturn({'pool': {'id': '5678'}})
280
neutronclient.Client.create_vip({
282
'protocol': u'HTTP', 'name': 'pool.vip',
283
'admin_state_up': True, 'subnet_id': u'sub123',
284
'pool_id': '5678', 'protocol_port': 80}}
285
).AndReturn({'vip': {'id': 'xyz'}})
286
neutronclient.Client.show_pool('5678').AndReturn(
287
{'pool': {'status': 'PENDING_CREATE'}})
288
neutronclient.Client.show_pool('5678').MultipleTimes().AndReturn(
289
{'pool': {'status': 'ACTIVE'}})
290
neutronclient.Client.show_vip('xyz').AndReturn(
291
{'vip': {'status': 'PENDING_CREATE'}})
292
neutronclient.Client.show_vip('xyz').AndReturn(
293
{'vip': {'status': 'ACTIVE'}})
295
snippet = template_format.parse(pool_template)
296
stack = utils.parse_stack(snippet)
297
rsrc = loadbalancer.Pool(
298
'pool', snippet['Resources']['pool'], stack)
300
scheduler.TaskRunner(rsrc.create)()
301
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
304
def test_create_failed_unexpected_status(self):
305
clients.OpenStackClients.keystone().AndReturn(
306
fakes.FakeKeystoneClient())
307
neutronclient.Client.create_pool({
309
'subnet_id': 'sub123', 'protocol': u'HTTP',
310
'name': utils.PhysName('test_stack', 'pool'),
311
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
312
).AndReturn({'pool': {'id': '5678'}})
313
neutronclient.Client.create_vip({
315
'protocol': u'HTTP', 'name': 'pool.vip',
316
'admin_state_up': True, 'subnet_id': u'sub123',
317
'pool_id': '5678', 'protocol_port': 80}}
318
).AndReturn({'vip': {'id': 'xyz'}})
319
neutronclient.Client.show_pool('5678').AndReturn(
320
{'pool': {'status': 'ERROR', 'name': '5678'}})
322
snippet = template_format.parse(pool_template)
323
stack = utils.parse_stack(snippet)
324
rsrc = loadbalancer.Pool(
325
'pool', snippet['Resources']['pool'], stack)
327
error = self.assertRaises(exception.ResourceFailure,
328
scheduler.TaskRunner(rsrc.create))
330
'Error: neutron report unexpected pool '
331
'resource[5678] status[ERROR]',
333
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
336
def test_create_failed_unexpected_vip_status(self):
337
clients.OpenStackClients.keystone().AndReturn(
338
fakes.FakeKeystoneClient())
339
neutronclient.Client.create_pool({
341
'subnet_id': 'sub123', 'protocol': u'HTTP',
342
'name': utils.PhysName('test_stack', 'pool'),
343
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
344
).AndReturn({'pool': {'id': '5678'}})
345
neutronclient.Client.create_vip({
347
'protocol': u'HTTP', 'name': 'pool.vip',
348
'admin_state_up': True, 'subnet_id': u'sub123',
349
'pool_id': '5678', 'protocol_port': 80}}
350
).AndReturn({'vip': {'id': 'xyz'}})
351
neutronclient.Client.show_pool('5678').MultipleTimes().AndReturn(
352
{'pool': {'status': 'ACTIVE'}})
353
neutronclient.Client.show_vip('xyz').AndReturn(
354
{'vip': {'status': 'ERROR', 'name': 'xyz'}})
356
snippet = template_format.parse(pool_template)
357
stack = utils.parse_stack(snippet)
358
rsrc = loadbalancer.Pool(
359
'pool', snippet['Resources']['pool'], stack)
361
error = self.assertRaises(exception.ResourceFailure,
362
scheduler.TaskRunner(rsrc.create))
364
'Error: neutron reported unexpected vip '
365
'resource[xyz] status[ERROR]',
367
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
370
def test_create_failed(self):
371
clients.OpenStackClients.keystone().AndReturn(
372
fakes.FakeKeystoneClient())
373
neutronclient.Client.create_pool({
375
'subnet_id': 'sub123', 'protocol': u'HTTP',
376
'name': utils.PhysName('test_stack', 'pool'),
377
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
378
).AndRaise(loadbalancer.NeutronClientException())
381
snippet = template_format.parse(pool_template)
382
stack = utils.parse_stack(snippet)
383
rsrc = loadbalancer.Pool(
384
'pool', snippet['Resources']['pool'], stack)
385
error = self.assertRaises(exception.ResourceFailure,
386
scheduler.TaskRunner(rsrc.create))
388
'NeutronClientException: An unknown exception occurred.',
390
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
393
def test_delete(self):
394
rsrc = self.create_pool()
395
neutronclient.Client.delete_vip('xyz')
396
neutronclient.Client.show_vip('xyz').AndRaise(
397
loadbalancer.NeutronClientException(status_code=404))
398
neutronclient.Client.delete_pool('5678')
399
neutronclient.Client.show_pool('5678').AndRaise(
400
loadbalancer.NeutronClientException(status_code=404))
402
scheduler.TaskRunner(rsrc.create)()
403
scheduler.TaskRunner(rsrc.delete)()
404
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
407
def test_delete_already_gone(self):
408
neutronclient.Client.delete_vip('xyz').AndRaise(
409
loadbalancer.NeutronClientException(status_code=404))
410
neutronclient.Client.delete_pool('5678').AndRaise(
411
loadbalancer.NeutronClientException(status_code=404))
413
rsrc = self.create_pool()
415
scheduler.TaskRunner(rsrc.create)()
416
scheduler.TaskRunner(rsrc.delete)()
417
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
420
def test_delete_vip_failed(self):
421
neutronclient.Client.delete_vip('xyz').AndRaise(
422
loadbalancer.NeutronClientException(status_code=400))
424
rsrc = self.create_pool()
426
scheduler.TaskRunner(rsrc.create)()
427
error = self.assertRaises(exception.ResourceFailure,
428
scheduler.TaskRunner(rsrc.delete))
430
'NeutronClientException: An unknown exception occurred.',
432
self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
435
def test_delete_failed(self):
436
neutronclient.Client.delete_vip('xyz').AndRaise(
437
loadbalancer.NeutronClientException(status_code=404))
438
neutronclient.Client.delete_pool('5678').AndRaise(
439
loadbalancer.NeutronClientException(status_code=400))
441
rsrc = self.create_pool()
443
scheduler.TaskRunner(rsrc.create)()
444
error = self.assertRaises(exception.ResourceFailure,
445
scheduler.TaskRunner(rsrc.delete))
447
'NeutronClientException: An unknown exception occurred.',
449
self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
452
def test_attribute(self):
453
rsrc = self.create_pool()
454
neutronclient.Client.show_pool('5678').MultipleTimes(
456
{'pool': {'admin_state_up': True, 'lb_method': 'ROUND_ROBIN'}})
458
scheduler.TaskRunner(rsrc.create)()
459
self.assertEqual(True, rsrc.FnGetAtt('admin_state_up'))
460
self.assertEqual('ROUND_ROBIN', rsrc.FnGetAtt('lb_method'))
463
def test_vip_attribute(self):
464
rsrc = self.create_pool()
465
neutronclient.Client.show_vip('xyz').AndReturn(
466
{'vip': {'address': '10.0.0.3', 'name': 'xyz'}})
468
scheduler.TaskRunner(rsrc.create)()
469
self.assertEqual({'address': '10.0.0.3', 'name': 'xyz'},
470
rsrc.FnGetAtt('vip'))
473
def test_attribute_failed(self):
474
rsrc = self.create_pool()
476
scheduler.TaskRunner(rsrc.create)()
477
error = self.assertRaises(exception.InvalidTemplateAttribute,
478
rsrc.FnGetAtt, 'net_id')
480
'The Referenced Attribute (pool net_id) is incorrect.',
484
def test_update(self):
485
rsrc = self.create_pool()
486
neutronclient.Client.update_pool(
487
'5678', {'pool': {'admin_state_up': False}})
489
scheduler.TaskRunner(rsrc.create)()
491
update_template = copy.deepcopy(rsrc.t)
492
update_template['Properties']['admin_state_up'] = False
493
scheduler.TaskRunner(rsrc.update, update_template)()
497
def test_update_monitors(self):
498
clients.OpenStackClients.keystone().AndReturn(
499
fakes.FakeKeystoneClient())
500
neutronclient.Client.create_pool({
502
'subnet_id': 'sub123', 'protocol': u'HTTP',
503
'name': utils.PhysName('test_stack', 'pool'),
504
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
505
).AndReturn({'pool': {'id': '5678'}})
506
neutronclient.Client.associate_health_monitor(
507
'5678', {'health_monitor': {'id': 'mon123'}})
508
neutronclient.Client.associate_health_monitor(
509
'5678', {'health_monitor': {'id': 'mon456'}})
510
neutronclient.Client.create_vip({
512
'protocol': u'HTTP', 'name': 'pool.vip',
513
'admin_state_up': True, 'subnet_id': u'sub123',
514
'pool_id': '5678', 'protocol_port': 80}}
515
).AndReturn({'vip': {'id': 'xyz'}})
516
neutronclient.Client.show_pool('5678').AndReturn(
517
{'pool': {'status': 'ACTIVE'}})
518
neutronclient.Client.show_vip('xyz').AndReturn(
519
{'vip': {'status': 'ACTIVE'}})
520
neutronclient.Client.disassociate_health_monitor(
521
'5678', {'health_monitor': {'id': 'mon456'}})
522
neutronclient.Client.associate_health_monitor(
523
'5678', {'health_monitor': {'id': 'mon789'}})
525
snippet = template_format.parse(pool_template)
526
stack = utils.parse_stack(snippet)
527
snippet['Resources']['pool']['Properties']['monitors'] = [
529
rsrc = loadbalancer.Pool(
530
'pool', snippet['Resources']['pool'], stack)
532
scheduler.TaskRunner(rsrc.create)()
534
update_template = copy.deepcopy(rsrc.t)
535
update_template['Properties']['monitors'] = ['mon123', 'mon789']
536
scheduler.TaskRunner(rsrc.update, update_template)()
541
@skipIf(neutronclient is None, 'neutronclient unavailable')
542
class LoadBalancerTest(HeatTestCase):
545
super(LoadBalancerTest, self).setUp()
546
self.fc = nova_fakes.FakeClient()
547
self.m.StubOutWithMock(neutronclient.Client, 'create_member')
548
self.m.StubOutWithMock(neutronclient.Client, 'delete_member')
549
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
550
self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
551
utils.setup_dummy_db()
553
def create_load_balancer(self):
554
clients.OpenStackClients.keystone().AndReturn(
555
fakes.FakeKeystoneClient())
556
clients.OpenStackClients.nova("compute").MultipleTimes().AndReturn(
558
neutronclient.Client.create_member({
560
'pool_id': 'pool123', 'protocol_port': 8080,
561
'address': '1.2.3.4'}}
562
).AndReturn({'member': {'id': 'member5678'}})
563
snippet = template_format.parse(lb_template)
564
stack = utils.parse_stack(snippet)
565
return loadbalancer.LoadBalancer(
566
'lb', snippet['Resources']['lb'], stack)
568
def test_create(self):
569
rsrc = self.create_load_balancer()
572
scheduler.TaskRunner(rsrc.create)()
573
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
576
def test_update(self):
577
rsrc = self.create_load_balancer()
578
neutronclient.Client.delete_member(u'member5678')
579
neutronclient.Client.create_member({
581
'pool_id': 'pool123', 'protocol_port': 8080,
582
'address': '4.5.6.7'}}
583
).AndReturn({'member': {'id': 'memberxyz'}})
586
scheduler.TaskRunner(rsrc.create)()
588
update_template = copy.deepcopy(rsrc.t)
589
update_template['Properties']['members'] = ['5678']
591
scheduler.TaskRunner(rsrc.update, update_template)()
594
def test_update_missing_member(self):
595
rsrc = self.create_load_balancer()
596
neutronclient.Client.delete_member(u'member5678').AndRaise(
597
loadbalancer.NeutronClientException(status_code=404))
600
scheduler.TaskRunner(rsrc.create)()
602
update_template = copy.deepcopy(rsrc.t)
603
update_template['Properties']['members'] = []
605
scheduler.TaskRunner(rsrc.update, update_template)()
606
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
609
def test_delete(self):
610
rsrc = self.create_load_balancer()
611
neutronclient.Client.delete_member(u'member5678')
614
scheduler.TaskRunner(rsrc.create)()
615
scheduler.TaskRunner(rsrc.delete)()
616
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
619
def test_delete_missing_member(self):
620
rsrc = self.create_load_balancer()
621
neutronclient.Client.delete_member(u'member5678').AndRaise(
622
loadbalancer.NeutronClientException(status_code=404))
625
scheduler.TaskRunner(rsrc.create)()
626
scheduler.TaskRunner(rsrc.delete)()
627
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)