blob: 41ac2e61a1deed80807f6413e99802cf083c74e4 [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
ccamposrfd7d8a72022-03-01 00:30:02 +010036def turn_nic6_on(ssh, ipv6_port, 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.
40 Searches for the IPv6 vNIC's MAC and brings it 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
47 @param ipv6_port: port from IPv6 network attached to the server
48 """
49 ip_command = ip.IPCommand(ssh)
50 nic = ip_command.get_nic_name_by_mac(ipv6_port['mac_address'])
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)
153
154 def guest_has_address(expected_address):
155 ip_addresses = [a.address for a in ip_command.list_addresses()]
156 for ip_address in ip_addresses:
157 if expected_address in ip_address:
158 return True
159 return False
ccamposrfd7d8a72022-03-01 00:30:02 +0100160 # Set NIC with IPv6 to be UP and wait until IPv6 address
161 # will be configured on this NIC
162 turn_nic6_on(ssh_client, ipv6_port, False)
163 # And check if IPv6 address will be properly configured
164 # on this NIC
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200165 try:
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200166 utils.wait_until_true(
167 lambda: guest_has_address(ipv6_address),
ccamposrfd7d8a72022-03-01 00:30:02 +0100168 timeout=60)
169 except utils.WaitTimeout:
170 LOG.debug('Timeout without NM configuration')
171 except (lib_exc.SSHTimeout,
172 ssh_exc.AuthenticationException) as ssh_e:
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200173 LOG.debug(ssh_e)
174 self._log_console_output([vm])
175 self._log_local_network_status()
176 raise
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100177
ccamposrfd7d8a72022-03-01 00:30:02 +0100178 if not guest_has_address(ipv6_address):
179 try:
180 # Set NIC with IPv6 to be UP and wait until IPv6 address
181 # will be configured on this NIC
182 turn_nic6_on(ssh_client, ipv6_port)
183 # And check if IPv6 address will be properly configured
184 # on this NIC
185 utils.wait_until_true(
186 lambda: guest_has_address(ipv6_address),
187 timeout=90,
188 exception=RuntimeError(
189 "Timed out waiting for IP address {!r} to be "
190 "configured in the VM {!r}.".format(ipv6_address,
191 vm['id'])))
192 except (lib_exc.SSHTimeout,
193 ssh_exc.AuthenticationException) as ssh_e:
194 LOG.debug(ssh_e)
195 self._log_console_output([vm])
196 self._log_local_network_status()
197 raise
198
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100199 def _test_ipv6_hotplug(self, ra_mode, address_mode):
200 ipv6_networks = [self.create_network() for _ in range(2)]
201 for net in ipv6_networks:
202 subnet = self.create_subnet(
203 network=net, ip_version=6,
204 ipv6_ra_mode=ra_mode, ipv6_address_mode=address_mode)
205 self.create_router_interface(self.router['id'], subnet['id'])
206
207 server_kwargs = {
208 'flavor_ref': CONF.compute.flavor_ref,
209 'image_ref': CONF.compute.image_ref,
210 'key_name': self.keypair['name'],
211 'networks': [
212 {'uuid': self.network['id']},
213 {'uuid': ipv6_networks[0]['id']}],
214 'security_groups': [{'name': self.secgroup['name']}],
215 }
216 vm = self.create_server(**server_kwargs)['server']
217 self.wait_for_server_active(vm)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200218 self.wait_for_guest_os_ready(vm)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100219 ipv4_port = self.client.list_ports(
220 network_id=self.network['id'],
221 device_id=vm['id'])['ports'][0]
222 fip = self.create_floatingip(port=ipv4_port)
223 ssh_client = ssh.Client(
224 fip['floating_ip_address'], CONF.validation.image_ssh_user,
225 pkey=self.keypair['private_key'])
226
227 ipv6_port = self.client.list_ports(
228 network_id=ipv6_networks[0]['id'],
229 device_id=vm['id'])['ports'][0]
230 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
231
232 # Now remove this port IPv6 port from the VM and attach new one
233 self.delete_interface(vm['id'], ipv6_port['id'])
234
235 # And plug VM to the second IPv6 network
236 ipv6_port = self.create_port(ipv6_networks[1])
ccamposrdfca7c12021-03-06 00:26:14 +0100237 # Add NetworkManager profile with ipv6 eui64 format to guest OS
238 configure_eth_connection_profile_NM(ssh_client)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100239 self.create_interface(vm['id'], ipv6_port['id'])
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000240 ip.wait_for_interface_status(
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100241 self.os_primary.interfaces_client, vm['id'],
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000242 ipv6_port['id'], lib_constants.PORT_STATUS_ACTIVE,
243 ssh_client=ssh_client, mac_address=ipv6_port['mac_address'])
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100244 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
245
ibumarskov4d740442021-03-30 11:58:25 +0400246 @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
247 "DHCPv6 attributes are not enabled.")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100248 @decorators.idempotent_id('b13e5408-5250-4a42-8e46-6996ce613e91')
249 def test_ipv6_hotplug_slaac(self):
250 self._test_ipv6_hotplug("slaac", "slaac")
251
ibumarskov4d740442021-03-30 11:58:25 +0400252 @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
253 "DHCPv6 attributes are not enabled.")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100254 @decorators.idempotent_id('9aaedbc4-986d-42d5-9177-3e721728e7e0')
255 def test_ipv6_hotplug_dhcpv6stateless(self):
256 self._test_ipv6_hotplug("dhcpv6-stateless", "dhcpv6-stateless")