blob: 24e58203f110364322397036bd83c1e6c0a58036 [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
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +000021from neutron_lib.utils import test
elajkat38d90512021-12-13 14:21:30 +010022from oslo_log import log
Chandan Kumarc125fd12017-11-15 19:41:01 +053023from tempest.common import utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090024from tempest.common import waiters
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090025from tempest.lib.common.utils import data_utils
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +000026from tempest.lib import decorators
Slawek Kaplonski168e5012018-10-04 14:31:19 +020027from tempest.lib import exceptions
Roman Safronov29c2dff2019-04-02 22:01:23 +030028import testtools
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090029
LIU Yulong5ba88ef2017-12-22 10:50:15 +080030from neutron_tempest_plugin.api import base as base_api
Chandan Kumar667d3d32017-09-22 12:24:06 +053031from neutron_tempest_plugin.common import ssh
Brian Haleyba800452017-12-14 10:30:48 -050032from neutron_tempest_plugin.common import utils as common_utils
Chandan Kumar667d3d32017-09-22 12:24:06 +053033from neutron_tempest_plugin import config
34from neutron_tempest_plugin.scenario import base
35from neutron_tempest_plugin.scenario import constants
LIU Yulong5ba88ef2017-12-22 10:50:15 +080036from neutron_tempest_plugin.scenario import test_qos
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090037
38
39CONF = config.CONF
elajkat38d90512021-12-13 14:21:30 +010040LOG = log.getLogger(__name__)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090041
42
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090043class FloatingIpTestCasesMixin(object):
44 credentials = ['primary', 'admin']
45
46 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +053047 @utils.requires_ext(extension="router", service="network")
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090048 def resource_setup(cls):
49 super(FloatingIpTestCasesMixin, cls).resource_setup()
50 cls.network = cls.create_network()
51 cls.subnet = cls.create_subnet(cls.network)
52 cls.router = cls.create_router_by_client()
53 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
54 cls.keypair = cls.create_keypair()
55
rajat294495c042017-06-28 15:37:16 +053056 cls.secgroup = cls.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +053057 name=data_utils.rand_name('secgroup'))['security_group']
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090058 cls.security_groups.append(cls.secgroup)
59 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
60 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
61
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090062 if cls.same_network:
63 cls._dest_network = cls.network
64 else:
65 cls._dest_network = cls._create_dest_network()
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090066
67 @classmethod
Itzik Browna31510f2018-01-19 11:09:48 +020068 def _get_external_gateway(cls):
69 if CONF.network.public_network_id:
70 subnets = cls.os_admin.network_client.list_subnets(
71 network_id=CONF.network.public_network_id)
72
73 for subnet in subnets['subnets']:
Brian Haley33ef4602018-04-26 14:37:49 -040074 if (subnet['gateway_ip'] and
75 subnet['ip_version'] == lib_constants.IP_VERSION_4):
Itzik Browna31510f2018-01-19 11:09:48 +020076 return subnet['gateway_ip']
77
78 @classmethod
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090079 def _create_dest_network(cls):
80 network = cls.create_network()
Federico Ressi0ddc93b2018-04-09 12:01:48 +020081 subnet = cls.create_subnet(network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090082 cls.create_router_interface(cls.router['id'], subnet['id'])
83 return network
84
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000085 def _create_server(self, create_floating_ip=True, network=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090086 if network is None:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000087 network = self.network
88 port = self.create_port(network, security_groups=[self.secgroup['id']])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090089 if create_floating_ip:
Federico Ressi3dfa94c2018-07-06 09:46:39 +020090 fip = self.create_floatingip(port=port)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090091 else:
92 fip = None
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000093 server = self.create_server(
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090094 flavor_ref=CONF.compute.flavor_ref,
95 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000096 key_name=self.keypair['name'],
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090097 networks=[{'port': port['id']}])['server']
rajat294495c042017-06-28 15:37:16 +053098 waiters.wait_for_server_status(self.os_primary.servers_client,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090099 server['id'],
100 constants.SERVER_STATUS_ACTIVE)
101 return {'port': port, 'fip': fip, 'server': server}
102
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100103 def _test_east_west(self, src_has_fip, dest_has_fip):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900104 # The proxy VM is used to control the source VM when it doesn't
105 # have a floating-ip.
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100106 if src_has_fip:
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900107 proxy = None
108 proxy_client = None
109 else:
110 proxy = self._create_server()
111 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
112 CONF.validation.image_ssh_user,
113 pkey=self.keypair['private_key'])
114
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900115 # Source VM
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100116 if src_has_fip:
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900117 src_server = self._create_server()
118 src_server_ip = src_server['fip']['floating_ip_address']
119 else:
120 src_server = self._create_server(create_floating_ip=False)
121 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
122 ssh_client = ssh.Client(src_server_ip,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900123 CONF.validation.image_ssh_user,
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900124 pkey=self.keypair['private_key'],
125 proxy_client=proxy_client)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900126
127 # Destination VM
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100128 if dest_has_fip:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000129 dest_server = self._create_server(network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900130 else:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000131 dest_server = self._create_server(create_floating_ip=False,
132 network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900133
134 # Check connectivity
135 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100136 dest_server['port']['fixed_ips'][0]['ip_address'],
137 servers=[src_server, dest_server])
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100138 if dest_has_fip:
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900139 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100140 dest_server['fip']['floating_ip_address'],
141 servers=[src_server, dest_server])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900142
143
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100144@ddt.ddt
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900145class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
146 base.BaseTempestTestCase):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900147
148 same_network = True
149
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +0000150 @test.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000151 @decorators.idempotent_id('05c4e3b3-7319-4052-90ad-e8916436c23b')
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100152 @ddt.unpack
153 @ddt.data({'src_has_fip': True, 'dest_has_fip': True},
154 {'src_has_fip': True, 'dest_has_fip': False},
155 {'src_has_fip': False, 'dest_has_fip': True},
156 {'src_has_fip': True, 'dest_has_fip': False})
157 def test_east_west(self, src_has_fip, dest_has_fip):
158 self._test_east_west(src_has_fip=src_has_fip,
159 dest_has_fip=dest_has_fip)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900160
161
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100162@ddt.ddt
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900163class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
164 base.BaseTempestTestCase):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900165
166 same_network = False
167
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +0000168 @test.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000169 @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100170 @ddt.unpack
171 @ddt.data({'src_has_fip': True, 'dest_has_fip': True},
172 {'src_has_fip': True, 'dest_has_fip': False},
173 {'src_has_fip': False, 'dest_has_fip': True},
174 {'src_has_fip': True, 'dest_has_fip': False})
175 def test_east_west(self, src_has_fip, dest_has_fip):
176 self._test_east_west(src_has_fip=src_has_fip,
177 dest_has_fip=dest_has_fip)
Itzik Browna31510f2018-01-19 11:09:48 +0200178
179
180class DefaultSnatToExternal(FloatingIpTestCasesMixin,
181 base.BaseTempestTestCase):
182 same_network = True
183
184 @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
185 def test_snat_external_ip(self):
186 """Check connectivity to an external IP"""
187 gateway_external_ip = self._get_external_gateway()
188
189 if not gateway_external_ip:
190 raise self.skipTest("IPv4 gateway is not configured for public "
191 "network or public_network_id is not "
192 "configured")
193 proxy = self._create_server()
194 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
195 CONF.validation.image_ssh_user,
196 pkey=self.keypair['private_key'])
197 src_server = self._create_server(create_floating_ip=False)
198 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
199 ssh_client = ssh.Client(src_server_ip,
200 CONF.validation.image_ssh_user,
201 pkey=self.keypair['private_key'],
202 proxy_client=proxy_client)
203 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100204 gateway_external_ip,
205 servers=[proxy, src_server])
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800206
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000207 @decorators.idempotent_id('b911b124-b6cb-449d-83d9-b34f3665741d')
208 @utils.requires_ext(extension='extraroute', service='network')
209 @testtools.skipUnless(
210 CONF.neutron_plugin_options.snat_rules_apply_to_nested_networks,
211 "Backend doesn't enable nested SNAT.")
212 def test_nested_snat_external_ip(self):
213 """Check connectivity to an external IP from a nested network."""
214 gateway_external_ip = self._get_external_gateway()
215
216 if not gateway_external_ip:
217 raise self.skipTest("IPv4 gateway is not configured for public "
218 "network or public_network_id is not "
219 "configured")
220 proxy = self._create_server()
221 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
222 CONF.validation.image_ssh_user,
223 pkey=self.keypair['private_key'])
224
225 # Create a nested router
226 router = self.create_router(
227 router_name=data_utils.rand_name('router'),
228 admin_state_up=True)
229
230 # Attach outer subnet to it
231 outer_port = self.create_port(self.network)
232 self.client.add_router_interface_with_port_id(router['id'],
233 outer_port['id'])
234
235 # Attach a nested subnet to it
236 network = self.create_network()
237 subnet = self.create_subnet(network)
238 self.create_router_interface(router['id'], subnet['id'])
239
240 # Set up static routes in both directions
241 self.client.update_extra_routes(
242 self.router['id'],
243 outer_port['fixed_ips'][0]['ip_address'], subnet['cidr'])
244 self.client.update_extra_routes(
245 router['id'], self.subnet['gateway_ip'], '0.0.0.0/0')
246
247 # Create a server inside the nested network
248 src_server = self._create_server(create_floating_ip=False,
249 network=network)
250
251 # Validate that it can access external gw ip (via nested snat)
252 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
253 ssh_client = ssh.Client(src_server_ip,
254 CONF.validation.image_ssh_user,
255 pkey=self.keypair['private_key'],
256 proxy_client=proxy_client)
257 self.check_remote_connectivity(ssh_client,
258 gateway_external_ip,
259 servers=[proxy, src_server])
260
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800261
Hongbin Lu965b03d2018-04-25 22:32:30 +0000262class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
263 base.BaseTempestTestCase):
264 same_network = True
265
266 @classmethod
267 @utils.requires_ext(extension="router", service="network")
268 @utils.requires_ext(extension="fip-port-details", service="network")
269 def resource_setup(cls):
270 super(FloatingIPPortDetailsTest, cls).resource_setup()
271
272 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
273 def test_floatingip_port_details(self):
274 """Tests the following:
275
276 1. Create a port with floating ip in Neutron.
277 2. Create two servers in Nova.
278 3. Attach the port to the server.
279 4. Detach the port from the server.
280 5. Attach the port to the second server.
281 6. Detach the port from the second server.
282 """
283 port = self.create_port(self.network)
284 fip = self.create_and_associate_floatingip(port['id'])
285 server1 = self._create_server(create_floating_ip=False)
286 server2 = self._create_server(create_floating_ip=False)
287
288 for server in [server1, server2]:
289 # attach the port to the server
290 self.create_interface(
291 server['server']['id'], port_id=port['id'])
292 waiters.wait_for_interface_status(
293 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600294 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000295 fip = self.client.show_floatingip(fip['id'])['floatingip']
Roman Safronov4d6ae072023-10-31 17:49:35 +0200296 server_data = self.os_admin.servers_client.show_server(
297 server['server']['id'])['server']
298 zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
Hongbin Lu965b03d2018-04-25 22:32:30 +0000299 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600300 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Roman Safronov4d6ae072023-10-31 17:49:35 +0200301 device_id=server['server']['id'],
302 device_owner=zone)
elajkat38d90512021-12-13 14:21:30 +0100303 LOG.debug('Port check for server %s and FIP %s finished, '
304 'lets detach port %s from server!',
305 server['server']['id'], fip['id'], port['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000306
307 # detach the port from the server; this is a cast in the compute
308 # API so we have to poll the port until the device_id is unset.
309 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600310 port = self._wait_for_port_detach(port['id'])
elajkat38d90512021-12-13 14:21:30 +0100311 LOG.debug('Port %s has been detached from server %s, lets check '
312 'the status of port in FIP %s details!',
313 port['id'], server['server']['id'], fip['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600314 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000315 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600316 fip, port, status=lib_constants.PORT_STATUS_DOWN,
317 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000318
319 def _check_port_details(self, fip, port, status, device_id, device_owner):
320 self.assertIn('port_details', fip)
321 port_details = fip['port_details']
322 self.assertEqual(port['name'], port_details['name'])
323 self.assertEqual(port['network_id'], port_details['network_id'])
324 self.assertEqual(port['mac_address'], port_details['mac_address'])
325 self.assertEqual(port['admin_state_up'],
326 port_details['admin_state_up'])
327 self.assertEqual(status, port_details['status'])
328 self.assertEqual(device_id, port_details['device_id'])
329 self.assertEqual(device_owner, port_details['device_owner'])
330
331 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
332 """Waits for the port's device_id to be unset.
333
334 :param port_id: The id of the port being detached.
335 :returns: The final port dict from the show_port response.
336 """
337 port = self.client.show_port(port_id)['port']
338 device_id = port['device_id']
339 start = int(time.time())
340
341 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
342 # None, but it's not contractual so handle Falsey either way.
343 while device_id:
344 time.sleep(interval)
345 port = self.client.show_port(port_id)['port']
346 device_id = port['device_id']
347
348 timed_out = int(time.time()) - start >= timeout
349
350 if device_id and timed_out:
351 message = ('Port %s failed to detach (device_id %s) within '
352 'the required time (%s s).' %
353 (port_id, device_id, timeout))
354 raise exceptions.TimeoutException(message)
355
356 return port
357
Brian Haleyaf347da2018-09-14 11:24:00 -0600358 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
359 """Waits for the fip's attached port status to be 'DOWN'.
360
361 :param fip_id: The id of the floating IP.
362 :returns: The final fip dict from the show_floatingip response.
363 """
364 fip = self.client.show_floatingip(fip_id)['floatingip']
365 self.assertIn('port_details', fip)
366 port_details = fip['port_details']
367 status = port_details['status']
368 start = int(time.time())
369
370 while status != lib_constants.PORT_STATUS_DOWN:
371 time.sleep(interval)
372 fip = self.client.show_floatingip(fip_id)['floatingip']
373 self.assertIn('port_details', fip)
374 port_details = fip['port_details']
375 status = port_details['status']
376
377 timed_out = int(time.time()) - start >= timeout
378
379 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100380 port_id = fip.get("port_id")
381 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600382 message = ('Floating IP %s attached port status failed to '
383 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100384 'the required time (%s s). Port details: %s' %
385 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600386 raise exceptions.TimeoutException(message)
387
elajkat38d90512021-12-13 14:21:30 +0100388 LOG.debug('Port %s attached to FIP %s is down after %s!',
389 fip.get("port_id"), fip_id, int(time.time()) - start)
Brian Haleyaf347da2018-09-14 11:24:00 -0600390 return fip
391
Hongbin Lu965b03d2018-04-25 22:32:30 +0000392
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800393class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900394 test_qos.QoSTestMixin,
395 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800396
397 same_network = True
398
399 @classmethod
400 @utils.requires_ext(extension="router", service="network")
401 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900402 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800403 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
404 def resource_setup(cls):
405 super(FloatingIPQosTest, cls).resource_setup()
406
Eduardo Olivares98772912021-02-19 12:36:21 +0100407 @classmethod
408 def setup_clients(cls):
409 super(FloatingIPQosTest, cls).setup_clients()
410 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300411 cls.qos_bw_limit_rule_client = \
412 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100413
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800414 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
415 def test_qos(self):
416 """Test floating IP is binding to a QoS policy with
417
418 ingress and egress bandwidth limit rules. And it applied correctly
419 by sending a file from the instance to the test node.
420 Then calculating the bandwidth every ~1 sec by the number of bits
421 received / elapsed time.
422 """
423
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200424 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
425
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800426 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100427
428 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800429 policy_id = self._create_qos_policy()
430 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100431
432 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300433 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
434 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
435 'direction': lib_constants.INGRESS_DIRECTION}
436 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
437 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800438
zahlabut7ebb66e2021-09-01 22:39:49 +0300439 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
440 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
441 'direction': lib_constants.EGRESS_DIRECTION}
442 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
443 qos_policy_id=policy_id, **rule_data)
444
445 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800446 policy_id)
447 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
448
449 fip = self.os_admin.network_client.get_floatingip(
450 self.fip['id'])['floatingip']
451 self.assertEqual(self.port['id'], fip['port_id'])
452
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100453 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800454 self.os_admin.network_client.update_floatingip(
455 self.fip['id'],
456 qos_policy_id=policy_id)
457
458 fip = self.os_admin.network_client.get_floatingip(
459 self.fip['id'])['floatingip']
460 self.assertEqual(policy_id, fip['qos_policy_id'])
461
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100462 # Basic test, Check that actual BW while downloading file
463 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800464 common_utils.wait_until_true(lambda: self._check_bw(
465 ssh_client,
466 self.fip['floating_ip_address'],
467 port=self.NC_PORT),
468 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100469 sleep=1,
470 exception=RuntimeError(
471 'Failed scenario: "Create a QoS policy associated with FIP" '
472 'Actual BW is not as expected!'))
473
474 # As admin user update QoS rules
475 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300476 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
477 policy_id, rule['id'],
478 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
479 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100480
481 # Check that actual BW while downloading file
482 # is as expected (Update BW)
483 common_utils.wait_until_true(lambda: self._check_bw(
484 ssh_client,
485 self.fip['floating_ip_address'],
486 port=self.NC_PORT,
487 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
488 timeout=120,
489 sleep=1,
490 exception=RuntimeError(
491 'Failed scenario: "Update QoS policy associated with FIP" '
492 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000493
494
495class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
496 base.BaseTempestTestCase):
497
498 same_network = None
499
500 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
501 def test_floating_ip_update(self):
502 """Test updating FIP with another port.
503
504 The test creates two servers and attaches floating ip to first server.
505 Then it checks server is accesible using the FIP. FIP is then
506 associated with the second server and connectivity is checked again.
507 """
508 ports = [self.create_port(
509 self.network, security_groups=[self.secgroup['id']])
510 for i in range(2)]
511
512 servers = []
513 for port in ports:
514 name = data_utils.rand_name("server-%s" % port['id'][:8])
515 server = self.create_server(
516 name=name,
517 flavor_ref=CONF.compute.flavor_ref,
518 key_name=self.keypair['name'],
519 image_ref=CONF.compute.image_ref,
520 networks=[{'port': port['id']}])['server']
521 server['name'] = name
522 servers.append(server)
523 for server in servers:
524 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200525 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000526
527 self.fip = self.create_floatingip(port=ports[0])
528 self.check_connectivity(self.fip['floating_ip_address'],
529 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100530 self.keypair['private_key'],
531 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000532 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
533
534 def _wait_for_fip_associated():
535 try:
536 self.check_servers_hostnames(servers[-1:], log_errors=False)
537 except (AssertionError, exceptions.SSHTimeout):
538 return False
539 return True
540
541 # The FIP is now associated with the port of the second server.
542 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200543 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000544 except common_utils.WaitTimeout:
545 self._log_console_output(servers[-1:])
546 self.fail(
547 "Server %s is not accessible via its floating ip %s" % (
548 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300549
550
551class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
552 credentials = ['primary', 'admin']
553
554 @classmethod
555 @utils.requires_ext(extension="router", service="network")
556 def skip_checks(cls):
557 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
558
559 def _create_keypair_and_secgroup(self):
560 self.keypair = self.create_keypair()
561 self.secgroup = self.create_security_group()
562 self.create_loginable_secgroup_rule(
563 secgroup_id=self.secgroup['id'])
564 self.create_pingable_secgroup_rule(
565 secgroup_id=self.secgroup['id'])
566
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000567 def _delete_floating_ip(self, fip_address):
568 ip_address = fip_address['floating_ip_address']
569
570 def _fip_is_free():
571 fips = self.os_admin.network_client.list_floatingips()
572 for fip in fips['floatingips']:
573 if ip_address == fip['floating_ip_address']:
574 return False
575 return True
576
577 self.delete_floatingip(fip_address)
578 try:
579 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
580 except common_utils.WaitTimeout:
581 self.fail("Can't reuse IP address %s because it is not free" %
582 ip_address)
583
584 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
585 delete_fip_ids=None):
586 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300587 if fip_addresses:
588 self.assertEqual(servers_num, len(fip_addresses),
589 ('Number of specified fip addresses '
590 'does not match the number of servers'))
591 network = self.create_network()
592 subnet = self.create_subnet(network)
593 router = self.create_router_by_client()
594 self.create_router_interface(router['id'], subnet['id'])
595
596 fips = []
597 for server in range(servers_num):
598 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000599 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300600 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000601 self._create_server_and_fip(network=network,
602 fip_address=fip,
603 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300604 return fips
605
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000606 def _create_server_and_fip(self, network, fip_address=None,
607 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300608 server = self.create_server(
609 flavor_ref=CONF.compute.flavor_ref,
610 image_ref=CONF.compute.image_ref,
611 key_name=self.keypair['name'],
612 networks=[{'uuid': network['id']}],
613 security_groups=[{'name': self.secgroup['name']}])
614 waiters.wait_for_server_status(self.os_primary.servers_client,
615 server['server']['id'],
616 constants.SERVER_STATUS_ACTIVE)
617 port = self.client.list_ports(
618 network_id=network['id'],
619 device_id=server['server']['id'])['ports'][0]
620
621 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000622 if delete_fip_address:
623 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300624 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000625 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300626 client=self.os_admin.network_client,
627 port=port)
628 self.addCleanup(
629 self.delete_floatingip, fip, self.os_admin.network_client)
630 else:
631 fip = self.create_floatingip(port=port)
632 return fip
633
634 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
635 for fip in [mutable_fip, permanent_fip]:
636 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
637 CONF.validation.image_ssh_user,
638 pkey=self.keypair['private_key'])
639 self.check_remote_connectivity(
640 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
641 self.check_remote_connectivity(
642 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
643
644 @testtools.skipUnless(CONF.network.public_network_id,
645 'The public_network_id option must be specified.')
646 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
647 def test_reuse_ip_address_with_other_fip_on_other_router(self):
648 """Reuse IP address by another floating IP on another router
649
650 Scenario:
651 1. Create and connect a router to the external network.
652 2. Create and connect an internal network to the router.
653 3. Create and connect 2 VMs to the internal network.
654 4. Create FIPs in the external network for the VMs.
655 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000656 6. Create and connect one more router to the external network.
657 7. Create and connect an internal network to the second router.
658 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300659 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000660 9. Delete VM2 FIP but save IP address that it used. The FIP is
661 deleted just before the creation of the new IP to "reserve" the
662 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300663 10. Create a FIP for the VM3 in the external network with
Elod Illesf2e985e2023-11-06 19:30:29 +0100664 the same IP address that was used for VM2.
Roman Safronov29c2dff2019-04-02 22:01:23 +0300665 11. Make sure that now VM1 is able to reach VM3 using the FIP.
666
667 Note, the scenario passes only in case corresponding
668 ARP update was sent to the external network when reusing same IP
669 address for another FIP.
670 """
671
672 self._create_keypair_and_secgroup()
673 [mutable_fip, permanent_fip] = (
674 self._create_network_and_servers(servers_num=2))
675 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300676 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000677 servers_num=1, fip_addresses=[mutable_fip],
678 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300679 self._check_fips_connectivity(mutable_fip, permanent_fip)