blob: 4ff1c0d4a1ec3f9f5573c8e2a0466fb7f4d40eca [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
17import subprocess
18
19import netaddr
20from oslo_log import log
21from oslo_utils import netutils
22
23from tempest.common import compute
24from tempest.common import utils
25from tempest.common.utils.linux import remote_client
26from tempest.common.utils import net_utils
27from tempest.common import waiters
28from tempest import config
29from tempest.lib.common.utils import data_utils
30from tempest.lib.common.utils import test_utils
31from tempest.lib import exceptions as lib_exc
32import tempest.test
33
34CONF = config.CONF
35
36LOG = log.getLogger(__name__)
37
38
39class ScenarioTest(tempest.test.BaseTestCase):
40 """Base class for scenario tests. Uses tempest own clients. """
41
42 credentials = ['primary']
43
44 @classmethod
45 def setup_clients(cls):
46 super(ScenarioTest, cls).setup_clients()
47 # Clients (in alphabetical order)
48 cls.keypairs_client = cls.os_primary.keypairs_client
49 cls.servers_client = cls.os_primary.servers_client
50 # Neutron network client
51 cls.networks_client = cls.os_primary.networks_client
52 cls.ports_client = cls.os_primary.ports_client
53 cls.routers_client = cls.os_primary.routers_client
54 cls.subnets_client = cls.os_primary.subnets_client
55 cls.floating_ips_client = cls.os_primary.floating_ips_client
56 cls.security_groups_client = cls.os_primary.security_groups_client
57 cls.security_group_rules_client = (
58 cls.os_primary.security_group_rules_client)
59
60 # ## Test functions library
61 #
62 # The create_[resource] functions only return body and discard the
63 # resp part which is not used in scenario tests
64
65 def _create_port(self, network_id, client=None, namestart='port-quotatest',
66 **kwargs):
67 if not client:
68 client = self.ports_client
69 name = data_utils.rand_name(namestart)
70 result = client.create_port(
71 name=name,
72 network_id=network_id,
73 **kwargs)
74 self.assertIsNotNone(result, 'Unable to allocate port')
75 port = result['port']
76 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
77 client.delete_port, port['id'])
78 return port
79
80 def create_keypair(self, client=None):
81 if not client:
82 client = self.keypairs_client
83 name = data_utils.rand_name(self.__class__.__name__)
84 # We don't need to create a keypair by pubkey in scenario
85 body = client.create_keypair(name=name)
86 self.addCleanup(client.delete_keypair, name)
87 return body['keypair']
88
89 def create_server(self, name=None, image_id=None, flavor=None,
90 validatable=False, wait_until='ACTIVE',
91 clients=None, **kwargs):
92 """Wrapper utility that returns a test server.
93
94 This wrapper utility calls the common create test server and
95 returns a test server. The purpose of this wrapper is to minimize
96 the impact on the code of the tests already using this
97 function.
98 """
99
100 # NOTE(jlanoux): As a first step, ssh checks in the scenario
101 # tests need to be run regardless of the run_validation and
102 # validatable parameters and thus until the ssh validation job
103 # becomes voting in CI. The test resources management and IP
104 # association are taken care of in the scenario tests.
105 # Therefore, the validatable parameter is set to false in all
106 # those tests. In this way create_server just return a standard
107 # server and the scenario tests always perform ssh checks.
108
109 # Needed for the cross_tenant_traffic test:
110 if clients is None:
111 clients = self.os_primary
112
113 if name is None:
114 name = data_utils.rand_name(self.__class__.__name__ + "-server")
115
116 vnic_type = CONF.network.port_vnic_type
117
118 # If vnic_type is configured create port for
119 # every network
120 if vnic_type:
121 ports = []
122
123 create_port_body = {'binding:vnic_type': vnic_type,
124 'namestart': 'port-smoke'}
125 if kwargs:
126 # Convert security group names to security group ids
127 # to pass to create_port
128 if 'security_groups' in kwargs:
129 security_groups = \
130 clients.security_groups_client.list_security_groups(
131 ).get('security_groups')
132 sec_dict = dict([(s['name'], s['id'])
133 for s in security_groups])
134
135 sec_groups_names = [s['name'] for s in kwargs.pop(
136 'security_groups')]
137 security_groups_ids = [sec_dict[s]
138 for s in sec_groups_names]
139
140 if security_groups_ids:
141 create_port_body[
142 'security_groups'] = security_groups_ids
143 networks = kwargs.pop('networks', [])
144 else:
145 networks = []
146
147 # If there are no networks passed to us we look up
148 # for the project's private networks and create a port.
149 # The same behaviour as we would expect when passing
150 # the call to the clients with no networks
151 if not networks:
152 networks = clients.networks_client.list_networks(
153 **{'router:external': False, 'fields': 'id'})['networks']
154
155 # It's net['uuid'] if networks come from kwargs
156 # and net['id'] if they come from
157 # clients.networks_client.list_networks
158 for net in networks:
159 net_id = net.get('uuid', net.get('id'))
160 if 'port' not in net:
161 port = self._create_port(network_id=net_id,
162 client=clients.ports_client,
163 **create_port_body)
164 ports.append({'port': port['id']})
165 else:
166 ports.append({'port': net['port']})
167 if ports:
168 kwargs['networks'] = ports
169 self.ports = ports
170
171 tenant_network = self.get_tenant_network()
172
173 body, servers = compute.create_test_server(
174 clients,
175 tenant_network=tenant_network,
176 wait_until=wait_until,
177 name=name, flavor=flavor,
178 image_id=image_id, **kwargs)
179
180 self.addCleanup(waiters.wait_for_server_termination,
181 clients.servers_client, body['id'])
182 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
183 clients.servers_client.delete_server, body['id'])
184 server = clients.servers_client.show_server(body['id'])['server']
185 return server
186
187 def get_remote_client(self, ip_address, username=None, private_key=None):
188 """Get a SSH client to a remote server
189
190 @param ip_address the server floating or fixed IP address to use
191 for ssh validation
192 @param username name of the Linux account on the remote server
193 @param private_key the SSH private key to use
194 @return a RemoteClient object
195 """
196
197 if username is None:
198 username = CONF.validation.image_ssh_user
199 # Set this with 'keypair' or others to log in with keypair or
200 # username/password.
201 if CONF.validation.auth_method == 'keypair':
202 password = None
203 if private_key is None:
204 private_key = self.keypair['private_key']
205 else:
206 password = CONF.validation.image_ssh_password
207 private_key = None
208 linux_client = remote_client.RemoteClient(ip_address, username,
209 pkey=private_key,
210 password=password)
211 try:
212 linux_client.validate_authentication()
213 except Exception as e:
214 message = ('Initializing SSH connection to %(ip)s failed. '
215 'Error: %(error)s' % {'ip': ip_address,
216 'error': e})
217 caller = test_utils.find_test_caller()
218 if caller:
219 message = '(%s) %s' % (caller, message)
220 LOG.exception(message)
221 self._log_console_output()
222 raise
223
224 return linux_client
225
226 def _log_console_output(self, servers=None):
227 if not CONF.compute_feature_enabled.console_output:
228 LOG.debug('Console output not supported, cannot log')
229 return
230 if not servers:
231 servers = self.servers_client.list_servers()
232 servers = servers['servers']
233 for server in servers:
234 try:
235 console_output = self.servers_client.get_console_output(
236 server['id'])['output']
237 LOG.debug('Console output for %s\nbody=\n%s',
238 server['id'], console_output)
239 except lib_exc.NotFound:
240 LOG.debug("Server %s disappeared(deleted) while looking "
241 "for the console log", server['id'])
242
243 def _log_net_info(self, exc):
244 # network debug is called as part of ssh init
245 if not isinstance(exc, lib_exc.SSHTimeout):
246 LOG.debug('Network information on a devstack host')
247
248 def ping_ip_address(self, ip_address, should_succeed=True,
249 ping_timeout=None, mtu=None):
250 timeout = ping_timeout or CONF.validation.ping_timeout
251 cmd = ['ping', '-c1', '-w1']
252
253 if mtu:
254 cmd += [
255 # don't fragment
256 '-M', 'do',
257 # ping receives just the size of ICMP payload
258 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
259 ]
260 cmd.append(ip_address)
261
262 def ping():
263 proc = subprocess.Popen(cmd,
264 stdout=subprocess.PIPE,
265 stderr=subprocess.PIPE)
266 proc.communicate()
267
268 return (proc.returncode == 0) == should_succeed
269
270 caller = test_utils.find_test_caller()
271 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
272 ' expected result is %(should_succeed)s', {
273 'caller': caller, 'ip': ip_address, 'timeout': timeout,
274 'should_succeed':
275 'reachable' if should_succeed else 'unreachable'
276 })
277 result = test_utils.call_until_true(ping, timeout, 1)
278 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
279 'ping result is %(result)s', {
280 'caller': caller, 'ip': ip_address, 'timeout': timeout,
281 'result': 'expected' if result else 'unexpected'
282 })
283 return result
284
285 def check_vm_connectivity(self, ip_address,
286 username=None,
287 private_key=None,
288 should_connect=True,
289 mtu=None):
290 """Check server connectivity
291
292 :param ip_address: server to test against
293 :param username: server's ssh username
294 :param private_key: server's ssh private key to be used
295 :param should_connect: True/False indicates positive/negative test
296 positive - attempt ping and ssh
297 negative - attempt ping and fail if succeed
298 :param mtu: network MTU to use for connectivity validation
299
300 :raises: AssertError if the result of the connectivity check does
301 not match the value of the should_connect param
302 """
303 if should_connect:
304 msg = "Timed out waiting for %s to become reachable" % ip_address
305 else:
306 msg = "ip address %s is reachable" % ip_address
307 self.assertTrue(self.ping_ip_address(ip_address,
308 should_succeed=should_connect,
309 mtu=mtu),
310 msg=msg)
311 if should_connect:
312 # no need to check ssh for negative connectivity
313 self.get_remote_client(ip_address, username, private_key)
314
315 def check_public_network_connectivity(self, ip_address, username,
316 private_key, should_connect=True,
317 msg=None, servers=None, mtu=None):
318 # The target login is assumed to have been configured for
319 # key-based authentication by cloud-init.
320 LOG.debug('checking network connections to IP %s with user: %s',
321 ip_address, username)
322 try:
323 self.check_vm_connectivity(ip_address,
324 username,
325 private_key,
326 should_connect=should_connect,
327 mtu=mtu)
328 except Exception:
329 ex_msg = 'Public network connectivity check failed'
330 if msg:
331 ex_msg += ": " + msg
332 LOG.exception(ex_msg)
333 self._log_console_output(servers)
334 raise
335
336
337class NetworkScenarioTest(ScenarioTest):
338 """Base class for network scenario tests.
339
340 This class provide helpers for network scenario tests, using the neutron
341 API. Helpers from ancestor which use the nova network API are overridden
342 with the neutron API.
343
344 This Class also enforces using Neutron instead of novanetwork.
345 Subclassed tests will be skipped if Neutron is not enabled
346
347 """
348
349 credentials = ['primary', 'admin']
350
351 @classmethod
352 def skip_checks(cls):
353 super(NetworkScenarioTest, cls).skip_checks()
354 if not CONF.service_available.neutron:
355 raise cls.skipException('Neutron not available')
356 if not utils.is_extension_enabled('bgpvpn', 'network'):
357 msg = "Bgpvpn extension not enabled."
358 raise cls.skipException(msg)
359
360 def _create_network(self, networks_client=None,
361 tenant_id=None,
362 namestart='network-smoke-',
363 port_security_enabled=True):
364 if not networks_client:
365 networks_client = self.networks_client
366 if not tenant_id:
367 tenant_id = networks_client.tenant_id
368 name = data_utils.rand_name(namestart)
369 network_kwargs = dict(name=name, tenant_id=tenant_id)
370 # Neutron disables port security by default so we have to check the
371 # config before trying to create the network with port_security_enabled
372 if CONF.network_feature_enabled.port_security:
373 network_kwargs['port_security_enabled'] = port_security_enabled
374 result = networks_client.create_network(**network_kwargs)
375 network = result['network']
376
377 self.assertEqual(network['name'], name)
378 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
379 networks_client.delete_network,
380 network['id'])
381 return network
382
383 def _create_subnet(self, network, subnets_client=None,
384 routers_client=None, namestart='subnet-smoke',
385 **kwargs):
386 """Create a subnet for the given network
387
388 within the cidr block configured for tenant networks.
389 """
390 if not subnets_client:
391 subnets_client = self.subnets_client
392 if not routers_client:
393 routers_client = self.routers_client
394
395 def cidr_in_use(cidr, tenant_id):
396 """Check cidr existence
397
398 :returns: True if subnet with cidr already exist in tenant
399 False else
400 """
401 cidr_in_use = self.os_admin.subnets_client.list_subnets(
402 tenant_id=tenant_id, cidr=cidr)['subnets']
403 return len(cidr_in_use) != 0
404
405 ip_version = kwargs.pop('ip_version', 4)
406
407 if ip_version == 6:
408 tenant_cidr = netaddr.IPNetwork(
409 CONF.network.project_network_v6_cidr)
410 num_bits = CONF.network.project_network_v6_mask_bits
411 else:
412 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
413 num_bits = CONF.network.project_network_mask_bits
414
415 result = None
416 str_cidr = None
417 # Repeatedly attempt subnet creation with sequential cidr
418 # blocks until an unallocated block is found.
419 for subnet_cidr in tenant_cidr.subnet(num_bits):
420 str_cidr = str(subnet_cidr)
421 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
422 continue
423
424 subnet = dict(
425 name=data_utils.rand_name(namestart),
426 network_id=network['id'],
427 tenant_id=network['tenant_id'],
428 cidr=str_cidr,
429 ip_version=ip_version,
430 **kwargs
431 )
432 try:
433 result = subnets_client.create_subnet(**subnet)
434 break
435 except lib_exc.Conflict as e:
436 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
437 if not is_overlapping_cidr:
438 raise
439 self.assertIsNotNone(result, 'Unable to allocate tenant network')
440
441 subnet = result['subnet']
442 self.assertEqual(subnet['cidr'], str_cidr)
443
444 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
445 subnets_client.delete_subnet, subnet['id'])
446
447 return subnet
448
449 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
450 ports = self.os_admin.ports_client.list_ports(
451 device_id=server['id'], fixed_ip=ip_addr)['ports']
452 # A port can have more than one IP address in some cases.
453 # If the network is dual-stack (IPv4 + IPv6), this port is associated
454 # with 2 subnets
455 p_status = ['ACTIVE']
456 # NOTE(vsaienko) With Ironic, instances live on separate hardware
457 # servers. Neutron does not bind ports for Ironic instances, as a
458 # result the port remains in the DOWN state.
459 # TODO(vsaienko) remove once bug: #1599836 is resolved.
460 if getattr(CONF.service_available, 'ironic', False):
461 p_status.append('DOWN')
462 port_map = [(p["id"], fxip["ip_address"])
463 for p in ports
464 for fxip in p["fixed_ips"]
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200465 if netutils.is_valid_ipv4(fxip["ip_address"]) and
466 p['status'] in p_status]
Slawek Kaplonski8dd49aa2019-04-16 14:47:07 +0200467 inactive = [p for p in ports if p['status'] != 'ACTIVE']
468 if inactive:
469 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
470
471 self.assertNotEqual(0, len(port_map),
472 "No IPv4 addresses found in: %s" % ports)
473 self.assertEqual(len(port_map), 1,
474 "Found multiple IPv4 addresses: %s. "
475 "Unable to determine which port to target."
476 % port_map)
477 return port_map[0]
478
479 def _get_network_by_name(self, network_name):
480 net = self.os_admin.networks_client.list_networks(
481 name=network_name)['networks']
482 self.assertNotEqual(len(net), 0,
483 "Unable to get network by name: %s" % network_name)
484 return net[0]
485
486 def create_floating_ip(self, thing, external_network_id=None,
487 port_id=None, client=None):
488 """Create a floating IP and associates to a resource/port on Neutron"""
489 if not external_network_id:
490 external_network_id = CONF.network.public_network_id
491 if not client:
492 client = self.floating_ips_client
493 if not port_id:
494 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
495 else:
496 ip4 = None
497 result = client.create_floatingip(
498 floating_network_id=external_network_id,
499 port_id=port_id,
500 tenant_id=thing['tenant_id'],
501 fixed_ip_address=ip4
502 )
503 floating_ip = result['floatingip']
504 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
505 client.delete_floatingip,
506 floating_ip['id'])
507 return floating_ip
508
509 def _associate_floating_ip(self, floating_ip, server):
510 port_id, _ = self._get_server_port_id_and_ip4(server)
511 kwargs = dict(port_id=port_id)
512 floating_ip = self.floating_ips_client.update_floatingip(
513 floating_ip['id'], **kwargs)['floatingip']
514 self.assertEqual(port_id, floating_ip['port_id'])
515 return floating_ip
516
517 def _disassociate_floating_ip(self, floating_ip):
518 """:param floating_ip: floating_ips_client.create_floatingip"""
519 kwargs = dict(port_id=None)
520 floating_ip = self.floating_ips_client.update_floatingip(
521 floating_ip['id'], **kwargs)['floatingip']
522 self.assertIsNone(floating_ip['port_id'])
523 return floating_ip
524
525 def check_floating_ip_status(self, floating_ip, status):
526 """Verifies floatingip reaches the given status
527
528 :param dict floating_ip: floating IP dict to check status
529 :param status: target status
530 :raises: AssertionError if status doesn't match
531 """
532 floatingip_id = floating_ip['id']
533
534 def refresh():
535 result = (self.floating_ips_client.
536 show_floatingip(floatingip_id)['floatingip'])
537 return status == result['status']
538
539 test_utils.call_until_true(refresh,
540 CONF.network.build_timeout,
541 CONF.network.build_interval)
542 floating_ip = self.floating_ips_client.show_floatingip(
543 floatingip_id)['floatingip']
544 self.assertEqual(status, floating_ip['status'],
545 message="FloatingIP: {fp} is at status: {cst}. "
546 "failed to reach status: {st}"
547 .format(fp=floating_ip, cst=floating_ip['status'],
548 st=status))
549 LOG.info("FloatingIP: {fp} is at status: {st}"
550 .format(fp=floating_ip, st=status))
551
552 def _check_tenant_network_connectivity(self, server,
553 username,
554 private_key,
555 should_connect=True,
556 servers_for_debug=None):
557 if not CONF.network.project_networks_reachable:
558 msg = 'Tenant networks not configured to be reachable.'
559 LOG.info(msg)
560 return
561 # The target login is assumed to have been configured for
562 # key-based authentication by cloud-init.
563 try:
564 for net_name, ip_addresses in server['addresses'].items():
565 for ip_address in ip_addresses:
566 self.check_vm_connectivity(ip_address['addr'],
567 username,
568 private_key,
569 should_connect=should_connect)
570 except Exception as e:
571 LOG.exception('Tenant network connectivity check failed')
572 self._log_console_output(servers_for_debug)
573 self._log_net_info(e)
574 raise
575
576 def _check_remote_connectivity(self, source, dest, should_succeed=True,
577 nic=None):
578 """check ping server via source ssh connection
579
580 :param source: RemoteClient: an ssh connection from which to ping
581 :param dest: and IP to ping against
582 :param should_succeed: boolean should ping succeed or not
583 :param nic: specific network interface to ping from
584 :returns: boolean -- should_succeed == ping
585 :returns: ping is false if ping failed
586 """
587 def ping_remote():
588 try:
589 source.ping_host(dest, nic=nic)
590 except lib_exc.SSHExecCommandFailed:
591 LOG.warning('Failed to ping IP: %s via a ssh connection '
592 'from: %s.', dest, source.ssh_client.host)
593 return not should_succeed
594 return should_succeed
595
596 return test_utils.call_until_true(ping_remote,
597 CONF.validation.ping_timeout,
598 1)
599
600 def _create_security_group(self, security_group_rules_client=None,
601 tenant_id=None,
602 namestart='secgroup-smoke',
603 security_groups_client=None):
604 if security_group_rules_client is None:
605 security_group_rules_client = self.security_group_rules_client
606 if security_groups_client is None:
607 security_groups_client = self.security_groups_client
608 if tenant_id is None:
609 tenant_id = security_groups_client.tenant_id
610 secgroup = self._create_empty_security_group(
611 namestart=namestart, client=security_groups_client,
612 tenant_id=tenant_id)
613
614 # Add rules to the security group
615 rules = self._create_loginable_secgroup_rule(
616 security_group_rules_client=security_group_rules_client,
617 secgroup=secgroup,
618 security_groups_client=security_groups_client)
619 for rule in rules:
620 self.assertEqual(tenant_id, rule['tenant_id'])
621 self.assertEqual(secgroup['id'], rule['security_group_id'])
622 return secgroup
623
624 def _create_empty_security_group(self, client=None, tenant_id=None,
625 namestart='secgroup-smoke'):
626 """Create a security group without rules.
627
628 Default rules will be created:
629 - IPv4 egress to any
630 - IPv6 egress to any
631
632 :param tenant_id: secgroup will be created in this tenant
633 :returns: the created security group
634 """
635 if client is None:
636 client = self.security_groups_client
637 if not tenant_id:
638 tenant_id = client.tenant_id
639 sg_name = data_utils.rand_name(namestart)
640 sg_desc = sg_name + " description"
641 sg_dict = dict(name=sg_name,
642 description=sg_desc)
643 sg_dict['tenant_id'] = tenant_id
644 result = client.create_security_group(**sg_dict)
645
646 secgroup = result['security_group']
647 self.assertEqual(secgroup['name'], sg_name)
648 self.assertEqual(tenant_id, secgroup['tenant_id'])
649 self.assertEqual(secgroup['description'], sg_desc)
650
651 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
652 client.delete_security_group, secgroup['id'])
653 return secgroup
654
655 def _default_security_group(self, client=None, tenant_id=None):
656 """Get default secgroup for given tenant_id.
657
658 :returns: default secgroup for given tenant
659 """
660 if client is None:
661 client = self.security_groups_client
662 if not tenant_id:
663 tenant_id = client.tenant_id
664 sgs = [
665 sg for sg in list(client.list_security_groups().values())[0]
666 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
667 ]
668 msg = "No default security group for tenant %s." % (tenant_id)
669 self.assertGreater(len(sgs), 0, msg)
670 return sgs[0]
671
672 def _create_security_group_rule(self, secgroup=None,
673 sec_group_rules_client=None,
674 tenant_id=None,
675 security_groups_client=None, **kwargs):
676 """Create a rule from a dictionary of rule parameters.
677
678 Create a rule in a secgroup. if secgroup not defined will search for
679 default secgroup in tenant_id.
680
681 :param secgroup: the security group.
682 :param tenant_id: if secgroup not passed -- the tenant in which to
683 search for default secgroup
684 :param kwargs: a dictionary containing rule parameters:
685 for example, to allow incoming ssh:
686 rule = {
687 direction: 'ingress'
688 protocol:'tcp',
689 port_range_min: 22,
690 port_range_max: 22
691 }
692 """
693 if sec_group_rules_client is None:
694 sec_group_rules_client = self.security_group_rules_client
695 if security_groups_client is None:
696 security_groups_client = self.security_groups_client
697 if not tenant_id:
698 tenant_id = security_groups_client.tenant_id
699 if secgroup is None:
700 secgroup = self._default_security_group(
701 client=security_groups_client, tenant_id=tenant_id)
702
703 ruleset = dict(security_group_id=secgroup['id'],
704 tenant_id=secgroup['tenant_id'])
705 ruleset.update(kwargs)
706
707 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
708 sg_rule = sg_rule['security_group_rule']
709
710 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
711 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
712
713 return sg_rule
714
715 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
716 secgroup=None,
717 security_groups_client=None):
718 """Create loginable security group rule
719
720 This function will create:
721 1. egress and ingress tcp port 22 allow rule in order to allow ssh
722 access for ipv4.
723 2. egress and ingress tcp port 80 allow rule in order to allow http
724 access for ipv4.
725 3. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
726 4. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
727 """
728
729 if security_group_rules_client is None:
730 security_group_rules_client = self.security_group_rules_client
731 if security_groups_client is None:
732 security_groups_client = self.security_groups_client
733 rules = []
734 rulesets = [
735 dict(
736 # ssh
737 protocol='tcp',
738 port_range_min=22,
739 port_range_max=22,
740 ),
741 dict(
742 # http
743 protocol='tcp',
744 port_range_min=80,
745 port_range_max=80,
746 ),
747 dict(
748 # ping
749 protocol='icmp',
750 ),
751 dict(
752 # ipv6-icmp for ping6
753 protocol='icmp',
754 ethertype='IPv6',
755 )
756 ]
757 sec_group_rules_client = security_group_rules_client
758 for ruleset in rulesets:
759 for r_direction in ['ingress', 'egress']:
760 ruleset['direction'] = r_direction
761 try:
762 sg_rule = self._create_security_group_rule(
763 sec_group_rules_client=sec_group_rules_client,
764 secgroup=secgroup,
765 security_groups_client=security_groups_client,
766 **ruleset)
767 except lib_exc.Conflict as ex:
768 # if rule already exist - skip rule and continue
769 msg = 'Security group rule already exists'
770 if msg not in ex._error_string:
771 raise ex
772 else:
773 self.assertEqual(r_direction, sg_rule['direction'])
774 rules.append(sg_rule)
775
776 return rules
777
778 def _get_router(self, client=None, tenant_id=None):
779 """Retrieve a router for the given tenant id.
780
781 If a public router has been configured, it will be returned.
782
783 If a public router has not been configured, but a public
784 network has, a tenant router will be created and returned that
785 routes traffic to the public network.
786 """
787 if not client:
788 client = self.routers_client
789 if not tenant_id:
790 tenant_id = client.tenant_id
791 router_id = CONF.network.public_router_id
792 network_id = CONF.network.public_network_id
793 if router_id:
794 body = client.show_router(router_id)
795 return body['router']
796 elif network_id:
797 router = self._create_router(client, tenant_id)
798 kwargs = {'external_gateway_info': dict(network_id=network_id)}
799 router = client.update_router(router['id'], **kwargs)['router']
800 return router
801 else:
802 raise Exception("Neither of 'public_router_id' or "
803 "'public_network_id' has been defined.")
804
805 def _create_router(self, client=None, tenant_id=None,
806 namestart='router-smoke'):
807 if not client:
808 client = self.routers_client
809 if not tenant_id:
810 tenant_id = client.tenant_id
811 name = data_utils.rand_name(namestart)
812 result = client.create_router(name=name,
813 admin_state_up=True,
814 tenant_id=tenant_id)
815 router = result['router']
816 self.assertEqual(router['name'], name)
817 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
818 client.delete_router,
819 router['id'])
820 return router
821
822 def _update_router_admin_state(self, router, admin_state_up):
823 kwargs = dict(admin_state_up=admin_state_up)
824 router = self.routers_client.update_router(
825 router['id'], **kwargs)['router']
826 self.assertEqual(admin_state_up, router['admin_state_up'])
827
828 def create_networks(self, networks_client=None,
829 routers_client=None, subnets_client=None,
830 tenant_id=None, dns_nameservers=None,
831 port_security_enabled=True):
832 """Create a network with a subnet connected to a router.
833
834 The baremetal driver is a special case since all nodes are
835 on the same shared network.
836
837 :param tenant_id: id of tenant to create resources in.
838 :param dns_nameservers: list of dns servers to send to subnet.
839 :returns: network, subnet, router
840 """
841 if CONF.network.shared_physical_network:
842 # NOTE(Shrews): This exception is for environments where tenant
843 # credential isolation is available, but network separation is
844 # not (the current baremetal case). Likely can be removed when
845 # test account mgmt is reworked:
846 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
847 if not CONF.compute.fixed_network_name:
848 m = 'fixed_network_name must be specified in config'
849 raise lib_exc.InvalidConfiguration(m)
850 network = self._get_network_by_name(
851 CONF.compute.fixed_network_name)
852 router = None
853 subnet = None
854 else:
855 network = self._create_network(
856 networks_client=networks_client,
857 tenant_id=tenant_id,
858 port_security_enabled=port_security_enabled)
859 router = self._get_router(client=routers_client,
860 tenant_id=tenant_id)
861 subnet_kwargs = dict(network=network,
862 subnets_client=subnets_client,
863 routers_client=routers_client)
864 # use explicit check because empty list is a valid option
865 if dns_nameservers is not None:
866 subnet_kwargs['dns_nameservers'] = dns_nameservers
867 subnet = self._create_subnet(**subnet_kwargs)
868 if not routers_client:
869 routers_client = self.routers_client
870 router_id = router['id']
871 routers_client.add_router_interface(router_id,
872 subnet_id=subnet['id'])
873
874 # save a cleanup job to remove this association between
875 # router and subnet
876 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
877 routers_client.remove_router_interface, router_id,
878 subnet_id=subnet['id'])
879 return network, subnet, router