blob: f3d03e6a7993a4b21aff4b1ae546a098d3fc082c [file] [log] [blame]
YAMAMOTO Takashi25935722017-01-23 15:34:11 +09001# Copyright (c) 2017 Midokura SARL
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
Hongbin Lu965b03d2018-04-25 22:32:30 +000016import time
17
Slawek Kaplonskiec162e02024-03-06 12:21:24 +010018import ddt
LIU Yulong5ba88ef2017-12-22 10:50:15 +080019from neutron_lib import constants as lib_constants
20from neutron_lib.services.qos import constants as qos_consts
elajkat38d90512021-12-13 14:21:30 +010021from oslo_log import log
Chandan Kumarc125fd12017-11-15 19:41:01 +053022from tempest.common import utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090023from tempest.common import waiters
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090024from tempest.lib.common.utils import data_utils
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +000025from tempest.lib import decorators
Slawek Kaplonski168e5012018-10-04 14:31:19 +020026from tempest.lib import exceptions
Roman Safronov29c2dff2019-04-02 22:01:23 +030027import testtools
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090028
LIU Yulong5ba88ef2017-12-22 10:50:15 +080029from neutron_tempest_plugin.api import base as base_api
Chandan Kumar667d3d32017-09-22 12:24:06 +053030from neutron_tempest_plugin.common import ssh
Brian Haleyba800452017-12-14 10:30:48 -050031from neutron_tempest_plugin.common import utils as common_utils
Chandan Kumar667d3d32017-09-22 12:24:06 +053032from neutron_tempest_plugin import config
33from neutron_tempest_plugin.scenario import base
34from neutron_tempest_plugin.scenario import constants
LIU Yulong5ba88ef2017-12-22 10:50:15 +080035from neutron_tempest_plugin.scenario import test_qos
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090036
37
38CONF = config.CONF
elajkat38d90512021-12-13 14:21:30 +010039LOG = log.getLogger(__name__)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090040
41
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090042class FloatingIpTestCasesMixin(object):
43 credentials = ['primary', 'admin']
44
45 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +053046 @utils.requires_ext(extension="router", service="network")
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090047 def resource_setup(cls):
48 super(FloatingIpTestCasesMixin, cls).resource_setup()
49 cls.network = cls.create_network()
50 cls.subnet = cls.create_subnet(cls.network)
51 cls.router = cls.create_router_by_client()
52 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
53 cls.keypair = cls.create_keypair()
54
rajat294495c042017-06-28 15:37:16 +053055 cls.secgroup = cls.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +053056 name=data_utils.rand_name('secgroup'))['security_group']
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090057 cls.security_groups.append(cls.secgroup)
58 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
59 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
60
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090061 if cls.same_network:
62 cls._dest_network = cls.network
63 else:
64 cls._dest_network = cls._create_dest_network()
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090065
66 @classmethod
Itzik Browna31510f2018-01-19 11:09:48 +020067 def _get_external_gateway(cls):
68 if CONF.network.public_network_id:
69 subnets = cls.os_admin.network_client.list_subnets(
70 network_id=CONF.network.public_network_id)
71
72 for subnet in subnets['subnets']:
Brian Haley33ef4602018-04-26 14:37:49 -040073 if (subnet['gateway_ip'] and
74 subnet['ip_version'] == lib_constants.IP_VERSION_4):
Itzik Browna31510f2018-01-19 11:09:48 +020075 return subnet['gateway_ip']
76
77 @classmethod
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090078 def _create_dest_network(cls):
79 network = cls.create_network()
Federico Ressi0ddc93b2018-04-09 12:01:48 +020080 subnet = cls.create_subnet(network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090081 cls.create_router_interface(cls.router['id'], subnet['id'])
82 return network
83
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000084 def _create_server(self, create_floating_ip=True, network=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090085 if network is None:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000086 network = self.network
87 port = self.create_port(network, security_groups=[self.secgroup['id']])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090088 if create_floating_ip:
Federico Ressi3dfa94c2018-07-06 09:46:39 +020089 fip = self.create_floatingip(port=port)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090090 else:
91 fip = None
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000092 server = self.create_server(
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090093 flavor_ref=CONF.compute.flavor_ref,
94 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000095 key_name=self.keypair['name'],
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090096 networks=[{'port': port['id']}])['server']
rajat294495c042017-06-28 15:37:16 +053097 waiters.wait_for_server_status(self.os_primary.servers_client,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090098 server['id'],
99 constants.SERVER_STATUS_ACTIVE)
100 return {'port': port, 'fip': fip, 'server': server}
101
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100102 def _test_east_west(self, src_has_fip, dest_has_fip):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900103 # The proxy VM is used to control the source VM when it doesn't
104 # have a floating-ip.
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100105 if src_has_fip:
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900106 proxy = None
107 proxy_client = None
108 else:
109 proxy = self._create_server()
110 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
111 CONF.validation.image_ssh_user,
112 pkey=self.keypair['private_key'])
113
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900114 # Source VM
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100115 if src_has_fip:
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900116 src_server = self._create_server()
117 src_server_ip = src_server['fip']['floating_ip_address']
118 else:
119 src_server = self._create_server(create_floating_ip=False)
120 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
121 ssh_client = ssh.Client(src_server_ip,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900122 CONF.validation.image_ssh_user,
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900123 pkey=self.keypair['private_key'],
124 proxy_client=proxy_client)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900125
126 # Destination VM
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100127 if dest_has_fip:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000128 dest_server = self._create_server(network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900129 else:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000130 dest_server = self._create_server(create_floating_ip=False,
131 network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900132
133 # Check connectivity
134 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100135 dest_server['port']['fixed_ips'][0]['ip_address'],
136 servers=[src_server, dest_server])
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100137 if dest_has_fip:
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900138 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100139 dest_server['fip']['floating_ip_address'],
140 servers=[src_server, dest_server])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900141
142
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100143@ddt.ddt
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900144class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
145 base.BaseTempestTestCase):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900146
147 same_network = True
148
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000149 @decorators.idempotent_id('05c4e3b3-7319-4052-90ad-e8916436c23b')
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100150 @ddt.unpack
151 @ddt.data({'src_has_fip': True, 'dest_has_fip': True},
152 {'src_has_fip': True, 'dest_has_fip': False},
153 {'src_has_fip': False, 'dest_has_fip': True},
154 {'src_has_fip': True, 'dest_has_fip': False})
155 def test_east_west(self, src_has_fip, dest_has_fip):
156 self._test_east_west(src_has_fip=src_has_fip,
157 dest_has_fip=dest_has_fip)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900158
159
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100160@ddt.ddt
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900161class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
162 base.BaseTempestTestCase):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900163
164 same_network = False
165
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000166 @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100167 @ddt.unpack
168 @ddt.data({'src_has_fip': True, 'dest_has_fip': True},
169 {'src_has_fip': True, 'dest_has_fip': False},
170 {'src_has_fip': False, 'dest_has_fip': True},
171 {'src_has_fip': True, 'dest_has_fip': False})
172 def test_east_west(self, src_has_fip, dest_has_fip):
173 self._test_east_west(src_has_fip=src_has_fip,
174 dest_has_fip=dest_has_fip)
Itzik Browna31510f2018-01-19 11:09:48 +0200175
176
177class DefaultSnatToExternal(FloatingIpTestCasesMixin,
178 base.BaseTempestTestCase):
179 same_network = True
180
181 @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
182 def test_snat_external_ip(self):
183 """Check connectivity to an external IP"""
184 gateway_external_ip = self._get_external_gateway()
185
186 if not gateway_external_ip:
187 raise self.skipTest("IPv4 gateway is not configured for public "
188 "network or public_network_id is not "
189 "configured")
190 proxy = self._create_server()
191 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
192 CONF.validation.image_ssh_user,
193 pkey=self.keypair['private_key'])
194 src_server = self._create_server(create_floating_ip=False)
195 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
196 ssh_client = ssh.Client(src_server_ip,
197 CONF.validation.image_ssh_user,
198 pkey=self.keypair['private_key'],
199 proxy_client=proxy_client)
200 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100201 gateway_external_ip,
202 servers=[proxy, src_server])
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800203
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000204 def _test_nested_snat_external_ip(self, feature_enabled_bool):
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000205 """Check connectivity to an external IP from a nested network."""
206 gateway_external_ip = self._get_external_gateway()
207
208 if not gateway_external_ip:
209 raise self.skipTest("IPv4 gateway is not configured for public "
210 "network or public_network_id is not "
211 "configured")
212 proxy = self._create_server()
213 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
214 CONF.validation.image_ssh_user,
215 pkey=self.keypair['private_key'])
216
217 # Create a nested router
218 router = self.create_router(
219 router_name=data_utils.rand_name('router'),
220 admin_state_up=True)
221
222 # Attach outer subnet to it
223 outer_port = self.create_port(self.network)
224 self.client.add_router_interface_with_port_id(router['id'],
225 outer_port['id'])
226
227 # Attach a nested subnet to it
228 network = self.create_network()
229 subnet = self.create_subnet(network)
230 self.create_router_interface(router['id'], subnet['id'])
231
232 # Set up static routes in both directions
233 self.client.update_extra_routes(
234 self.router['id'],
235 outer_port['fixed_ips'][0]['ip_address'], subnet['cidr'])
236 self.client.update_extra_routes(
237 router['id'], self.subnet['gateway_ip'], '0.0.0.0/0')
238
239 # Create a server inside the nested network
240 src_server = self._create_server(create_floating_ip=False,
241 network=network)
242
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000243 # Check connectivity if nested SNAT is enabled, else no connectivity
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000244 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
245 ssh_client = ssh.Client(src_server_ip,
246 CONF.validation.image_ssh_user,
247 pkey=self.keypair['private_key'],
248 proxy_client=proxy_client)
249 self.check_remote_connectivity(ssh_client,
250 gateway_external_ip,
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000251 should_succeed=feature_enabled_bool,
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000252 servers=[proxy, src_server])
253
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000254 @decorators.idempotent_id('b911b124-b6cb-449d-83d9-b34f3665741d')
255 @utils.requires_ext(extension='extraroute', service='network')
256 def test_nested_snat_external_ip(self):
257 feature_enabled_bool = (
258 CONF.neutron_plugin_options.snat_rules_apply_to_nested_networks
259 )
260 self._test_nested_snat_external_ip(feature_enabled_bool)
261
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800262
Hongbin Lu965b03d2018-04-25 22:32:30 +0000263class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
264 base.BaseTempestTestCase):
265 same_network = True
266
267 @classmethod
268 @utils.requires_ext(extension="router", service="network")
269 @utils.requires_ext(extension="fip-port-details", service="network")
270 def resource_setup(cls):
271 super(FloatingIPPortDetailsTest, cls).resource_setup()
272
273 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
274 def test_floatingip_port_details(self):
275 """Tests the following:
276
277 1. Create a port with floating ip in Neutron.
278 2. Create two servers in Nova.
279 3. Attach the port to the server.
280 4. Detach the port from the server.
281 5. Attach the port to the second server.
282 6. Detach the port from the second server.
283 """
284 port = self.create_port(self.network)
285 fip = self.create_and_associate_floatingip(port['id'])
286 server1 = self._create_server(create_floating_ip=False)
287 server2 = self._create_server(create_floating_ip=False)
288
289 for server in [server1, server2]:
290 # attach the port to the server
291 self.create_interface(
292 server['server']['id'], port_id=port['id'])
293 waiters.wait_for_interface_status(
294 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600295 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000296 fip = self.client.show_floatingip(fip['id'])['floatingip']
Roman Safronov4d6ae072023-10-31 17:49:35 +0200297 server_data = self.os_admin.servers_client.show_server(
298 server['server']['id'])['server']
299 zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
Hongbin Lu965b03d2018-04-25 22:32:30 +0000300 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600301 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Roman Safronov4d6ae072023-10-31 17:49:35 +0200302 device_id=server['server']['id'],
303 device_owner=zone)
elajkat38d90512021-12-13 14:21:30 +0100304 LOG.debug('Port check for server %s and FIP %s finished, '
305 'lets detach port %s from server!',
306 server['server']['id'], fip['id'], port['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000307
308 # detach the port from the server; this is a cast in the compute
309 # API so we have to poll the port until the device_id is unset.
310 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600311 port = self._wait_for_port_detach(port['id'])
elajkat38d90512021-12-13 14:21:30 +0100312 LOG.debug('Port %s has been detached from server %s, lets check '
313 'the status of port in FIP %s details!',
314 port['id'], server['server']['id'], fip['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600315 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000316 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600317 fip, port, status=lib_constants.PORT_STATUS_DOWN,
318 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000319
320 def _check_port_details(self, fip, port, status, device_id, device_owner):
321 self.assertIn('port_details', fip)
322 port_details = fip['port_details']
323 self.assertEqual(port['name'], port_details['name'])
324 self.assertEqual(port['network_id'], port_details['network_id'])
325 self.assertEqual(port['mac_address'], port_details['mac_address'])
326 self.assertEqual(port['admin_state_up'],
327 port_details['admin_state_up'])
328 self.assertEqual(status, port_details['status'])
329 self.assertEqual(device_id, port_details['device_id'])
330 self.assertEqual(device_owner, port_details['device_owner'])
331
332 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
333 """Waits for the port's device_id to be unset.
334
335 :param port_id: The id of the port being detached.
336 :returns: The final port dict from the show_port response.
337 """
338 port = self.client.show_port(port_id)['port']
339 device_id = port['device_id']
340 start = int(time.time())
341
342 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
343 # None, but it's not contractual so handle Falsey either way.
344 while device_id:
345 time.sleep(interval)
346 port = self.client.show_port(port_id)['port']
347 device_id = port['device_id']
348
349 timed_out = int(time.time()) - start >= timeout
350
351 if device_id and timed_out:
352 message = ('Port %s failed to detach (device_id %s) within '
353 'the required time (%s s).' %
354 (port_id, device_id, timeout))
355 raise exceptions.TimeoutException(message)
356
357 return port
358
Brian Haleyaf347da2018-09-14 11:24:00 -0600359 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
360 """Waits for the fip's attached port status to be 'DOWN'.
361
362 :param fip_id: The id of the floating IP.
363 :returns: The final fip dict from the show_floatingip response.
364 """
365 fip = self.client.show_floatingip(fip_id)['floatingip']
366 self.assertIn('port_details', fip)
367 port_details = fip['port_details']
368 status = port_details['status']
369 start = int(time.time())
370
371 while status != lib_constants.PORT_STATUS_DOWN:
372 time.sleep(interval)
373 fip = self.client.show_floatingip(fip_id)['floatingip']
374 self.assertIn('port_details', fip)
375 port_details = fip['port_details']
376 status = port_details['status']
377
378 timed_out = int(time.time()) - start >= timeout
379
380 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100381 port_id = fip.get("port_id")
382 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600383 message = ('Floating IP %s attached port status failed to '
384 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100385 'the required time (%s s). Port details: %s' %
386 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600387 raise exceptions.TimeoutException(message)
388
elajkat38d90512021-12-13 14:21:30 +0100389 LOG.debug('Port %s attached to FIP %s is down after %s!',
390 fip.get("port_id"), fip_id, int(time.time()) - start)
Brian Haleyaf347da2018-09-14 11:24:00 -0600391 return fip
392
Hongbin Lu965b03d2018-04-25 22:32:30 +0000393
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800394class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900395 test_qos.QoSTestMixin,
396 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800397
398 same_network = True
399
400 @classmethod
401 @utils.requires_ext(extension="router", service="network")
402 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900403 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800404 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
405 def resource_setup(cls):
406 super(FloatingIPQosTest, cls).resource_setup()
407
Eduardo Olivares98772912021-02-19 12:36:21 +0100408 @classmethod
409 def setup_clients(cls):
410 super(FloatingIPQosTest, cls).setup_clients()
411 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300412 cls.qos_bw_limit_rule_client = \
413 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100414
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800415 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
416 def test_qos(self):
417 """Test floating IP is binding to a QoS policy with
418
419 ingress and egress bandwidth limit rules. And it applied correctly
420 by sending a file from the instance to the test node.
421 Then calculating the bandwidth every ~1 sec by the number of bits
422 received / elapsed time.
423 """
424
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200425 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
426
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800427 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100428
429 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800430 policy_id = self._create_qos_policy()
431 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100432
433 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300434 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
435 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
436 'direction': lib_constants.INGRESS_DIRECTION}
437 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
438 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800439
zahlabut7ebb66e2021-09-01 22:39:49 +0300440 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
441 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
442 'direction': lib_constants.EGRESS_DIRECTION}
443 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
444 qos_policy_id=policy_id, **rule_data)
445
446 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800447 policy_id)
448 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
449
450 fip = self.os_admin.network_client.get_floatingip(
451 self.fip['id'])['floatingip']
452 self.assertEqual(self.port['id'], fip['port_id'])
453
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100454 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800455 self.os_admin.network_client.update_floatingip(
456 self.fip['id'],
457 qos_policy_id=policy_id)
458
459 fip = self.os_admin.network_client.get_floatingip(
460 self.fip['id'])['floatingip']
461 self.assertEqual(policy_id, fip['qos_policy_id'])
462
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100463 # Basic test, Check that actual BW while downloading file
464 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800465 common_utils.wait_until_true(lambda: self._check_bw(
466 ssh_client,
467 self.fip['floating_ip_address'],
468 port=self.NC_PORT),
469 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100470 sleep=1,
471 exception=RuntimeError(
472 'Failed scenario: "Create a QoS policy associated with FIP" '
473 'Actual BW is not as expected!'))
474
475 # As admin user update QoS rules
476 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300477 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
478 policy_id, rule['id'],
479 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
480 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100481
482 # Check that actual BW while downloading file
483 # is as expected (Update BW)
484 common_utils.wait_until_true(lambda: self._check_bw(
485 ssh_client,
486 self.fip['floating_ip_address'],
487 port=self.NC_PORT,
488 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
489 timeout=120,
490 sleep=1,
491 exception=RuntimeError(
492 'Failed scenario: "Update QoS policy associated with FIP" '
493 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000494
495
496class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
497 base.BaseTempestTestCase):
498
499 same_network = None
500
501 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
502 def test_floating_ip_update(self):
503 """Test updating FIP with another port.
504
505 The test creates two servers and attaches floating ip to first server.
506 Then it checks server is accesible using the FIP. FIP is then
507 associated with the second server and connectivity is checked again.
508 """
509 ports = [self.create_port(
510 self.network, security_groups=[self.secgroup['id']])
511 for i in range(2)]
512
513 servers = []
514 for port in ports:
515 name = data_utils.rand_name("server-%s" % port['id'][:8])
516 server = self.create_server(
517 name=name,
518 flavor_ref=CONF.compute.flavor_ref,
519 key_name=self.keypair['name'],
520 image_ref=CONF.compute.image_ref,
521 networks=[{'port': port['id']}])['server']
522 server['name'] = name
523 servers.append(server)
524 for server in servers:
525 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200526 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000527
528 self.fip = self.create_floatingip(port=ports[0])
529 self.check_connectivity(self.fip['floating_ip_address'],
530 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100531 self.keypair['private_key'],
532 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000533 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
534
535 def _wait_for_fip_associated():
536 try:
537 self.check_servers_hostnames(servers[-1:], log_errors=False)
538 except (AssertionError, exceptions.SSHTimeout):
539 return False
540 return True
541
542 # The FIP is now associated with the port of the second server.
543 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200544 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000545 except common_utils.WaitTimeout:
546 self._log_console_output(servers[-1:])
547 self.fail(
548 "Server %s is not accessible via its floating ip %s" % (
549 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300550
551
552class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
553 credentials = ['primary', 'admin']
554
555 @classmethod
556 @utils.requires_ext(extension="router", service="network")
557 def skip_checks(cls):
558 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
559
560 def _create_keypair_and_secgroup(self):
561 self.keypair = self.create_keypair()
562 self.secgroup = self.create_security_group()
563 self.create_loginable_secgroup_rule(
564 secgroup_id=self.secgroup['id'])
565 self.create_pingable_secgroup_rule(
566 secgroup_id=self.secgroup['id'])
567
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000568 def _delete_floating_ip(self, fip_address):
569 ip_address = fip_address['floating_ip_address']
570
571 def _fip_is_free():
572 fips = self.os_admin.network_client.list_floatingips()
573 for fip in fips['floatingips']:
574 if ip_address == fip['floating_ip_address']:
575 return False
576 return True
577
578 self.delete_floatingip(fip_address)
579 try:
580 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
581 except common_utils.WaitTimeout:
582 self.fail("Can't reuse IP address %s because it is not free" %
583 ip_address)
584
585 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
586 delete_fip_ids=None):
587 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300588 if fip_addresses:
589 self.assertEqual(servers_num, len(fip_addresses),
590 ('Number of specified fip addresses '
591 'does not match the number of servers'))
592 network = self.create_network()
593 subnet = self.create_subnet(network)
594 router = self.create_router_by_client()
595 self.create_router_interface(router['id'], subnet['id'])
596
597 fips = []
598 for server in range(servers_num):
599 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000600 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300601 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000602 self._create_server_and_fip(network=network,
603 fip_address=fip,
604 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300605 return fips
606
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000607 def _create_server_and_fip(self, network, fip_address=None,
608 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300609 server = self.create_server(
610 flavor_ref=CONF.compute.flavor_ref,
611 image_ref=CONF.compute.image_ref,
612 key_name=self.keypair['name'],
613 networks=[{'uuid': network['id']}],
614 security_groups=[{'name': self.secgroup['name']}])
615 waiters.wait_for_server_status(self.os_primary.servers_client,
616 server['server']['id'],
617 constants.SERVER_STATUS_ACTIVE)
618 port = self.client.list_ports(
619 network_id=network['id'],
620 device_id=server['server']['id'])['ports'][0]
621
622 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000623 if delete_fip_address:
624 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300625 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000626 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300627 client=self.os_admin.network_client,
628 port=port)
629 self.addCleanup(
630 self.delete_floatingip, fip, self.os_admin.network_client)
631 else:
632 fip = self.create_floatingip(port=port)
633 return fip
634
635 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
636 for fip in [mutable_fip, permanent_fip]:
637 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
638 CONF.validation.image_ssh_user,
639 pkey=self.keypair['private_key'])
640 self.check_remote_connectivity(
641 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
642 self.check_remote_connectivity(
643 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
644
645 @testtools.skipUnless(CONF.network.public_network_id,
646 'The public_network_id option must be specified.')
647 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
648 def test_reuse_ip_address_with_other_fip_on_other_router(self):
649 """Reuse IP address by another floating IP on another router
650
651 Scenario:
652 1. Create and connect a router to the external network.
653 2. Create and connect an internal network to the router.
654 3. Create and connect 2 VMs to the internal network.
655 4. Create FIPs in the external network for the VMs.
656 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000657 6. Create and connect one more router to the external network.
658 7. Create and connect an internal network to the second router.
659 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300660 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000661 9. Delete VM2 FIP but save IP address that it used. The FIP is
662 deleted just before the creation of the new IP to "reserve" the
663 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300664 10. Create a FIP for the VM3 in the external network with
Elod Illesf2e985e2023-11-06 19:30:29 +0100665 the same IP address that was used for VM2.
Roman Safronov29c2dff2019-04-02 22:01:23 +0300666 11. Make sure that now VM1 is able to reach VM3 using the FIP.
667
668 Note, the scenario passes only in case corresponding
669 ARP update was sent to the external network when reusing same IP
670 address for another FIP.
671 """
672
673 self._create_keypair_and_secgroup()
674 [mutable_fip, permanent_fip] = (
675 self._create_network_and_servers(servers_num=2))
676 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300677 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000678 servers_num=1, fip_addresses=[mutable_fip],
679 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300680 self._check_fips_connectivity(mutable_fip, permanent_fip)