blob: 7fec3244d23888656bc6884c1d1bb3f102493f4f [file] [log] [blame]
Jude Cross638c4ef2017-07-24 14:57:20 -07001# Copyright 2017 GoDaddy
2# Copyright 2017 Catalyst IT Ltd
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15import time
16
17from oslo_log import log as logging
18import requests
19from tempest import config
20from tempest.lib.common.utils import data_utils
21from tempest.lib.common.utils import test_utils
22from tempest.lib import exceptions as lib_exc
23from tempest import test
24import tenacity
25
26from octavia_tempest_plugin.tests import server_util
27
28CONF = config.CONF
29LOG = logging.getLogger(__name__)
30
31
32class BaseLoadbalancerTest(test.BaseTestCase):
33 credentials = (['lbmember', CONF.loadbalancer.member_role], 'admin')
34 name_prefix = 'Tempest-BaseLoadbalancerTest'
35 vip_network_id = None
36 vip_subnet_id = None
37 vip_address = None
38 member_subnet_id = None
39 member_network_id = None
40 vm_ip = None
41
42 @classmethod
43 def skip_checks(cls):
44 super(BaseLoadbalancerTest, cls).skip_checks()
45
46 if not CONF.service_available.loadbalancer:
47 raise cls.skipException("Loadbalancing service is not available.")
48
49 service_list = {
50 'loadbalancing': CONF.service_available.loadbalancer,
51 'compute': CONF.service_available.nova,
52 'image': CONF.service_available.glance,
53 'neutron': CONF.service_available.neutron
54 }
55 for srv, available in service_list.items():
56 if not available:
57 raise cls.skipException("Service %s is not available." % srv)
58
59 @classmethod
60 def setup_clients(cls):
61 super(BaseLoadbalancerTest, cls).setup_clients()
62
63 cls.lb_client = cls.os_roles_lbmember.octavia_v2.LoadbalancerClient()
64 cls.servers_client = cls.os_roles_lbmember.servers_client
65 cls.networks_client = cls.os_roles_lbmember.networks_client
66 cls.subnets_client = cls.os_roles_lbmember.subnets_client
67 cls.interfaces_client = cls.os_roles_lbmember.interfaces_client
68 cls.sg_rule_client = cls.os_roles_lbmember.security_group_rules_client
69 cls.floatingip_client = cls.os_roles_lbmember.floating_ips_client
70 cls.floatingip_adm_client = cls.os_admin.floating_ips_client
71 cls.routers_adm_client = cls.os_admin.routers_client
72
73 if CONF.identity.auth_version == 'v3':
74 project_id = cls.os_roles_lbmember.auth_provider.auth_data[1][
75 'project']['id']
76 else:
77 project_id = cls.os_roles_lbmember.auth_provider.auth_data[
78 1]['token']['tenant']['id']
79
80 cls.tenant_id = project_id
81 cls.user_id = cls.os_roles_lbmember.auth_provider.auth_data[1][
82 'user']['id']
83
84 @classmethod
85 def resource_setup(cls):
86 """Creates network resources."""
87 super(BaseLoadbalancerTest, cls).resource_setup()
88 if not CONF.loadbalancer.vip_network_id:
89 network_name = data_utils.rand_name(
90 'network',
91 prefix=cls.name_prefix
92 )
93 body = cls.networks_client.create_network(name=network_name)
94 cls.vip_network_id = body['network']['id']
95 cls.addClassResourceCleanup(
96 test_utils.call_and_ignore_notfound_exc,
97 cls.networks_client.delete_network,
98 cls.vip_network_id
99 )
100
101 subnet_name = data_utils.rand_name(
102 'subnet',
103 prefix=cls.name_prefix
104 )
105 body = cls.subnets_client.create_subnet(
106 name=subnet_name,
107 network_id=cls.vip_network_id,
108 cidr='10.100.1.0/24',
109 ip_version=4,
110 gateway_ip='10.100.1.1',
111 )
112 cls.vip_subnet_id = body['subnet']['id']
113 cls.addClassResourceCleanup(
114 test_utils.call_and_ignore_notfound_exc,
115 cls.subnets_client.delete_subnet,
116 cls.vip_subnet_id
117 )
118 cls.member_network_id = cls.vip_network_id
119 cls.member_subnet_id = cls.vip_subnet_id
120
121 if CONF.validation.connect_method == 'floating':
122 router_name = data_utils.rand_name(
123 'router',
124 prefix=cls.name_prefix
125 )
126 kwargs = {
127 'name': router_name,
128 'tenant_id': cls.tenant_id
129 }
130 if CONF.network.public_network_id:
131 kwargs['external_gateway_info'] = dict(
132 network_id=CONF.network.public_network_id
133 )
134 body = cls.routers_adm_client.create_router(**kwargs)
135 cls.router_id = body['router']['id']
136 cls.addClassResourceCleanup(
137 test_utils.call_and_ignore_notfound_exc,
138 cls.routers_adm_client.delete_router,
139 cls.router_id,
140 )
141
142 cls.routers_adm_client.add_router_interface(
143 cls.router_id, subnet_id=cls.member_subnet_id
144 )
145 cls.addClassResourceCleanup(
146 test_utils.call_and_ignore_notfound_exc,
147 cls.routers_adm_client.remove_router_interface,
148 cls.router_id,
149 subnet_id=cls.member_subnet_id
150 )
151 else:
152 cls.vip_network_id = CONF.loadbalancer.vip_network_id
153 cls.vip_subnet_id = CONF.loadbalancer.vip_subnet_id
154 cls.member_subnet_id = CONF.loadbalancer.premade_server_subnet_id
155
156 @tenacity.retry(
157 wait=tenacity.wait_fixed(CONF.loadbalancer.lb_build_interval),
158 stop=tenacity.stop_after_delay(CONF.loadbalancer.lb_build_timeout),
159 retry=tenacity.retry_if_exception_type(AssertionError)
160 )
161 def await_loadbalancer_active(self, id, name=None):
162 resp, body = self.lb_client.get_obj('loadbalancers', id)
163 self.assertEqual(200, resp.status)
164
165 lb = body['loadbalancer']
166
167 if lb['provisioning_status'] == 'ERROR':
168 raise Exception('Failed to wait for loadbalancer to be active, '
169 'actual provisioning_status: ERROR')
170
171 self.assertEqual('ACTIVE', lb['provisioning_status'])
172
173 if name:
174 self.assertEqual(name, lb['name'])
175
176 @tenacity.retry(
177 wait=tenacity.wait_fixed(CONF.loadbalancer.lb_build_interval),
178 stop=tenacity.stop_after_delay(CONF.loadbalancer.lb_build_timeout),
179 retry=tenacity.retry_if_exception_type(AssertionError)
180 )
181 def await_loadbalancer_deleted(self, id):
182 resp, body = self.lb_client.get_obj('loadbalancers', id)
183 self.assertEqual(200, resp.status)
184
185 lb = body['loadbalancer']
186 self.assertEqual('DELETED', lb['provisioning_status'])
187
188 @tenacity.retry(
189 wait=tenacity.wait_fixed(CONF.loadbalancer.lb_build_interval),
190 stop=tenacity.stop_after_delay(CONF.loadbalancer.lb_build_timeout),
191 retry=tenacity.retry_if_exception_type(AssertionError)
192 )
193 def await_listener_active(self, id, name=None):
194 resp, body = self.lb_client.get_obj('listeners', id)
195 self.assertEqual(200, resp.status)
196
197 listener = body['listener']
198
199 if listener['provisioning_status'] == 'ERROR':
200 raise Exception('Failed to wait for listener to be active, actual '
201 'provisioning_status: ERROR')
202
203 self.assertEqual('ACTIVE', listener['provisioning_status'])
204 self.assertEqual('ONLINE', listener['operating_status'])
205
206 if name:
207 self.assertEqual(name, listener['name'])
208
209 def create_loadbalancer(self, **kwargs):
210 name = data_utils.rand_name('lb', prefix=self.name_prefix)
211 payload = {'loadbalancer': {'name': name}}
212 payload['loadbalancer'].update(kwargs)
213
214 resp, body = self.lb_client.post_json('loadbalancers', payload)
215 self.assertEqual(201, resp.status)
216
217 lb = body['loadbalancer']
218 lb_id = lb['id']
219
220 self.addCleanup(self.delete_loadbalancer, lb_id, ignore_error=True)
221 LOG.info('Waiting for loadbalancer %s to be active', lb_id)
222 self.await_loadbalancer_active(
223 lb_id,
224 name=payload['loadbalancer']['name']
225 )
226
227 self.lb_id = lb['id']
228 self.vip_port = lb['vip_port_id']
229 if CONF.validation.connect_method == 'floating':
230 self.vip_address = self._associate_floatingip()
231 else:
232 self.vip_address = lb['vip_address']
233
234 return lb
235
236 def update_loadbalancer(self, lb_id, **kwargs):
237 new_name = data_utils.rand_name('lb', prefix=self.name_prefix)
238 payload = {'loadbalancer': {'name': new_name}}
239 payload['loadbalancer'].update(kwargs)
240
241 resp, _ = self.lb_client.put_json('loadbalancers', lb_id, payload)
242 self.assertEqual(200, resp.status)
243
244 # Wait for loadbalancer to be active
245 LOG.info(
246 'Waiting for loadbalancer %s to be active after update', lb_id
247 )
248 self.await_loadbalancer_active(lb_id)
249
250 def delete_loadbalancer(self, id, ignore_error=False):
251 """Delete loadbalancer and wait for it to be deleted.
252
253 Only if loadbalancer is deleted completely can other network resources
254 be deleted.
255 """
256 resp = self.lb_client.delete_resource('loadbalancers', id,
257 ignore_error=ignore_error,
258 cascade=True)
259 if resp:
260 self.assertEqual(204, resp.status)
261
262 LOG.info('Waiting for loadbalancer %s to be deleted', id)
263 self.await_loadbalancer_deleted(id)
264
265 def create_listener(self, lb_id, **kwargs):
266 name = data_utils.rand_name('listener', prefix=self.name_prefix)
267 payload = {
268 'listener': {
269 'protocol': 'HTTP',
270 'protocol_port': '80',
271 'loadbalancer_id': lb_id,
272 'name': name
273 }
274 }
275 payload['listener'].update(kwargs)
276
277 resp, body = self.lb_client.post_json('listeners', payload)
278 self.assertEqual(201, resp.status)
279
280 listener_id = body['listener']['id']
281
282 LOG.info(
283 'Waiting for loadbalancer %s to be active after listener %s '
284 'creation', lb_id, listener_id
285 )
286 self.addCleanup(self.delete_listener, listener_id, lb_id,
287 ignore_error=True)
288 self.await_loadbalancer_active(lb_id)
289
290 return body['listener']
291
292 def update_listener(self, listener_id, lb_id, **kwargs):
293 new_name = data_utils.rand_name('listener', prefix=self.name_prefix)
294 payload = {'listener': {'name': new_name}}
295 payload['listener'].update(kwargs)
296
297 resp, _ = self.lb_client.put_json('listeners', listener_id, payload)
298 self.assertEqual(200, resp.status)
299
300 # Wait for loadbalancer to be active
301 LOG.info(
302 'Waiting for loadbalancer %s to be active after listener %s '
303 'update', lb_id, listener_id
304 )
305 self.await_loadbalancer_active(lb_id)
306
307 def delete_listener(self, id, lb_id, ignore_error=False):
308 resp = self.lb_client.delete_resource('listeners', id,
309 ignore_error=ignore_error)
310 if resp:
311 self.assertEqual(204, resp.status)
312
313 LOG.info(
314 'Waiting for loadbalancer %s to be active after deleting '
315 'listener %s', lb_id, id
316 )
317 self.await_loadbalancer_active(lb_id)
318
319 def create_pool(self, lb_id, **kwargs):
320 name = data_utils.rand_name('pool', prefix=self.name_prefix)
321 payload = {
322 'pool': {
323 'name': name,
324 'loadbalancer_id': lb_id,
325 'lb_algorithm': 'ROUND_ROBIN',
326 'protocol': 'HTTP'
327 }
328 }
329 payload['pool'].update(kwargs)
330
331 resp, body = self.lb_client.post_json('pools', payload)
332 self.assertEqual(201, resp.status)
333
334 pool_id = body['pool']['id']
335
336 LOG.info(
337 'Waiting for loadbalancer %s to be active after pool %s creation',
338 lb_id, pool_id
339 )
340 self.addCleanup(self.delete_pool, pool_id, lb_id, ignore_error=True)
341 self.await_loadbalancer_active(lb_id)
342
343 return body['pool']
344
345 def update_pool(self, pool_id, lb_id, **kwargs):
346 new_name = data_utils.rand_name('pool', prefix=self.name_prefix)
347 payload = {'pool': {'name': new_name}}
348 payload['pool'].update(kwargs)
349
350 resp, _ = self.lb_client.put_json('pools', pool_id, payload)
351 self.assertEqual(200, resp.status)
352
353 # Wait for loadbalancer to be active
354 LOG.info(
355 'Waiting for loadbalancer %s to be active after pool %s update',
356 lb_id, pool_id
357 )
358 self.await_loadbalancer_active(lb_id)
359
360 def delete_pool(self, id, lb_id, ignore_error=False):
361 resp = self.lb_client.delete_resource('pools', id,
362 ignore_error=ignore_error)
363 if resp:
364 self.assertEqual(204, resp.status)
365
366 LOG.info(
367 'Waiting for loadbalancer %s to be active after deleting '
368 'pool %s', lb_id, id
369 )
370 self.await_loadbalancer_active(lb_id)
371
372 def create_member(self, pool_id, lb_id, **kwargs):
373 name = data_utils.rand_name('member', prefix=self.name_prefix)
374 payload = {'member': {'name': name}}
375 payload['member'].update(kwargs)
376
377 resp, body = self.lb_client.post_json(
378 'pools/%s/members' % pool_id, payload
379 )
380 self.assertEqual(201, resp.status)
381
382 member_id = body['member']['id']
383
384 LOG.info(
385 'Waiting for loadbalancer %s to be active after adding '
386 'member %s', lb_id, member_id
387 )
388 self.addCleanup(self.delete_member, member_id, pool_id,
389 lb_id, ignore_error=True)
390 self.await_loadbalancer_active(lb_id)
391
392 return body['member']
393
394 def delete_member(self, id, pool_id, lb_id, ignore_error=False):
395 resp = self.lb_client.delete_resource(
396 'pools/%s/members' % pool_id,
397 id,
398 ignore_error=ignore_error
399 )
400 if resp:
401 self.assertEqual(204, resp.status)
402
403 LOG.info(
404 'Waiting for loadbalancer %s to be active after deleting '
405 'member %s', lb_id, id
406 )
407 self.await_loadbalancer_active(lb_id)
408
409 def _wait_for_lb_functional(self, vip_address):
410 session = requests.Session()
411 start = time.time()
412
413 while time.time() - start < CONF.loadbalancer.lb_build_timeout:
414 try:
415 session.get("http://{0}".format(vip_address), timeout=2)
416 time.sleep(1)
417 return
418 except Exception:
419 LOG.warning('Server is not passing initial traffic. Waiting.')
420 time.sleep(1)
421 LOG.error('Server did not begin passing traffic within the timeout '
422 'period. Failing test.')
423 raise lib_exc.ServerFault()
424
425 def check_members_balanced(self):
426 session = requests.Session()
427 response_counts = {}
428
429 self._wait_for_lb_functional(self.vip_address)
430
431 # Send a number requests to lb vip
432 for i in range(20):
433 try:
434 r = session.get('http://{0}'.format(self.vip_address),
435 timeout=2)
436 LOG.debug('Loadbalancer response: %s', r.content)
437
438 if r.content in response_counts:
439 response_counts[r.content] += 1
440 else:
441 response_counts[r.content] = 1
442
443 except Exception:
444 LOG.exception('Failed to send request to loadbalancer vip')
445 raise lib_exc.BadRequest(message='Failed to connect to lb')
446
447 # Ensure the correct number of members
448 self.assertEqual(2, len(response_counts))
449
450 # Ensure both members got the same number of responses
451 self.assertEqual(1, len(set(response_counts.values())))
452
453 def _delete_floatingip(self, floating_ip):
454 self.floatingip_adm_client.update_floatingip(
455 floating_ip,
456 port_id=None
457 )
458 test_utils.call_and_ignore_notfound_exc(
459 self.floatingip_adm_client.delete_floatingip, floating_ip
460 )
461
462 def _associate_floatingip(self):
463 # Associate floatingip with loadbalancer vip
464 floatingip = self.floatingip_adm_client.create_floatingip(
465 floating_network_id=CONF.network.public_network_id
466 )['floatingip']
467 floatip_vip = floatingip['floating_ip_address']
468 self.addCleanup(self._delete_floatingip, floatingip['id'])
469
470 LOG.debug('Floating ip %s created.', floatip_vip)
471
472 self.floatingip_adm_client.update_floatingip(
473 floatingip['id'],
474 port_id=self.vip_port
475 )
476
477 LOG.debug('Floating ip %s associated with vip.', floatip_vip)
478 return floatip_vip
479
480 def create_backend(self):
481 if CONF.loadbalancer.premade_server_ip:
482 self.vm_ip = CONF.loadbalancer.premade_server_ip
483 return
484
485 vr_resources = self.vr.resources
486 vm = server_util.create_server(
487 self.os_roles_lbmember,
488 validatable=True,
489 validation_resources=vr_resources,
490 wait_until='ACTIVE',
491 tenant_network=({'id': self.member_network_id}
492 if self.member_network_id else None),
493 )
494 self.addCleanup(
495 server_util.clear_server,
496 self.os_roles_lbmember.servers_client,
497 vm['id']
498 )
499
500 # Get vm private ip address.
501 ifaces = self.interfaces_client.list_interfaces(vm['id'])
502 for iface in ifaces['interfaceAttachments']:
503 if not self.member_network_id or (iface['net_id'] ==
504 self.vip_network_id):
505 for ip_info in iface['fixed_ips']:
506 if not self.vip_subnet_id or (ip_info['subnet_id'] ==
507 self.vip_subnet_id):
508 self.vm_ip = ip_info['ip_address']
509 break
510 if self.vm_ip:
511 break
512
513 self.assertIsNotNone(self.vm_ip)
514
515 if CONF.validation.connect_method == 'floating':
516 connect_ip = vr_resources['floating_ip']['floating_ip_address']
517 else:
518 connect_ip = self.vm_ip
519
520 server_util.run_webserver(
521 connect_ip,
522 vr_resources['keypair']['private_key']
523 )
524 LOG.debug('Web servers are running inside %s', vm['id'])