blob: 32394174beebc687e0370141a5e3b8e3549c4afc [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
ibumarskov4d740442021-03-30 11:58:25 +040023import testtools
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010024
25from neutron_tempest_plugin.common import ip
26from neutron_tempest_plugin.common import ssh
27from neutron_tempest_plugin.common import utils
28from neutron_tempest_plugin import config
29from neutron_tempest_plugin.scenario import base
30
31CONF = config.CONF
32
33LOG = log.getLogger(__name__)
34
35
Yatin Karele5aa9ea2026-03-02 12:58:29 +053036def turn_nic6_on(ssh, nic, config_nic=True):
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010037 """Turns the IPv6 vNIC on
38
39 Required because guest images usually set only the first vNIC on boot.
Yatin Karele5aa9ea2026-03-02 12:58:29 +053040 Brings the specified NIC up.
ccamposrfd7d8a72022-03-01 00:30:02 +010041 # NOTE(slaweq): on RHEL based OS ifcfg file for new interface is
42 # needed to make IPv6 working on it, so if
43 # /etc/sysconfig/network-scripts directory exists ifcfg-%(nic)s file
44 # should be added in it
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010045
46 @param ssh: RemoteClient ssh instance to server
Yatin Karele5aa9ea2026-03-02 12:58:29 +053047 @param nic: network interface name
48 @param config_nic: whether to configure with NetworkManager
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010049 """
50 ip_command = ip.IPCommand(ssh)
ccamposrfd7d8a72022-03-01 00:30:02 +010051
52 if config_nic:
53 try:
54 if sysconfig_network_scripts_dir_exists(ssh):
55 ssh.execute_script(
56 'echo -e "DEVICE=%(nic)s\\nNAME=%(nic)s\\nIPV6INIT=yes" | '
57 'tee /etc/sysconfig/network-scripts/ifcfg-%(nic)s; '
58 % {'nic': nic}, become_root=True)
59 if nmcli_command_exists(ssh):
60 ssh.execute_script('nmcli connection reload %s' % nic,
61 become_root=True)
62 ssh.execute_script('nmcli con mod %s ipv6.addr-gen-mode eui64'
63 % nic, become_root=True)
64 ssh.execute_script('nmcli connection up %s' % nic,
65 become_root=True)
66
67 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.
73 LOG.debug("Error creating NetworkManager profile. "
74 "Error message: %(error)s",
75 {'error': e})
76
ccamposrdfca7c12021-03-06 00:26:14 +010077 ip_command.set_link(nic, "up")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010078
ccamposrdfca7c12021-03-06 00:26:14 +010079
80def configure_eth_connection_profile_NM(ssh):
81 """Prepare a Network manager profile for ipv6 port
82
83 By default the NetworkManager uses IPv6 privacy
84 format it isn't supported by neutron then we create
85 a ether profile with eui64 supported format
86
87 @param ssh: RemoteClient ssh instance to server
88 """
89 # NOTE(ccamposr): on RHEL based OS we need a ether profile with
90 # eui64 format
91 if nmcli_command_exists(ssh):
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010092 try:
ccamposrdfca7c12021-03-06 00:26:14 +010093 ssh.execute_script('nmcli connection add type ethernet con-name '
94 'ether ifname "*"', become_root=True)
95 ssh.execute_script('nmcli con mod ether ipv6.addr-gen-mode eui64',
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010096 become_root=True)
ccamposrdfca7c12021-03-06 00:26:14 +010097
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010098 except lib_exc.SSHExecCommandFailed as e:
99 # NOTE(slaweq): Sometimes it can happen that this SSH command
100 # will fail because of some error from network manager in
101 # guest os.
102 # But even then doing ip link set up below is fine and
103 # IP address should be configured properly.
ccamposrdfca7c12021-03-06 00:26:14 +0100104 LOG.debug("Error creating NetworkManager profile. "
105 "Error message: %(error)s",
106 {'error': e})
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100107
108
ccamposrfd7d8a72022-03-01 00:30:02 +0100109def sysconfig_network_scripts_dir_exists(ssh):
110 return "False" not in ssh.execute_script(
111 'test -d /etc/sysconfig/network-scripts/ || echo "False"')
112
113
ccamposrdfca7c12021-03-06 00:26:14 +0100114def nmcli_command_exists(ssh):
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100115 return "False" not in ssh.execute_script(
ccamposrdfca7c12021-03-06 00:26:14 +0100116 'if ! type nmcli > /dev/null ; then echo "False"; fi')
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100117
118
119class IPv6Test(base.BaseTempestTestCase):
120 credentials = ['primary', 'admin']
121
122 ipv6_ra_mode = 'slaac'
123 ipv6_address_mode = 'slaac'
124
125 @classmethod
ibumarskov4d740442021-03-30 11:58:25 +0400126 def skip_checks(cls):
127 super(IPv6Test, cls).skip_checks()
128 if not CONF.network_feature_enabled.ipv6:
129 raise cls.skipException("IPv6 is not enabled")
130
131 @classmethod
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100132 @tempest_utils.requires_ext(extension="router", service="network")
133 def resource_setup(cls):
134 super(IPv6Test, cls).resource_setup()
yangjianfeng23e40c22020-11-22 08:42:18 +0000135 cls.reserve_external_subnet_cidrs()
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100136 cls._setup_basic_resources()
137
138 @classmethod
139 def _setup_basic_resources(cls):
140 cls.network = cls.create_network()
141 cls.subnet = cls.create_subnet(cls.network)
142 cls.router = cls.create_router_by_client()
143 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
144 cls.keypair = cls.create_keypair()
145 cls.secgroup = cls.create_security_group(
146 name=data_utils.rand_name('secgroup'))
147 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
148 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
149
150 def _test_ipv6_address_configured(self, ssh_client, vm, ipv6_port):
151 ipv6_address = ipv6_port['fixed_ips'][0]['ip_address']
152 ip_command = ip.IPCommand(ssh_client)
Yatin Karele5aa9ea2026-03-02 12:58:29 +0530153 nic = ip_command.get_nic_name_by_mac(ipv6_port['mac_address'])
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100154
155 def guest_has_address(expected_address):
156 ip_addresses = [a.address for a in ip_command.list_addresses()]
157 for ip_address in ip_addresses:
158 if expected_address in ip_address:
159 return True
Yatin Karele5aa9ea2026-03-02 12:58:29 +0530160
161 # NOTE(ykarel): Sometimes with the cirros VM a race is seen with
162 # ovs_create_tap feature https://launchpad.net/bugs/2069718 and
163 # ipv6 is not configured, adding nic restart if dad failure is
164 # detected to workaround this
165 if ip_command.has_dadfailed(nic):
166 LOG.debug('DAD failure detected on %s, restarting', nic)
167 ip_command.set_link(nic, "down")
168 ip_command.set_link(nic, "up")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100169 return False
ccamposrfd7d8a72022-03-01 00:30:02 +0100170 # Set NIC with IPv6 to be UP and wait until IPv6 address
171 # will be configured on this NIC
Yatin Karele5aa9ea2026-03-02 12:58:29 +0530172 turn_nic6_on(ssh_client, nic, False)
ccamposrfd7d8a72022-03-01 00:30:02 +0100173 # And check if IPv6 address will be properly configured
174 # on this NIC
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200175 try:
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200176 utils.wait_until_true(
177 lambda: guest_has_address(ipv6_address),
ccamposrfd7d8a72022-03-01 00:30:02 +0100178 timeout=60)
179 except utils.WaitTimeout:
180 LOG.debug('Timeout without NM configuration')
181 except (lib_exc.SSHTimeout,
182 ssh_exc.AuthenticationException) as ssh_e:
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200183 LOG.debug(ssh_e)
184 self._log_console_output([vm])
185 self._log_local_network_status()
186 raise
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100187
ccamposrfd7d8a72022-03-01 00:30:02 +0100188 if not guest_has_address(ipv6_address):
189 try:
190 # Set NIC with IPv6 to be UP and wait until IPv6 address
191 # will be configured on this NIC
Yatin Karele5aa9ea2026-03-02 12:58:29 +0530192 turn_nic6_on(ssh_client, nic)
ccamposrfd7d8a72022-03-01 00:30:02 +0100193 # And check if IPv6 address will be properly configured
194 # on this NIC
195 utils.wait_until_true(
196 lambda: guest_has_address(ipv6_address),
197 timeout=90,
198 exception=RuntimeError(
199 "Timed out waiting for IP address {!r} to be "
200 "configured in the VM {!r}.".format(ipv6_address,
201 vm['id'])))
202 except (lib_exc.SSHTimeout,
203 ssh_exc.AuthenticationException) as ssh_e:
204 LOG.debug(ssh_e)
205 self._log_console_output([vm])
206 self._log_local_network_status()
207 raise
208
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100209 def _test_ipv6_hotplug(self, ra_mode, address_mode):
210 ipv6_networks = [self.create_network() for _ in range(2)]
211 for net in ipv6_networks:
212 subnet = self.create_subnet(
213 network=net, ip_version=6,
214 ipv6_ra_mode=ra_mode, ipv6_address_mode=address_mode)
215 self.create_router_interface(self.router['id'], subnet['id'])
216
217 server_kwargs = {
218 'flavor_ref': CONF.compute.flavor_ref,
219 'image_ref': CONF.compute.image_ref,
220 'key_name': self.keypair['name'],
221 'networks': [
222 {'uuid': self.network['id']},
223 {'uuid': ipv6_networks[0]['id']}],
224 'security_groups': [{'name': self.secgroup['name']}],
225 }
226 vm = self.create_server(**server_kwargs)['server']
227 self.wait_for_server_active(vm)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200228 self.wait_for_guest_os_ready(vm)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100229 ipv4_port = self.client.list_ports(
230 network_id=self.network['id'],
231 device_id=vm['id'])['ports'][0]
232 fip = self.create_floatingip(port=ipv4_port)
233 ssh_client = ssh.Client(
234 fip['floating_ip_address'], CONF.validation.image_ssh_user,
235 pkey=self.keypair['private_key'])
236
237 ipv6_port = self.client.list_ports(
238 network_id=ipv6_networks[0]['id'],
239 device_id=vm['id'])['ports'][0]
240 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
241
242 # Now remove this port IPv6 port from the VM and attach new one
243 self.delete_interface(vm['id'], ipv6_port['id'])
244
245 # And plug VM to the second IPv6 network
246 ipv6_port = self.create_port(ipv6_networks[1])
ccamposrdfca7c12021-03-06 00:26:14 +0100247 # Add NetworkManager profile with ipv6 eui64 format to guest OS
248 configure_eth_connection_profile_NM(ssh_client)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100249 self.create_interface(vm['id'], ipv6_port['id'])
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000250 ip.wait_for_interface_status(
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100251 self.os_primary.interfaces_client, vm['id'],
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000252 ipv6_port['id'], lib_constants.PORT_STATUS_ACTIVE,
253 ssh_client=ssh_client, mac_address=ipv6_port['mac_address'])
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100254 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
255
ibumarskov4d740442021-03-30 11:58:25 +0400256 @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
257 "DHCPv6 attributes are not enabled.")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100258 @decorators.idempotent_id('b13e5408-5250-4a42-8e46-6996ce613e91')
259 def test_ipv6_hotplug_slaac(self):
260 self._test_ipv6_hotplug("slaac", "slaac")
261
ibumarskov4d740442021-03-30 11:58:25 +0400262 @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
263 "DHCPv6 attributes are not enabled.")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100264 @decorators.idempotent_id('9aaedbc4-986d-42d5-9177-3e721728e7e0')
265 def test_ipv6_hotplug_dhcpv6stateless(self):
266 self._test_ipv6_hotplug("dhcpv6-stateless", "dhcpv6-stateless")