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
22
from heat.common import template_format
23
from heat.engine import scheduler
24
from heat.engine import resource
25
from heat.engine.resources.rackspace import cloud_loadbalancer as lb
26
from heat.tests.common import HeatTestCase
27
from heat.tests.utils import setup_dummy_db
28
from heat.tests.utils import parse_stack
30
# The following fakes are for pyrax
33
class FakeClient(object):
38
class FakeManager(object):
47
def delete(self, item):
50
def create(self, *args, **kwargs):
53
def find(self, *args, **kwargs):
56
def action(self, item, action_type, body={}):
60
class FakeLoadBalancerManager(object):
61
def __init__(self, api=None, *args, **kwargs):
64
def set_content_caching(self, *args, **kwargs):
68
class FakeNode(object):
69
def __init__(self, address="0.0.0.0", port=80, condition=None, weight=None,
70
status=None, parent=None, type=None, id=None):
71
self.address = address
73
self.condition = condition
80
def __eq__(self, other):
81
return self.__dict__ == other.__dict__
83
def __ne__(self, other):
84
return not self.__eq__(other)
87
class FakeVirtualIP(object):
88
def __init__(self, address=None, port=None, condition=None,
89
ipVersion=None, type=None):
90
self.address = address
92
self.condition = condition
93
self.ipVersion = ipVersion
96
def __eq__(self, other):
97
return self.__dict__ == other.__dict__
99
def __ne__(self, other):
100
return not self.__eq__(other)
103
class FakeLoadBalancerClient(object):
104
def __init__(self, *args, **kwargs):
106
self.VirtualIP = FakeVirtualIP
109
def get(*args, **kwargs):
112
def create(*args, **kwargs):
116
class FakeLoadBalancer(object):
117
def __init__(self, name=None, info=None, *args, **kwargs):
118
name = name or uuid.uuid4()
119
info = info or {"fake": "fake"}
120
self.id = uuid.uuid4()
121
self.manager = FakeLoadBalancerManager()
123
self.VirtualIP = FakeVirtualIP
126
def get(*args, **kwargs):
129
def add_nodes(*args, **kwargs):
132
def add_ssl_termination(*args, **kwargs):
135
def set_error_page(*args, **kwargs):
138
def add_access_list(*args, **kwargs):
142
class LoadBalancerWithFakeClient(lb.CloudLoadBalancer):
144
return FakeLoadBalancerClient()
147
def override_resource():
149
'Rackspace::Cloud::LoadBalancer': LoadBalancerWithFakeClient
153
class LoadBalancerTest(HeatTestCase):
156
super(LoadBalancerTest, self).setUp()
159
"AWSTemplateFormatVersion": "2010-09-09",
160
"Description": "fawef",
162
self._get_lb_resource_name(): {
163
"Type": "Rackspace::Cloud::LoadBalancer",
166
"nodes": [{"address": "166.78.103.141", "port": 80,
167
"condition": "ENABLED"}],
171
{"type": "PUBLIC", "ipVersion": "IPV6"}],
172
"algorithm": 'LEAST_CONNECTIONS',
173
"connectionThrottle": {'maxConnectionRate': 1000},
175
'contentCaching': 'DISABLED'
181
self.lb_name = 'test-clb'
182
self.expected_body = {
183
"nodes": [FakeNode(address=u"166.78.103.141", port=80,
184
condition=u"ENABLED")],
187
"virtual_ips": [FakeVirtualIP(type=u"PUBLIC", ipVersion=u"IPV6")],
189
"algorithm": u'LEAST_CONNECTIONS',
190
"connectionThrottle": {'maxConnectionRate': 1000,
191
'maxConnections': None,
192
'rateInterval': None,
193
'minConnections': None},
194
"connectionLogging": None,
196
"healthMonitor": None,
198
"sessionPersistence": None,
202
lb.resource_mapping = override_resource
204
resource._register_class("Rackspace::Cloud::LoadBalancer",
205
LoadBalancerWithFakeClient)
207
def _get_lb_resource_name(self):
208
return "lb-" + str(uuid.uuid4())
210
def __getattribute__(self, name):
211
if name == 'expected_body' or name == 'lb_template':
212
return copy.deepcopy(super(LoadBalancerTest, self)
213
.__getattribute__(name))
214
return super(LoadBalancerTest, self).__getattribute__(name)
216
def _mock_create(self, t, stack, resource_name, lb_name, lb_body):
217
rsrc = LoadBalancerWithFakeClient(resource_name,
218
t['Resources'][resource_name],
220
self.m.StubOutWithMock(rsrc.clb, 'create')
221
fake_loadbalancer = FakeLoadBalancer(name=lb_name)
222
rsrc.clb.create(lb_name, **lb_body).AndReturn(fake_loadbalancer)
223
return (rsrc, fake_loadbalancer)
225
def _get_first_resource_name(self, templ):
226
return next(k for k in templ['Resources'])
228
def _random_name(self):
229
return ''.join(random.choice(string.ascii_uppercase)
232
def _mock_loadbalancer(self, lb_template, expected_name, expected_body):
233
t = template_format.parse(json.dumps(lb_template))
234
s = parse_stack(t, stack_name=self._random_name())
236
rsrc, fake_loadbalancer = self._mock_create(t, s,
238
_get_first_resource_name(
242
self.m.StubOutWithMock(fake_loadbalancer, 'get')
243
fake_loadbalancer.get().MultipleTimes().AndReturn(None)
245
fake_loadbalancer.status = 'ACTIVE'
247
return (rsrc, fake_loadbalancer)
249
def _set_template(self, templ, **kwargs):
250
for k, v in kwargs.iteritems():
251
templ['Resources'][self._get_first_resource_name(templ)][
255
def _set_expected(self, expected, **kwargs):
256
for k, v in kwargs.iteritems():
260
def test_alter_properties(self):
261
#test alter properties functions
262
template = self._set_template(self.lb_template,
263
sessionPersistence='HTTP_COOKIE',
264
connectionLogging=True,
265
metadata={'yolo': 'heeyyy_gurl'})
267
expected = self._set_expected(self.expected_body,
269
{'persistenceType': 'HTTP_COOKIE'},
270
connectionLogging={'enabled': True},
273
'value': 'heeyyy_gurl'}])
275
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
280
scheduler.TaskRunner(rsrc.create)()
283
def test_validate_half_closed(self):
284
#test failure (invalid protocol)
285
template = self._set_template(self.lb_template, halfClosed=True)
286
expected = self._set_expected(self.expected_body, halfClosed=True)
287
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
290
self.assertEquals(rsrc.validate(), {
292
'The halfClosed property is only available for the '
293
'TCP or TCP_CLIENT_FIRST protocols'})
296
template = self._set_template(template, protocol='TCP')
297
expected = self._set_expected(expected, protocol='TCP')
298
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
301
self.assertEquals(rsrc.validate(), None)
303
#test TCP_CLIENT_FIRST protocol
304
template = self._set_template(template,
305
protocol='TCP_CLIENT_FIRST')
306
expected = self._set_expected(expected,
307
protocol='TCP_CLIENT_FIRST')
308
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
311
self.assertEquals(rsrc.validate(), None)
313
def test_validate_health_monitor(self):
314
#test connect success
317
'attemptsBeforeDeactivation': 1,
321
template = self._set_template(self.lb_template,
322
healthMonitor=health_monitor)
323
expected = self._set_expected(self.expected_body,
324
healthMonitor=health_monitor)
325
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
329
self.assertEquals(rsrc.validate(), None)
331
#test connect failure
332
#bodyRegex is only valid for type 'HTTP(S)'
333
health_monitor['bodyRegex'] = 'dfawefawe'
334
template = self._set_template(template,
335
healthMonitor=health_monitor)
336
expected = self._set_expected(expected,
337
healthMonitor=health_monitor)
338
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
341
self.assertEquals(rsrc.validate(),
342
{'Error': 'Unknown Property bodyRegex'})
345
health_monitor['type'] = 'HTTP'
346
health_monitor['bodyRegex'] = 'bodyRegex'
347
health_monitor['statusRegex'] = 'statusRegex'
348
health_monitor['hostHeader'] = 'hostHeader'
349
health_monitor['path'] = 'path'
351
template = self._set_template(template,
352
healthMonitor=health_monitor)
353
expected = self._set_expected(expected,
354
healthMonitor=health_monitor)
355
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
358
self.assertEquals(rsrc.validate(), None)
360
def test_validate_ssl_termination(self):
363
'privatekey': 'ewfawe',
364
'certificate': 'dfaewfwef',
365
'intermediateCertificate': 'fwaefawe',
366
'secureTrafficOnly': True
369
#test ssl termination enabled without required fields failure
370
template = self._set_template(self.lb_template,
371
sslTermination=ssl_termination)
372
expected = self._set_expected(self.expected_body,
373
sslTermination=ssl_termination)
374
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
377
self.assertEquals(rsrc.validate(),
379
'Property error : %s: Property securePort not '
380
'assigned' % rsrc.name})
382
ssl_termination['securePort'] = 443
383
template = self._set_template(template,
384
sslTermination=ssl_termination)
385
expected = self._set_expected(expected,
386
sslTermination=ssl_termination)
387
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
390
self.assertEquals(rsrc.validate(), None)
392
def test_post_creation_access_list(self):
393
access_list = [{"address": '192.168.1.1/0',
395
{'address': '172.165.3.43',
398
template = self._set_template(self.lb_template,
399
accessList=access_list)
400
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
403
self.m.StubOutWithMock(fake_loadbalancer, 'add_access_list')
404
fake_loadbalancer.add_access_list(access_list)
407
scheduler.TaskRunner(rsrc.create)()
410
def test_post_creation_error_page(self):
411
error_page = "REALLY BIG ERROR"
413
template = self._set_template(self.lb_template,
414
errorPage=error_page)
415
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
418
self.m.StubOutWithMock(fake_loadbalancer, 'set_error_page')
419
fake_loadbalancer.set_error_page(error_page)
422
scheduler.TaskRunner(rsrc.create)()
425
def test_post_creation_ssl_termination(self):
428
'privatekey': 'afwefawe',
429
'certificate': 'fawefwea',
430
'intermediateCertificate': "intermediate_certificate",
432
'secureTrafficOnly': False
435
template = self._set_template(self.lb_template,
436
sslTermination=ssl_termination)
437
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
440
self.m.StubOutWithMock(fake_loadbalancer, 'add_ssl_termination')
441
fake_loadbalancer.add_ssl_termination(
442
ssl_termination['securePort'],
443
ssl_termination['privatekey'],
444
ssl_termination['certificate'],
445
intermediateCertificate=ssl_termination['intermediateCertificate'],
446
enabled=ssl_termination['enabled'],
447
secureTrafficOnly=ssl_termination['secureTrafficOnly'])
450
scheduler.TaskRunner(rsrc.create)()
453
def test_post_creation_content_caching(self):
454
template = self._set_template(self.lb_template,
455
contentCaching='ENABLED')
456
rsrc, fake_loadbalancer = self._mock_loadbalancer(template,
460
scheduler.TaskRunner(rsrc.create)()
463
def test_update_add_node_by_ref(self):
464
added_node = {'nodes': [
465
{"address": "166.78.103.141", "port": 80, "condition": "ENABLED"},
466
{"ref": "TEST_NODE_REF", "port": 80, "condition": "ENABLED"}]}
467
expected_ip = '172.168.1.4'
468
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
471
fake_loadbalancer.nodes = self.expected_body['nodes']
473
scheduler.TaskRunner(rsrc.create)()
476
self.m.StubOutWithMock(rsrc.clb, 'get')
477
rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
479
self.m.StubOutWithMock(rsrc.stack, 'resource_by_refid')
481
class FakeFn(object):
482
def FnGetAtt(self, attr):
485
rsrc.stack.resource_by_refid('TEST_NODE_REF').AndReturn(FakeFn())
487
self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes')
488
fake_loadbalancer.add_nodes([
489
fake_loadbalancer.Node(address=expected_ip,
491
condition='ENABLED')])
494
rsrc.handle_update({}, {}, added_node)
497
def test_update_add_node_by_address(self):
498
expected_ip = '172.168.1.4'
499
added_node = {'nodes': [
500
{"address": "166.78.103.141", "port": 80, "condition": "ENABLED"},
501
{"address": expected_ip, "port": 80, "condition": "ENABLED"}]}
502
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
505
fake_loadbalancer.nodes = self.expected_body['nodes']
507
scheduler.TaskRunner(rsrc.create)()
510
self.m.StubOutWithMock(rsrc.clb, 'get')
511
rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
513
self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes')
514
fake_loadbalancer.add_nodes([
515
fake_loadbalancer.Node(address=expected_ip,
517
condition='ENABLED')])
520
rsrc.handle_update({}, {}, added_node)
523
def test_update_delete_node_failed(self):
524
deleted_node = {'nodes': []}
525
rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template,
528
fake_loadbalancer.nodes = self.expected_body['nodes']
530
scheduler.TaskRunner(rsrc.create)()
533
self.m.StubOutWithMock(rsrc.clb, 'get')
534
rsrc.clb.get(rsrc.resource_id).AndReturn(fake_loadbalancer)
537
self.assertRaises(ValueError, rsrc.handle_update, {}, {}, deleted_node)