blob: ca07e28e792bf615f50bacd81ff3e4cb137bd87b [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']])
dbiletskiyf656efa2025-12-09 15:31:00 +010088 self.addCleanup(self.client.delete_port, port['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
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000150 @decorators.idempotent_id('05c4e3b3-7319-4052-90ad-e8916436c23b')
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100151 @ddt.unpack
152 @ddt.data({'src_has_fip': True, 'dest_has_fip': True},
153 {'src_has_fip': True, 'dest_has_fip': False},
154 {'src_has_fip': False, 'dest_has_fip': True},
155 {'src_has_fip': True, 'dest_has_fip': False})
156 def test_east_west(self, src_has_fip, dest_has_fip):
157 self._test_east_west(src_has_fip=src_has_fip,
158 dest_has_fip=dest_has_fip)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900159
160
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100161@ddt.ddt
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900162class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
163 base.BaseTempestTestCase):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900164
165 same_network = False
166
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000167 @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
Slawek Kaplonskiec162e02024-03-06 12:21:24 +0100168 @ddt.unpack
169 @ddt.data({'src_has_fip': True, 'dest_has_fip': True},
170 {'src_has_fip': True, 'dest_has_fip': False},
171 {'src_has_fip': False, 'dest_has_fip': True},
172 {'src_has_fip': True, 'dest_has_fip': False})
173 def test_east_west(self, src_has_fip, dest_has_fip):
174 self._test_east_west(src_has_fip=src_has_fip,
175 dest_has_fip=dest_has_fip)
Itzik Browna31510f2018-01-19 11:09:48 +0200176
177
178class DefaultSnatToExternal(FloatingIpTestCasesMixin,
179 base.BaseTempestTestCase):
180 same_network = True
181
182 @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
183 def test_snat_external_ip(self):
184 """Check connectivity to an external IP"""
185 gateway_external_ip = self._get_external_gateway()
186
187 if not gateway_external_ip:
188 raise self.skipTest("IPv4 gateway is not configured for public "
189 "network or public_network_id is not "
190 "configured")
191 proxy = self._create_server()
192 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
193 CONF.validation.image_ssh_user,
194 pkey=self.keypair['private_key'])
195 src_server = self._create_server(create_floating_ip=False)
196 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
197 ssh_client = ssh.Client(src_server_ip,
198 CONF.validation.image_ssh_user,
199 pkey=self.keypair['private_key'],
200 proxy_client=proxy_client)
201 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100202 gateway_external_ip,
203 servers=[proxy, src_server])
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800204
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000205 def _test_nested_snat_external_ip(self, feature_enabled_bool):
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000206 """Check connectivity to an external IP from a nested network."""
207 gateway_external_ip = self._get_external_gateway()
208
209 if not gateway_external_ip:
210 raise self.skipTest("IPv4 gateway is not configured for public "
211 "network or public_network_id is not "
212 "configured")
213 proxy = self._create_server()
214 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
215 CONF.validation.image_ssh_user,
216 pkey=self.keypair['private_key'])
217
218 # Create a nested router
219 router = self.create_router(
220 router_name=data_utils.rand_name('router'),
221 admin_state_up=True)
222
223 # Attach outer subnet to it
224 outer_port = self.create_port(self.network)
225 self.client.add_router_interface_with_port_id(router['id'],
226 outer_port['id'])
227
228 # Attach a nested subnet to it
229 network = self.create_network()
230 subnet = self.create_subnet(network)
231 self.create_router_interface(router['id'], subnet['id'])
232
233 # Set up static routes in both directions
234 self.client.update_extra_routes(
235 self.router['id'],
236 outer_port['fixed_ips'][0]['ip_address'], subnet['cidr'])
237 self.client.update_extra_routes(
238 router['id'], self.subnet['gateway_ip'], '0.0.0.0/0')
239
240 # Create a server inside the nested network
241 src_server = self._create_server(create_floating_ip=False,
242 network=network)
243
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000244 # Check connectivity if nested SNAT is enabled, else no connectivity
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000245 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
246 ssh_client = ssh.Client(src_server_ip,
247 CONF.validation.image_ssh_user,
248 pkey=self.keypair['private_key'],
249 proxy_client=proxy_client)
250 self.check_remote_connectivity(ssh_client,
251 gateway_external_ip,
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000252 should_succeed=feature_enabled_bool,
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000253 servers=[proxy, src_server])
254
Renjing Xiao6ee007c2025-02-06 18:09:25 +0000255 @decorators.idempotent_id('b911b124-b6cb-449d-83d9-b34f3665741d')
256 @utils.requires_ext(extension='extraroute', service='network')
257 def test_nested_snat_external_ip(self):
258 feature_enabled_bool = (
259 CONF.neutron_plugin_options.snat_rules_apply_to_nested_networks
260 )
261 self._test_nested_snat_external_ip(feature_enabled_bool)
262
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800263
Hongbin Lu965b03d2018-04-25 22:32:30 +0000264class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
265 base.BaseTempestTestCase):
266 same_network = True
267
268 @classmethod
269 @utils.requires_ext(extension="router", service="network")
270 @utils.requires_ext(extension="fip-port-details", service="network")
271 def resource_setup(cls):
272 super(FloatingIPPortDetailsTest, cls).resource_setup()
273
274 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
275 def test_floatingip_port_details(self):
276 """Tests the following:
277
278 1. Create a port with floating ip in Neutron.
279 2. Create two servers in Nova.
280 3. Attach the port to the server.
281 4. Detach the port from the server.
282 5. Attach the port to the second server.
283 6. Detach the port from the second server.
284 """
285 port = self.create_port(self.network)
286 fip = self.create_and_associate_floatingip(port['id'])
287 server1 = self._create_server(create_floating_ip=False)
288 server2 = self._create_server(create_floating_ip=False)
289
290 for server in [server1, server2]:
291 # attach the port to the server
292 self.create_interface(
293 server['server']['id'], port_id=port['id'])
294 waiters.wait_for_interface_status(
295 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600296 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000297 fip = self.client.show_floatingip(fip['id'])['floatingip']
Roman Safronov4d6ae072023-10-31 17:49:35 +0200298 server_data = self.os_admin.servers_client.show_server(
299 server['server']['id'])['server']
300 zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
Hongbin Lu965b03d2018-04-25 22:32:30 +0000301 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600302 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Roman Safronov4d6ae072023-10-31 17:49:35 +0200303 device_id=server['server']['id'],
304 device_owner=zone)
elajkat38d90512021-12-13 14:21:30 +0100305 LOG.debug('Port check for server %s and FIP %s finished, '
306 'lets detach port %s from server!',
307 server['server']['id'], fip['id'], port['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000308
309 # detach the port from the server; this is a cast in the compute
310 # API so we have to poll the port until the device_id is unset.
311 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600312 port = self._wait_for_port_detach(port['id'])
elajkat38d90512021-12-13 14:21:30 +0100313 LOG.debug('Port %s has been detached from server %s, lets check '
314 'the status of port in FIP %s details!',
315 port['id'], server['server']['id'], fip['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600316 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000317 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600318 fip, port, status=lib_constants.PORT_STATUS_DOWN,
319 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000320
321 def _check_port_details(self, fip, port, status, device_id, device_owner):
322 self.assertIn('port_details', fip)
323 port_details = fip['port_details']
324 self.assertEqual(port['name'], port_details['name'])
325 self.assertEqual(port['network_id'], port_details['network_id'])
326 self.assertEqual(port['mac_address'], port_details['mac_address'])
327 self.assertEqual(port['admin_state_up'],
328 port_details['admin_state_up'])
329 self.assertEqual(status, port_details['status'])
330 self.assertEqual(device_id, port_details['device_id'])
331 self.assertEqual(device_owner, port_details['device_owner'])
332
333 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
334 """Waits for the port's device_id to be unset.
335
336 :param port_id: The id of the port being detached.
337 :returns: The final port dict from the show_port response.
338 """
339 port = self.client.show_port(port_id)['port']
340 device_id = port['device_id']
341 start = int(time.time())
342
343 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
344 # None, but it's not contractual so handle Falsey either way.
345 while device_id:
346 time.sleep(interval)
347 port = self.client.show_port(port_id)['port']
348 device_id = port['device_id']
349
350 timed_out = int(time.time()) - start >= timeout
351
352 if device_id and timed_out:
353 message = ('Port %s failed to detach (device_id %s) within '
354 'the required time (%s s).' %
355 (port_id, device_id, timeout))
356 raise exceptions.TimeoutException(message)
357
358 return port
359
Brian Haleyaf347da2018-09-14 11:24:00 -0600360 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
361 """Waits for the fip's attached port status to be 'DOWN'.
362
363 :param fip_id: The id of the floating IP.
364 :returns: The final fip dict from the show_floatingip response.
365 """
366 fip = self.client.show_floatingip(fip_id)['floatingip']
367 self.assertIn('port_details', fip)
368 port_details = fip['port_details']
369 status = port_details['status']
370 start = int(time.time())
371
372 while status != lib_constants.PORT_STATUS_DOWN:
373 time.sleep(interval)
374 fip = self.client.show_floatingip(fip_id)['floatingip']
375 self.assertIn('port_details', fip)
376 port_details = fip['port_details']
377 status = port_details['status']
378
379 timed_out = int(time.time()) - start >= timeout
380
381 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100382 port_id = fip.get("port_id")
383 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600384 message = ('Floating IP %s attached port status failed to '
385 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100386 'the required time (%s s). Port details: %s' %
387 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600388 raise exceptions.TimeoutException(message)
389
elajkat38d90512021-12-13 14:21:30 +0100390 LOG.debug('Port %s attached to FIP %s is down after %s!',
391 fip.get("port_id"), fip_id, int(time.time()) - start)
Brian Haleyaf347da2018-09-14 11:24:00 -0600392 return fip
393
Hongbin Lu965b03d2018-04-25 22:32:30 +0000394
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800395class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900396 test_qos.QoSTestMixin,
397 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800398
399 same_network = True
400
401 @classmethod
402 @utils.requires_ext(extension="router", service="network")
403 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900404 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800405 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
406 def resource_setup(cls):
407 super(FloatingIPQosTest, cls).resource_setup()
408
Eduardo Olivares98772912021-02-19 12:36:21 +0100409 @classmethod
410 def setup_clients(cls):
411 super(FloatingIPQosTest, cls).setup_clients()
412 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300413 cls.qos_bw_limit_rule_client = \
414 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100415
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800416 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
417 def test_qos(self):
418 """Test floating IP is binding to a QoS policy with
419
420 ingress and egress bandwidth limit rules. And it applied correctly
421 by sending a file from the instance to the test node.
422 Then calculating the bandwidth every ~1 sec by the number of bits
423 received / elapsed time.
424 """
425
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200426 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
427
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800428 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100429
430 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800431 policy_id = self._create_qos_policy()
432 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100433
434 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300435 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
436 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
437 'direction': lib_constants.INGRESS_DIRECTION}
438 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
439 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800440
zahlabut7ebb66e2021-09-01 22:39:49 +0300441 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
442 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
443 'direction': lib_constants.EGRESS_DIRECTION}
444 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
445 qos_policy_id=policy_id, **rule_data)
446
447 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800448 policy_id)
449 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
450
451 fip = self.os_admin.network_client.get_floatingip(
452 self.fip['id'])['floatingip']
453 self.assertEqual(self.port['id'], fip['port_id'])
454
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100455 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800456 self.os_admin.network_client.update_floatingip(
457 self.fip['id'],
458 qos_policy_id=policy_id)
459
460 fip = self.os_admin.network_client.get_floatingip(
461 self.fip['id'])['floatingip']
462 self.assertEqual(policy_id, fip['qos_policy_id'])
463
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100464 # Basic test, Check that actual BW while downloading file
465 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800466 common_utils.wait_until_true(lambda: self._check_bw(
467 ssh_client,
468 self.fip['floating_ip_address'],
469 port=self.NC_PORT),
470 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100471 sleep=1,
472 exception=RuntimeError(
473 'Failed scenario: "Create a QoS policy associated with FIP" '
474 'Actual BW is not as expected!'))
475
476 # As admin user update QoS rules
477 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300478 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
479 policy_id, rule['id'],
480 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
481 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100482
483 # Check that actual BW while downloading file
484 # is as expected (Update BW)
485 common_utils.wait_until_true(lambda: self._check_bw(
486 ssh_client,
487 self.fip['floating_ip_address'],
488 port=self.NC_PORT,
489 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
490 timeout=120,
491 sleep=1,
492 exception=RuntimeError(
493 'Failed scenario: "Update QoS policy associated with FIP" '
494 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000495
496
497class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
498 base.BaseTempestTestCase):
499
500 same_network = None
501
502 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
503 def test_floating_ip_update(self):
504 """Test updating FIP with another port.
505
506 The test creates two servers and attaches floating ip to first server.
507 Then it checks server is accesible using the FIP. FIP is then
508 associated with the second server and connectivity is checked again.
509 """
510 ports = [self.create_port(
511 self.network, security_groups=[self.secgroup['id']])
512 for i in range(2)]
513
514 servers = []
515 for port in ports:
516 name = data_utils.rand_name("server-%s" % port['id'][:8])
517 server = self.create_server(
518 name=name,
519 flavor_ref=CONF.compute.flavor_ref,
520 key_name=self.keypair['name'],
521 image_ref=CONF.compute.image_ref,
522 networks=[{'port': port['id']}])['server']
523 server['name'] = name
524 servers.append(server)
525 for server in servers:
526 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200527 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000528
529 self.fip = self.create_floatingip(port=ports[0])
530 self.check_connectivity(self.fip['floating_ip_address'],
531 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100532 self.keypair['private_key'],
533 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000534 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
535
536 def _wait_for_fip_associated():
537 try:
538 self.check_servers_hostnames(servers[-1:], log_errors=False)
Vasyl Saienko569df852021-12-22 11:54:04 +0200539 # NOTE(vsaienko): it might take some time by neutron to update VIP
540 # retry on any exception here.
541 except Exception:
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000542 return False
543 return True
544
545 # The FIP is now associated with the port of the second server.
546 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200547 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000548 except common_utils.WaitTimeout:
549 self._log_console_output(servers[-1:])
550 self.fail(
551 "Server %s is not accessible via its floating ip %s" % (
552 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300553
554
555class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
556 credentials = ['primary', 'admin']
557
558 @classmethod
559 @utils.requires_ext(extension="router", service="network")
560 def skip_checks(cls):
561 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
562
563 def _create_keypair_and_secgroup(self):
564 self.keypair = self.create_keypair()
565 self.secgroup = self.create_security_group()
566 self.create_loginable_secgroup_rule(
567 secgroup_id=self.secgroup['id'])
568 self.create_pingable_secgroup_rule(
569 secgroup_id=self.secgroup['id'])
570
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000571 def _delete_floating_ip(self, fip_address):
572 ip_address = fip_address['floating_ip_address']
573
574 def _fip_is_free():
575 fips = self.os_admin.network_client.list_floatingips()
576 for fip in fips['floatingips']:
577 if ip_address == fip['floating_ip_address']:
578 return False
579 return True
580
581 self.delete_floatingip(fip_address)
582 try:
583 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
584 except common_utils.WaitTimeout:
585 self.fail("Can't reuse IP address %s because it is not free" %
586 ip_address)
587
588 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
589 delete_fip_ids=None):
590 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300591 if fip_addresses:
592 self.assertEqual(servers_num, len(fip_addresses),
593 ('Number of specified fip addresses '
594 'does not match the number of servers'))
595 network = self.create_network()
596 subnet = self.create_subnet(network)
597 router = self.create_router_by_client()
598 self.create_router_interface(router['id'], subnet['id'])
599
600 fips = []
601 for server in range(servers_num):
602 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000603 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300604 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000605 self._create_server_and_fip(network=network,
606 fip_address=fip,
607 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300608 return fips
609
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000610 def _create_server_and_fip(self, network, fip_address=None,
611 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300612 server = self.create_server(
613 flavor_ref=CONF.compute.flavor_ref,
614 image_ref=CONF.compute.image_ref,
615 key_name=self.keypair['name'],
616 networks=[{'uuid': network['id']}],
617 security_groups=[{'name': self.secgroup['name']}])
618 waiters.wait_for_server_status(self.os_primary.servers_client,
619 server['server']['id'],
620 constants.SERVER_STATUS_ACTIVE)
621 port = self.client.list_ports(
622 network_id=network['id'],
623 device_id=server['server']['id'])['ports'][0]
624
625 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000626 if delete_fip_address:
627 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300628 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000629 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300630 client=self.os_admin.network_client,
631 port=port)
632 self.addCleanup(
633 self.delete_floatingip, fip, self.os_admin.network_client)
634 else:
635 fip = self.create_floatingip(port=port)
636 return fip
637
638 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
639 for fip in [mutable_fip, permanent_fip]:
640 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
641 CONF.validation.image_ssh_user,
642 pkey=self.keypair['private_key'])
643 self.check_remote_connectivity(
644 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
645 self.check_remote_connectivity(
646 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
647
648 @testtools.skipUnless(CONF.network.public_network_id,
649 'The public_network_id option must be specified.')
650 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
651 def test_reuse_ip_address_with_other_fip_on_other_router(self):
652 """Reuse IP address by another floating IP on another router
653
654 Scenario:
655 1. Create and connect a router to the external network.
656 2. Create and connect an internal network to the router.
657 3. Create and connect 2 VMs to the internal network.
658 4. Create FIPs in the external network for the VMs.
659 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000660 6. Create and connect one more router to the external network.
661 7. Create and connect an internal network to the second router.
662 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300663 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000664 9. Delete VM2 FIP but save IP address that it used. The FIP is
665 deleted just before the creation of the new IP to "reserve" the
666 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300667 10. Create a FIP for the VM3 in the external network with
Elod Illesf2e985e2023-11-06 19:30:29 +0100668 the same IP address that was used for VM2.
Roman Safronov29c2dff2019-04-02 22:01:23 +0300669 11. Make sure that now VM1 is able to reach VM3 using the FIP.
670
671 Note, the scenario passes only in case corresponding
672 ARP update was sent to the external network when reusing same IP
673 address for another FIP.
674 """
675
676 self._create_keypair_and_secgroup()
677 [mutable_fip, permanent_fip] = (
678 self._create_network_and_servers(servers_num=2))
679 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300680 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000681 servers_num=1, fip_addresses=[mutable_fip],
682 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300683 self._check_fips_connectivity(mutable_fip, permanent_fip)