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