blob: f5e9bf2bd3c23f06e3f0dd6f105912125eba11a7 [file] [log] [blame]
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +02001# Copyright 2012 OpenStack Foundation
2# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Roman Bubyr7eac3a82022-09-14 18:33:23 +030017import os
18
19import netaddr
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020020from oslo_log import log
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020021
Roman Bubyr7eac3a82022-09-14 18:33:23 +030022from tempest.common import compute
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020023from tempest.common import utils
Roman Bubyr7eac3a82022-09-14 18:33:23 +030024from tempest.common import waiters
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020025from tempest import config
26from tempest.lib.common.utils import data_utils
27from tempest.lib.common.utils import test_utils
28from tempest.lib import exceptions as lib_exc
Roman Popelkafd509eb2022-04-07 15:10:08 +020029from tempest.scenario import manager
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020030
Roman Bubyr7eac3a82022-09-14 18:33:23 +030031
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020032CONF = config.CONF
33
34LOG = log.getLogger(__name__)
35
Roman Bubyr7eac3a82022-09-14 18:33:23 +030036NET_A = 'A'
37NET_A_BIS = 'A-Bis'
38NET_B = 'B'
39NET_C = 'C'
40
41if "SUBNETPOOL_PREFIX_V4" in os.environ:
42 subnet_base = netaddr.IPNetwork(os.environ['SUBNETPOOL_PREFIX_V4'])
43 if subnet_base.prefixlen > 21:
44 raise Exception("if SUBNETPOOL_PREFIX_V4 is set, it needs to offer "
45 "space for at least 8 /24 subnets")
46else:
47 subnet_base = netaddr.IPNetwork("10.100.0.0/16")
48
49
50def assign_24(idx):
51 # how many addresses in a /24:
52 range_size = 2 ** (32 - 24)
53 return netaddr.cidr_merge(
54 subnet_base[range_size * idx:range_size * (idx + 1)])[0]
55
56
57S1A = assign_24(1)
58S2A = assign_24(2)
59S1B = assign_24(4)
60S2B = assign_24(6)
61S1C = assign_24(6)
62NET_A_S1 = str(S1A)
63NET_A_S2 = str(S2A)
64NET_B_S1 = str(S1B)
65NET_B_S2 = str(S2B)
66NET_C_S1 = str(S1C)
67IP_A_S1_1 = str(S1A[10])
68IP_B_S1_1 = str(S1B[20])
69IP_C_S1_1 = str(S1C[30])
70IP_A_S1_2 = str(S1A[30])
71IP_B_S1_2 = str(S1B[40])
72IP_A_S1_3 = str(S1A[50])
73IP_B_S1_3 = str(S1B[60])
74IP_A_S2_1 = str(S2A[50])
75IP_B_S2_1 = str(S2B[60])
76IP_A_BIS_S1_1 = IP_A_S1_1
77IP_A_BIS_S1_2 = IP_A_S1_2
78IP_A_BIS_S1_3 = IP_A_S1_3
79IP_A_BIS_S2_1 = IP_A_S2_1
80
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020081
Roman Popelkafd509eb2022-04-07 15:10:08 +020082class ScenarioTest(manager.NetworkScenarioTest):
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020083 """Base class for scenario tests. Uses tempest own clients. """
84
85 credentials = ['primary']
86
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +020087
88class NetworkScenarioTest(ScenarioTest):
89 """Base class for network scenario tests.
90
91 This class provide helpers for network scenario tests, using the neutron
92 API. Helpers from ancestor which use the nova network API are overridden
93 with the neutron API.
94
95 This Class also enforces using Neutron instead of novanetwork.
96 Subclassed tests will be skipped if Neutron is not enabled
97
98 """
99
100 credentials = ['primary', 'admin']
101
102 @classmethod
103 def skip_checks(cls):
104 super(NetworkScenarioTest, cls).skip_checks()
105 if not CONF.service_available.neutron:
106 raise cls.skipException('Neutron not available')
107 if not utils.is_extension_enabled('bgpvpn', 'network'):
108 msg = "Bgpvpn extension not enabled."
109 raise cls.skipException(msg)
110
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +0200111 def _check_remote_connectivity(self, source, dest, should_succeed=True,
112 nic=None):
113 """check ping server via source ssh connection
114
115 :param source: RemoteClient: an ssh connection from which to ping
116 :param dest: and IP to ping against
117 :param should_succeed: boolean should ping succeed or not
118 :param nic: specific network interface to ping from
119 :returns: boolean -- should_succeed == ping
120 :returns: ping is false if ping failed
121 """
122 def ping_remote():
123 try:
124 source.ping_host(dest, nic=nic)
125 except lib_exc.SSHExecCommandFailed:
126 LOG.warning('Failed to ping IP: %s via a ssh connection '
127 'from: %s.', dest, source.ssh_client.host)
128 return not should_succeed
129 return should_succeed
130
131 return test_utils.call_until_true(ping_remote,
132 CONF.validation.ping_timeout,
133 1)
134
Roman Popelka8235e3b2022-04-12 11:29:17 +0200135 def create_loginable_secgroup_rule(self, security_group_rules_client=None,
136 secgroup=None,
137 security_groups_client=None):
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +0200138 """Create loginable security group rule
139
140 This function will create:
141 1. egress and ingress tcp port 22 allow rule in order to allow ssh
142 access for ipv4.
143 2. egress and ingress tcp port 80 allow rule in order to allow http
144 access for ipv4.
145 3. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
146 4. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
147 """
148
149 if security_group_rules_client is None:
150 security_group_rules_client = self.security_group_rules_client
151 if security_groups_client is None:
152 security_groups_client = self.security_groups_client
153 rules = []
154 rulesets = [
155 dict(
156 # ssh
157 protocol='tcp',
158 port_range_min=22,
159 port_range_max=22,
160 ),
161 dict(
162 # http
163 protocol='tcp',
164 port_range_min=80,
165 port_range_max=80,
166 ),
167 dict(
168 # ping
169 protocol='icmp',
170 ),
171 dict(
172 # ipv6-icmp for ping6
173 protocol='icmp',
174 ethertype='IPv6',
175 )
176 ]
177 sec_group_rules_client = security_group_rules_client
178 for ruleset in rulesets:
179 for r_direction in ['ingress', 'egress']:
180 ruleset['direction'] = r_direction
181 try:
Roman Popelka46d4dda2022-04-12 11:36:39 +0200182 sg_rule = self.create_security_group_rule(
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +0200183 sec_group_rules_client=sec_group_rules_client,
184 secgroup=secgroup,
185 security_groups_client=security_groups_client,
186 **ruleset)
187 except lib_exc.Conflict as ex:
188 # if rule already exist - skip rule and continue
189 msg = 'Security group rule already exists'
190 if msg not in ex._error_string:
191 raise ex
192 else:
193 self.assertEqual(r_direction, sg_rule['direction'])
194 rules.append(sg_rule)
195
196 return rules
197
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +0200198 def _create_router(self, client=None, tenant_id=None,
199 namestart='router-smoke'):
200 if not client:
201 client = self.routers_client
202 if not tenant_id:
203 tenant_id = client.tenant_id
204 name = data_utils.rand_name(namestart)
205 result = client.create_router(name=name,
206 admin_state_up=True,
207 tenant_id=tenant_id)
208 router = result['router']
209 self.assertEqual(router['name'], name)
210 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
211 client.delete_router,
212 router['id'])
213 return router
Roman Bubyr7eac3a82022-09-14 18:33:23 +0300214
215 def _create_security_group_for_test(self):
216 self.security_group = self.create_security_group(
217 project_id=self.bgpvpn_client.project_id)
218
219 def _create_networks_and_subnets(self, names=None, subnet_cidrs=None,
220 port_security=True):
221 if not names:
222 names = [NET_A, NET_B, NET_C]
223 if not subnet_cidrs:
224 subnet_cidrs = [[NET_A_S1], [NET_B_S1], [NET_C_S1]]
225 for (name, subnet_cidrs) in zip(names, subnet_cidrs):
Pavlo Shchelokovskyyc14c2ab2023-10-04 17:59:03 +0000226 network = super(NetworkScenarioTest, self).create_network(
227 namestart=name,
228 port_security_enabled=port_security)
Roman Bubyr7eac3a82022-09-14 18:33:23 +0300229 self.networks[name] = network
230 self.subnets[name] = []
231 for (j, cidr) in enumerate(subnet_cidrs):
232 sub_name = "subnet-%s-%d" % (name, j + 1)
233 subnet = self._create_subnet_with_cidr(network,
234 namestart=sub_name,
235 cidr=cidr,
236 ip_version=4)
237 self.subnets[name].append(subnet)
238
239 def _create_subnet_with_cidr(self, network, subnets_client=None,
240 namestart='subnet-smoke', **kwargs):
241 if not subnets_client:
242 subnets_client = self.subnets_client
243 tenant_cidr = kwargs.get('cidr')
244 subnet = dict(
245 name=data_utils.rand_name(namestart),
246 network_id=network['id'],
247 tenant_id=network['tenant_id'],
248 **kwargs)
249 result = subnets_client.create_subnet(**subnet)
250 self.assertIsNotNone(result, 'Unable to allocate tenant network')
251 subnet = result['subnet']
252 self.assertEqual(subnet['cidr'], tenant_cidr)
253 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
254 subnets_client.delete_subnet, subnet['id'])
255 return subnet
256
257 def _create_fip_router(self, client=None, public_network_id=None,
258 subnet_id=None):
259 router = self._create_router(client, namestart='router-')
260 router_id = router['id']
261 if public_network_id is None:
262 public_network_id = CONF.network.public_network_id
263 if client is None:
264 client = self.routers_client
265 kwargs = {'external_gateway_info': {'network_id': public_network_id}}
266 router = client.update_router(router_id, **kwargs)['router']
267 if subnet_id is not None:
268 client.add_router_interface(router_id, subnet_id=subnet_id)
269 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
270 client.remove_router_interface, router_id,
271 subnet_id=subnet_id)
272 return router
273
274 def _associate_fip(self, server_index):
275 server = self.servers[server_index]
276 fip = self.create_floating_ip(
277 server, external_network_id=CONF.network.public_network_id,
278 port_id=self.ports[server['id']]['id'])
279 self.server_fips[server['id']] = fip
280 return fip
281
282 def _create_router_and_associate_fip(self, server_index, subnet):
283 router = self._create_fip_router(subnet_id=subnet['id'])
284 self._associate_fip(server_index)
285 return router
286
287 def _create_server(self, name, keypair, network, ip_address,
288 security_group_ids, clients, port_security):
289 security_groups = []
290 if port_security:
291 security_groups = security_group_ids
292 create_port_body = {'fixed_ips': [{'ip_address': ip_address}],
293 'namestart': 'port-smoke',
294 'security_groups': security_groups}
295
Pavlo Shchelokovskyyc14c2ab2023-10-04 17:59:03 +0000296 port = super(NetworkScenarioTest, self).create_port(
297 network_id=network['id'],
298 client=clients.ports_client,
299 **create_port_body)
Roman Bubyr7eac3a82022-09-14 18:33:23 +0300300
301 create_server_kwargs = {
302 'key_name': keypair['name'],
303 'networks': [{'uuid': network['id'], 'port': port['id']}]
304 }
305 body, servers = compute.create_test_server(
306 clients, wait_until='ACTIVE', name=name, **create_server_kwargs)
307 self.addCleanup(waiters.wait_for_server_termination,
308 clients.servers_client, body['id'])
309 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
310 clients.servers_client.delete_server, body['id'])
311 server = clients.servers_client.show_server(body['id'])['server']
312 LOG.debug('Created server: %s with status: %s', server['id'],
313 server['status'])
314 self.ports[server['id']] = port
315 return server
316
317 def _create_servers(self, ports_config=None, port_security=True):
318 keypair = self.create_keypair()
319 security_group_ids = [self.security_group['id']]
320 if not ports_config:
321 ports_config = [[self.networks[NET_A], IP_A_S1_1],
322 [self.networks[NET_B], IP_B_S1_1]]
323
324 for (i, port_config) in enumerate(ports_config):
325 network = port_config[0]
326 server = self._create_server(
327 'server-' + str(i + 1), keypair, network, port_config[1],
328 security_group_ids, self.os_primary, port_security)
329 self.servers.append(server)
330 self.servers_keypairs[server['id']] = keypair
331 self.server_fixed_ips[server['id']] = (
332 server['addresses'][network['name']][0]['addr'])
333 self.assertTrue(self.servers_keypairs)
334
335 def _create_l3_bgpvpn(self, name='test-l3-bgpvpn', rts=None,
336 import_rts=None, export_rts=None):
337 if rts is None and import_rts is None and export_rts is None:
338 rts = [self.RT1]
339 import_rts = import_rts or []
340 export_rts = export_rts or []
341 self.bgpvpn = self.create_bgpvpn(
342 self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id,
343 name=name, route_targets=rts, export_targets=export_rts,
344 import_targets=import_rts)
345 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
346 self.bgpvpn_admin_client.delete_bgpvpn,
347 self.bgpvpn['id'])
348 return self.bgpvpn
349
350 def _update_l3_bgpvpn(self, rts=None, import_rts=None, export_rts=None,
351 bgpvpn=None):
352 bgpvpn = bgpvpn or self.bgpvpn
353 if rts is None:
354 rts = [self.RT1]
355 import_rts = import_rts or []
356 export_rts = export_rts or []
357 LOG.debug('Updating targets in BGPVPN %s', bgpvpn['id'])
358 self.bgpvpn_admin_client.update_bgpvpn(bgpvpn['id'],
359 route_targets=rts,
360 export_targets=export_rts,
361 import_targets=import_rts)
362
363 def _associate_all_nets_to_bgpvpn(self, bgpvpn=None):
364 bgpvpn = bgpvpn or self.bgpvpn
365 for network in self.networks.values():
366 self.bgpvpn_client.create_network_association(
367 bgpvpn['id'], network['id'])
368 LOG.debug('BGPVPN network associations completed')
369
370 def _setup_ssh_client(self, server):
371 server_fip = self.server_fips[server['id']][
372 'floating_ip_address']
373 private_key = self.servers_keypairs[server['id']][
374 'private_key']
375 ssh_client = self.get_remote_client(server_fip,
376 private_key=private_key,
377 server=server)
378 return ssh_client
379
380 def _setup_http_server(self, server_index):
381 server = self.servers[server_index]
382 ssh_client = self._setup_ssh_client(server)
383 ssh_client.exec_command("sudo nc -kl -p 80 -e echo '%s:%s' &"
384 % (server['name'], server['id']))
385
386 def _setup_ip_forwarding(self, server_index):
387 server = self.servers[server_index]
388 ssh_client = self._setup_ssh_client(server)
389 ssh_client.exec_command("sudo sysctl -w net.ipv4.ip_forward=1")
390
391 def _setup_ip_address(self, server_index, cidr, device=None):
392 self._setup_range_ip_address(server_index, [cidr], device=None)
393
394 def _setup_range_ip_address(self, server_index, cidrs, device=None):
395 MAX_CIDRS = 50
396 if device is None:
397 device = 'lo'
398 server = self.servers[server_index]
399 ssh_client = self._setup_ssh_client(server)
400 for i in range(0, len(cidrs), MAX_CIDRS):
401 ips = ' '.join(cidrs[i:i + MAX_CIDRS])
402 ssh_client.exec_command(
403 ("for ip in {ips}; do sudo ip addr add $ip "
404 "dev {dev}; done").format(ips=ips, dev=device))
405
406 def _check_l3_bgpvpn(self, from_server=None, to_server=None,
407 should_succeed=True, validate_server=False):
408 to_server = to_server or self.servers[1]
409 destination_srv = None
410 if validate_server:
411 destination_srv = '%s:%s' % (to_server['name'], to_server['id'])
412 destination_ip = self.server_fixed_ips[to_server['id']]
413 self._check_l3_bgpvpn_by_specific_ip(from_server=from_server,
414 to_server_ip=destination_ip,
415 should_succeed=should_succeed,
416 validate_server=destination_srv)
417
418 def _check_l3_bgpvpn_by_specific_ip(self, from_server=None,
419 to_server_ip=None,
420 should_succeed=True,
421 validate_server=None,
422 repeat_validate_server=10):
423 from_server = from_server or self.servers[0]
424 from_server_ip = self.server_fips[from_server['id']][
425 'floating_ip_address']
426 if to_server_ip is None:
427 to_server_ip = self.server_fixed_ips[self.servers[1]['id']]
428 ssh_client = self._setup_ssh_client(from_server)
429 check_reachable = should_succeed or validate_server
430 msg = ""
431 if check_reachable:
432 msg = "Timed out waiting for {ip} to become reachable".format(
433 ip=to_server_ip)
434 else:
435 msg = ("Unexpected ping response from VM with IP address "
436 "{dest} originated from VM with IP address "
437 "{src}").format(dest=to_server_ip, src=from_server_ip)
438 try:
439 result = self._check_remote_connectivity(ssh_client,
440 to_server_ip,
441 check_reachable)
442 # if a negative connectivity check was unsuccessful (unexpected
443 # ping reply) then try to know more:
444 if not check_reachable and not result:
445 try:
446 content = ssh_client.exec_command(
447 "nc %s 80" % to_server_ip).strip()
448 LOG.warning("Can connect to %s: %s", to_server_ip, content)
449 except Exception:
450 LOG.warning("Could ping %s, but no http", to_server_ip)
451
452 self.assertTrue(result, msg)
453
454 if validate_server and result:
455 # repeating multiple times gives increased odds of avoiding
456 # false positives in the case where the dataplane does
457 # equal-cost multipath
458 for i in range(0, repeat_validate_server):
459 real_dest = ssh_client.exec_command(
460 "nc %s 80" % to_server_ip).strip()
461 result = real_dest == validate_server
462 self.assertTrue(
463 should_succeed == result,
464 ("Destination server name is '%s', expected is '%s'" %
465 (real_dest, validate_server)))
466 LOG.info("nc server name check %d successful", i)
467 except Exception:
468 LOG.exception("Error validating connectivity to %s "
469 "from VM with IP address %s: %s",
470 to_server_ip, from_server_ip, msg)
471 raise
472
473 def _associate_fip_and_check_l3_bgpvpn(self, subnet=None,
474 should_succeed=True):
475 if not subnet:
476 subnet = self.subnets[NET_A][0]
477 else:
478 subnet = self.subnets[subnet][0]
479
480 self.router = self._create_router_and_associate_fip(0, subnet)
481 self._check_l3_bgpvpn(should_succeed=should_succeed)
482
483 def _live_migrate(self, server_id, target_host, state,
484 volume_backed=False):
485 # If target_host is None,
486 # check whether source host is different with
487 # the new host after migration.
488 if target_host is None:
489 source_host = self.get_host_for_server(server_id)
490 self._migrate_server_to(server_id, target_host, volume_backed)
491 waiters.wait_for_server_status(self.servers_client, server_id, state)
492 migration_list = (self.admin_migration_client.list_migrations()
493 ['migrations'])
494 msg = ("Live Migration failed. Migrations list for Instance "
495 "%s: [" % server_id)
496 for live_migration in migration_list:
497 if (live_migration['instance_uuid'] == server_id):
498 msg += "\n%s" % live_migration
499 msg += "]"
500 if target_host is None:
501 self.assertNotEqual(source_host,
502 self.get_host_for_server(server_id), msg)
503 else:
504 self.assertEqual(target_host, self.get_host_for_server(server_id),
505 msg)
506
507 def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
508 kwargs = dict()
509 block_migration = getattr(self, 'block_migration', None)
510 if self.block_migration is None:
Roman Bubyr7eac3a82022-09-14 18:33:23 +0300511 block_migration = (CONF.compute_feature_enabled.
512 block_migration_for_live_migration and
513 not volume_backed)
514 self.admin_servers_client.live_migrate_server(
515 server_id, host=dest_host, block_migration=block_migration,
516 **kwargs)