blob: d0545e20e184def820a1049a9259ca5c7ee7fd3f [file] [log] [blame]
Slawek Kaplonski2bc73672020-10-27 13:06:08 +01001# All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
Eduardo Olivares32a7fbe2022-02-04 10:22:42 +010014import netaddr
15from neutron_lib import constants
Slawek Kaplonski2bc73672020-10-27 13:06:08 +010016from oslo_log import log
17from paramiko import ssh_exception as ssh_exc
18from tempest.common import utils
19from tempest.lib.common.utils import data_utils
20from tempest.lib import decorators
21from tempest.lib import exceptions as lib_exc
Eduardo Olivares32a7fbe2022-02-04 10:22:42 +010022import testtools
Slawek Kaplonski2bc73672020-10-27 13:06:08 +010023
24from neutron_tempest_plugin.common import ssh
Eduardo Olivares32a7fbe2022-02-04 10:22:42 +010025from neutron_tempest_plugin.common import utils as neutron_utils
Slawek Kaplonski2bc73672020-10-27 13:06:08 +010026from neutron_tempest_plugin import config
27from neutron_tempest_plugin.scenario import base
28
29CONF = config.CONF
30LOG = log.getLogger(__name__)
31
32
33class DHCPTest(base.BaseTempestTestCase):
34
35 credentials = ['primary', 'admin']
36 force_tenant_isolation = False
37
38 @classmethod
39 def resource_setup(cls):
40 super(DHCPTest, cls).resource_setup()
41 cls.rand_name = data_utils.rand_name(
42 cls.__name__.rsplit('.', 1)[-1])
43 cls.network = cls.create_network(name=cls.rand_name)
44 cls.subnet = cls.create_subnet(
45 network=cls.network, name=cls.rand_name)
46 cls.router = cls.create_router_by_client()
47 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
48 cls.keypair = cls.create_keypair(name=cls.rand_name)
49 cls.security_group = cls.create_security_group(name=cls.rand_name)
50 cls.create_loginable_secgroup_rule(cls.security_group['id'])
51
52 @utils.requires_ext(extension='extra_dhcp_opt', service='network')
53 @decorators.idempotent_id('58f7c094-1980-4e03-b0d3-6c4dd27217b1')
54 def test_extra_dhcp_opts(self):
55 """This test case tests DHCP extra options configured for Neutron port.
56
57 Test is checking just extra option "15" which is domain-name
58 according to the RFC 2132:
59 https://tools.ietf.org/html/rfc2132#section-5.3
60
61 To test that option, there is spawned VM connected to the port with
62 configured extra_dhcp_opts and test asserts that search domain name is
63 configured inside VM in /etc/resolv.conf file
64 """
65
66 test_domain = "test.domain"
67 extra_dhcp_opts = [
68 {'opt_name': 'domain-name',
69 'opt_value': '"%s"' % test_domain}]
70 port = self.create_port(
71 network=self.network, name=self.rand_name,
72 security_groups=[self.security_group['id']],
73 extra_dhcp_opts=extra_dhcp_opts)
74 floating_ip = self.create_floatingip(port=port)
75
76 server = self.create_server(
77 flavor_ref=CONF.compute.flavor_ref,
78 image_ref=CONF.compute.image_ref,
79 key_name=self.keypair['name'],
80 networks=[{'port': port['id']}])
81 self.wait_for_server_active(server['server'])
82 self.wait_for_guest_os_ready(server['server'])
83
84 try:
85 ssh_client = ssh.Client(
86 floating_ip['floating_ip_address'],
87 CONF.validation.image_ssh_user,
88 pkey=self.keypair['private_key'])
89 vm_resolv_conf = ssh_client.exec_command(
90 "cat /etc/resolv.conf")
91 self.assertIn(test_domain, vm_resolv_conf)
92 except (lib_exc.SSHTimeout,
93 ssh_exc.AuthenticationException,
94 AssertionError) as error:
95 LOG.debug(error)
96 self._log_console_output([server])
97 self._log_local_network_status()
98 raise
Eduardo Olivares32a7fbe2022-02-04 10:22:42 +010099
100
101class DHCPPortUpdateTest(base.BaseTempestTestCase):
102
103 credentials = ['primary', 'admin']
104
105 @classmethod
106 def resource_setup(cls):
107 super(DHCPPortUpdateTest, cls).resource_setup()
108 cls.rand_name = data_utils.rand_name(
109 cls.__name__.rsplit('.', 1)[-1])
110 cls.network = cls.create_network(name=cls.rand_name)
111 cls.router = cls.create_router_by_client()
112 cls.keypair = cls.create_keypair(name=cls.rand_name)
113 cls.security_group = cls.create_security_group(name=cls.rand_name)
114 cls.create_loginable_secgroup_rule(cls.security_group['id'])
115 cls.create_pingable_secgroup_rule(cls.security_group['id'])
116
117 @testtools.skipUnless(
118 CONF.neutron_plugin_options.firewall_driver == 'ovn',
119 "OVN driver is required to run this test - "
120 "LP#1942794 solution only applied to OVN")
121 @decorators.idempotent_id('8171cc68-9dbb-46ca-b065-17b5b2e26094')
122 def test_modify_dhcp_port_ip_address(self):
123 """Test Scenario
124
125 1) Create a network and a subnet with DHCP enabled
126 2) Modify the default IP address from the subnet DHCP port
127 3) Create a server in this network and check ssh connectivity
128
129 For the step 3), the server needs to obtain ssh keys from the metadata
130
131 Related bug: LP#1942794
132 """
133 # create subnet (dhcp is enabled by default)
134 subnet = self.create_subnet(network=self.network, name=self.rand_name)
135
136 def _get_dhcp_ports():
137 # in some cases, like ML2/OVS, the subnet port associated to DHCP
138 # is created with device_owner='network:dhcp'
139 dhcp_ports = self.client.list_ports(
140 network_id=self.network['id'],
141 device_owner=constants.DEVICE_OWNER_DHCP)['ports']
142 # in other cases, like ML2/OVN, the subnet port used for metadata
143 # is created with device_owner='network:distributed'
144 distributed_ports = self.client.list_ports(
145 network_id=self.network['id'],
146 device_owner=constants.DEVICE_OWNER_DISTRIBUTED)['ports']
147 self.dhcp_ports = dhcp_ports + distributed_ports
148 self.assertLessEqual(
149 len(self.dhcp_ports), 1, msg='Only one port was expected')
150 return len(self.dhcp_ports) == 1
151
152 # obtain the dhcp port
153 # in some cases this port is not created together with the subnet, but
154 # immediately after it, so some delay may be needed and that is the
155 # reason why a waiter function is used here
156 self.dhcp_ports = []
157 neutron_utils.wait_until_true(
158 lambda: _get_dhcp_ports(),
159 timeout=10)
160 dhcp_port = self.dhcp_ports[0]
161
162 # modify DHCP port IP address
163 old_dhcp_port_ip = netaddr.IPAddress(
164 dhcp_port['fixed_ips'][0]['ip_address'])
165 if str(old_dhcp_port_ip) != subnet['allocation_pools'][0]['end']:
166 new_dhcp_port_ip = str(old_dhcp_port_ip + 1)
167 else:
168 new_dhcp_port_ip = str(old_dhcp_port_ip - 1)
169 self.update_port(port=dhcp_port,
170 fixed_ips=[{'subnet_id': subnet['id'],
171 'ip_address': new_dhcp_port_ip}])
172
173 # create server
174 server = self.create_server(
175 flavor_ref=CONF.compute.flavor_ref,
176 image_ref=CONF.compute.image_ref,
177 key_name=self.keypair['name'],
178 security_groups=[{'name': self.security_group['name']}],
179 networks=[{'uuid': self.network['id']}])
180
181 # attach fip to the server
182 self.create_router_interface(self.router['id'], subnet['id'])
183 server_port = self.client.list_ports(
184 network_id=self.network['id'],
185 device_id=server['server']['id'])['ports'][0]
186 fip = self.create_floatingip(port_id=server_port['id'])
187
188 # check connectivity
189 self.check_connectivity(fip['floating_ip_address'],
190 CONF.validation.image_ssh_user,
191 self.keypair['private_key'])