blob: d9d1a22e5c2953b67d820f90a75934ac562fe3e3 [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
36def turn_nic6_on(ssh, ipv6_port):
37 """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.
41
42 @param ssh: RemoteClient ssh instance to server
43 @param ipv6_port: port from IPv6 network attached to the server
44 """
45 ip_command = ip.IPCommand(ssh)
46 nic = ip_command.get_nic_name_by_mac(ipv6_port['mac_address'])
ccamposrdfca7c12021-03-06 00:26:14 +010047 ip_command.set_link(nic, "up")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010048
ccamposrdfca7c12021-03-06 00:26:14 +010049
50def configure_eth_connection_profile_NM(ssh):
51 """Prepare a Network manager profile for ipv6 port
52
53 By default the NetworkManager uses IPv6 privacy
54 format it isn't supported by neutron then we create
55 a ether profile with eui64 supported format
56
57 @param ssh: RemoteClient ssh instance to server
58 """
59 # NOTE(ccamposr): on RHEL based OS we need a ether profile with
60 # eui64 format
61 if nmcli_command_exists(ssh):
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010062 try:
ccamposrdfca7c12021-03-06 00:26:14 +010063 ssh.execute_script('nmcli connection add type ethernet con-name '
64 'ether ifname "*"', become_root=True)
65 ssh.execute_script('nmcli con mod ether ipv6.addr-gen-mode eui64',
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010066 become_root=True)
ccamposrdfca7c12021-03-06 00:26:14 +010067
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010068 except lib_exc.SSHExecCommandFailed as e:
69 # NOTE(slaweq): Sometimes it can happen that this SSH command
70 # will fail because of some error from network manager in
71 # guest os.
72 # But even then doing ip link set up below is fine and
73 # IP address should be configured properly.
ccamposrdfca7c12021-03-06 00:26:14 +010074 LOG.debug("Error creating NetworkManager profile. "
75 "Error message: %(error)s",
76 {'error': e})
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010077
78
ccamposrdfca7c12021-03-06 00:26:14 +010079def nmcli_command_exists(ssh):
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010080 return "False" not in ssh.execute_script(
ccamposrdfca7c12021-03-06 00:26:14 +010081 'if ! type nmcli > /dev/null ; then echo "False"; fi')
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010082
83
84class IPv6Test(base.BaseTempestTestCase):
85 credentials = ['primary', 'admin']
86
87 ipv6_ra_mode = 'slaac'
88 ipv6_address_mode = 'slaac'
89
90 @classmethod
ibumarskov4d740442021-03-30 11:58:25 +040091 def skip_checks(cls):
92 super(IPv6Test, cls).skip_checks()
93 if not CONF.network_feature_enabled.ipv6:
94 raise cls.skipException("IPv6 is not enabled")
95
96 @classmethod
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +010097 @tempest_utils.requires_ext(extension="router", service="network")
98 def resource_setup(cls):
99 super(IPv6Test, cls).resource_setup()
yangjianfeng23e40c22020-11-22 08:42:18 +0000100 cls.reserve_external_subnet_cidrs()
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100101 cls._setup_basic_resources()
102
103 @classmethod
104 def _setup_basic_resources(cls):
105 cls.network = cls.create_network()
106 cls.subnet = cls.create_subnet(cls.network)
107 cls.router = cls.create_router_by_client()
108 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
109 cls.keypair = cls.create_keypair()
110 cls.secgroup = cls.create_security_group(
111 name=data_utils.rand_name('secgroup'))
112 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
113 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
114
115 def _test_ipv6_address_configured(self, ssh_client, vm, ipv6_port):
116 ipv6_address = ipv6_port['fixed_ips'][0]['ip_address']
117 ip_command = ip.IPCommand(ssh_client)
118
119 def guest_has_address(expected_address):
120 ip_addresses = [a.address for a in ip_command.list_addresses()]
121 for ip_address in ip_addresses:
122 if expected_address in ip_address:
123 return True
124 return False
125
Slawek Kaplonski8cccfe02020-09-29 22:34:09 +0200126 try:
127 # Set NIC with IPv6 to be UP and wait until IPv6 address will be
128 # configured on this NIC
129 turn_nic6_on(ssh_client, ipv6_port)
130 # And check if IPv6 address will be properly configured on this NIC
131 utils.wait_until_true(
132 lambda: guest_has_address(ipv6_address),
133 timeout=120,
134 exception=RuntimeError(
135 "Timed out waiting for IP address {!r} to be configured "
136 "in the VM {!r}.".format(ipv6_address, vm['id'])))
137 except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
138 LOG.debug(ssh_e)
139 self._log_console_output([vm])
140 self._log_local_network_status()
141 raise
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100142
143 def _test_ipv6_hotplug(self, ra_mode, address_mode):
144 ipv6_networks = [self.create_network() for _ in range(2)]
145 for net in ipv6_networks:
146 subnet = self.create_subnet(
147 network=net, ip_version=6,
148 ipv6_ra_mode=ra_mode, ipv6_address_mode=address_mode)
149 self.create_router_interface(self.router['id'], subnet['id'])
150
151 server_kwargs = {
152 'flavor_ref': CONF.compute.flavor_ref,
153 'image_ref': CONF.compute.image_ref,
154 'key_name': self.keypair['name'],
155 'networks': [
156 {'uuid': self.network['id']},
157 {'uuid': ipv6_networks[0]['id']}],
158 'security_groups': [{'name': self.secgroup['name']}],
159 }
160 vm = self.create_server(**server_kwargs)['server']
161 self.wait_for_server_active(vm)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200162 self.wait_for_guest_os_ready(vm)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100163 ipv4_port = self.client.list_ports(
164 network_id=self.network['id'],
165 device_id=vm['id'])['ports'][0]
166 fip = self.create_floatingip(port=ipv4_port)
167 ssh_client = ssh.Client(
168 fip['floating_ip_address'], CONF.validation.image_ssh_user,
169 pkey=self.keypair['private_key'])
170
171 ipv6_port = self.client.list_ports(
172 network_id=ipv6_networks[0]['id'],
173 device_id=vm['id'])['ports'][0]
174 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
175
176 # Now remove this port IPv6 port from the VM and attach new one
177 self.delete_interface(vm['id'], ipv6_port['id'])
178
179 # And plug VM to the second IPv6 network
180 ipv6_port = self.create_port(ipv6_networks[1])
ccamposrdfca7c12021-03-06 00:26:14 +0100181 # Add NetworkManager profile with ipv6 eui64 format to guest OS
182 configure_eth_connection_profile_NM(ssh_client)
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100183 self.create_interface(vm['id'], ipv6_port['id'])
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000184 ip.wait_for_interface_status(
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100185 self.os_primary.interfaces_client, vm['id'],
Rodolfo Alonso Hernandez0adf8a22020-06-11 11:28:25 +0000186 ipv6_port['id'], lib_constants.PORT_STATUS_ACTIVE,
187 ssh_client=ssh_client, mac_address=ipv6_port['mac_address'])
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100188 self._test_ipv6_address_configured(ssh_client, vm, ipv6_port)
189
ibumarskov4d740442021-03-30 11:58:25 +0400190 @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
191 "DHCPv6 attributes are not enabled.")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100192 @decorators.idempotent_id('b13e5408-5250-4a42-8e46-6996ce613e91')
193 def test_ipv6_hotplug_slaac(self):
194 self._test_ipv6_hotplug("slaac", "slaac")
195
ibumarskov4d740442021-03-30 11:58:25 +0400196 @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
197 "DHCPv6 attributes are not enabled.")
Slawek Kaplonskia1e88c42020-03-03 03:00:48 +0100198 @decorators.idempotent_id('9aaedbc4-986d-42d5-9177-3e721728e7e0')
199 def test_ipv6_hotplug_dhcpv6stateless(self):
200 self._test_ipv6_hotplug("dhcpv6-stateless", "dhcpv6-stateless")