blob: 32fb5813d3d8eb875d7127caa152dfa5e127ab06 [file] [log] [blame]
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +01001# Copyright 2020 Red Hat, Inc.
2# All Rights Reserved.
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.
15
16from neutron_lib import constants as lib_constants
17from oslo_log import log
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +020018from paramiko import ssh_exception as ssh_exc
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010019from tempest.common import utils as tempest_utils
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010020from tempest.lib.common.utils import data_utils
21from tempest.lib import decorators
22from tempest.lib import exceptions as lib_exc
23
24from neutron_tempest_plugin.common import ip
25from neutron_tempest_plugin.common import ssh
26from neutron_tempest_plugin.common import utils
27from neutron_tempest_plugin import config
28from neutron_tempest_plugin.scenario import base
29
30CONF = config.CONF
31
32LOG = log.getLogger(__name__)
33
34
35def turn_nic6_on(ssh, ipv6_port):
36 """Turns the IPv6 vNIC on
37
38 Required because guest images usually set only the first vNIC on boot.
39 Searches for the IPv6 vNIC's MAC and brings it up.
40
41 @param ssh: RemoteClient ssh instance to server
42 @param ipv6_port: port from IPv6 network attached to the server
43 """
44 ip_command = ip.IPCommand(ssh)
45 nic = ip_command.get_nic_name_by_mac(ipv6_port['mac_address'])
ccamposrdfca7c12021-03-06 00:26:14 +010046 ip_command.set_link(nic, "up")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010047
ccamposrdfca7c12021-03-06 00:26:14 +010048
49def configure_eth_connection_profile_NM(ssh):
50 """Prepare a Network manager profile for ipv6 port
51
52 By default the NetworkManager uses IPv6 privacy
53 format it isn't supported by neutron then we create
54 a ether profile with eui64 supported format
55
56 @param ssh: RemoteClient ssh instance to server
57 """
58 # NOTE(ccamposr): on RHEL based OS we need a ether profile with
59 # eui64 format
60 if nmcli_command_exists(ssh):
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010061 try:
ccamposrdfca7c12021-03-06 00:26:14 +010062 ssh.execute_script('nmcli connection add type ethernet con-name '
63 'ether ifname "*"', become_root=True)
64 ssh.execute_script('nmcli con mod ether ipv6.addr-gen-mode eui64',
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010065 become_root=True)
ccamposrdfca7c12021-03-06 00:26:14 +010066
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010067 except lib_exc.SSHExecCommandFailed as e:
68 # NOTE(slaweq): Sometimes it can happen that this SSH command
69 # will fail because of some error from network manager in
70 # guest os.
71 # But even then doing ip link set up below is fine and
72 # IP address should be configured properly.
ccamposrdfca7c12021-03-06 00:26:14 +010073 LOG.debug("Error creating NetworkManager profile. "
74 "Error message: %(error)s",
75 {'error': e})
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010076
77
ccamposrdfca7c12021-03-06 00:26:14 +010078def nmcli_command_exists(ssh):
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010079 return "False" not in ssh.execute_script(
ccamposrdfca7c12021-03-06 00:26:14 +010080 'if ! type nmcli > /dev/null ; then echo "False"; fi')
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010081
82
83class IPv6Test(base.BaseTempestTestCase):
84 credentials = ['primary', 'admin']
85
86 ipv6_ra_mode = 'slaac'
87 ipv6_address_mode = 'slaac'
88
89 @classmethod
90 @tempest_utils.requires_ext(extension="router", service="network")
91 def resource_setup(cls):
92 super(IPv6Test, cls).resource_setup()
yangjianfeng23e40c22020-11-22 08:42:18 +000093 cls.reserve_external_subnet_cidrs()
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010094 cls._setup_basic_resources()
95
96 @classmethod
97 def _setup_basic_resources(cls):
98 cls.network = cls.create_network()
99 cls.subnet = cls.create_subnet(cls.network)
100 cls.router = cls.create_router_by_client()
101 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
102 cls.keypair = cls.create_keypair()
103 cls.secgroup = cls.create_security_group(
104 name=data_utils.rand_name('secgroup'))
105 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
106 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
107
108 def _test_ipv6_address_configured(self, ssh_client, vm, ipv6_port):
109 ipv6_address = ipv6_port['fixed_ips'][0]['ip_address']
110 ip_command = ip.IPCommand(ssh_client)
111
112 def guest_has_address(expected_address):
113 ip_addresses = [a.address for a in ip_command.list_addresses()]
114 for ip_address in ip_addresses:
115 if expected_address in ip_address:
116 return True
117 return False
118
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200119 try:
120 # Set NIC with IPv6 to be UP and wait until IPv6 address will be
121 # configured on this NIC
122 turn_nic6_on(ssh_client, ipv6_port)
123 # And check if IPv6 address will be properly configured on this NIC
124 utils.wait_until_true(
125 lambda: guest_has_address(ipv6_address),
126 timeout=120,
127 exception=RuntimeError(
128 "Timed out waiting for IP address {!r} to be configured "
129 "in the VM {!r}.".format(ipv6_address, vm['id'])))
130 except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
131 LOG.debug(ssh_e)
132 self._log_console_output([vm])
133 self._log_local_network_status()
134 raise
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100135
136 def _test_ipv6_hotplug(self, ra_mode, address_mode):
137 ipv6_networks = [self.create_network() for _ in range(2)]
138 for net in ipv6_networks:
139 subnet = self.create_subnet(
140 network=net, ip_version=6,
141 ipv6_ra_mode=ra_mode, ipv6_address_mode=address_mode)
142 self.create_router_interface(self.router['id'], subnet['id'])
143
144 server_kwargs = {
145 'flavor_ref': CONF.compute.flavor_ref,
146 'image_ref': CONF.compute.image_ref,
147 'key_name': self.keypair['name'],
148 'networks': [
149 {'uuid': self.network['id']},
150 {'uuid': ipv6_networks[0]['id']}],
151 'security_groups': [{'name': self.secgroup['name']}],
152 }
153 vm = self.create_server(**server_kwargs)['server']
154 self.wait_for_server_active(vm)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200155 self.wait_for_guest_os_ready(vm)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100156 ipv4_port = self.client.list_ports(
157 network_id=self.network['id'],
158 device_id=vm['id'])['ports'][0]
159 fip = self.create_floatingip(port=ipv4_port)
160 ssh_client = ssh.Client(
161 fip['floating_ip_address'], CONF.validation.image_ssh_user,
162 pkey=self.keypair['private_key'])
163
164 ipv6_port = self.client.list_ports(
165 network_id=ipv6_networks[0]['id'],
166 device_id=vm['id'])['ports'][0]
167 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
168
169 # Now remove this port IPv6 port from the VM and attach new one
170 self.delete_interface(vm['id'], ipv6_port['id'])
171
172 # And plug VM to the second IPv6 network
173 ipv6_port = self.create_port(ipv6_networks[1])
ccamposrdfca7c12021-03-06 00:26:14 +0100174 # Add NetworkManager profile with ipv6 eui64 format to guest OS
175 configure_eth_connection_profile_NM(ssh_client)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100176 self.create_interface(vm['id'], ipv6_port['id'])
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000177 ip.wait_for_interface_status(
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100178 self.os_primary.interfaces_client, vm['id'],
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000179 ipv6_port['id'], lib_constants.PORT_STATUS_ACTIVE,
180 ssh_client=ssh_client, mac_address=ipv6_port['mac_address'])
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100181 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
182
183 @decorators.idempotent_id('b13e5408-5250-4a42-8e46-6996ce613e91')
184 def test_ipv6_hotplug_slaac(self):
185 self._test_ipv6_hotplug("slaac", "slaac")
186
187 @decorators.idempotent_id('9aaedbc4-986d-42d5-9177-3e721728e7e0')
188 def test_ipv6_hotplug_dhcpv6stateless(self):
189 self._test_ipv6_hotplug("dhcpv6-stateless", "dhcpv6-stateless")