blob: 9b7bfccdc1c68f08aa29ec91204cfd47044f2e91 [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
Ihar Hrachyshka889566a2024-08-22 18:08:29 +0000204 @decorators.idempotent_id('b911b124-b6cb-449d-83d9-b34f3665741d')
205 @utils.requires_ext(extension='extraroute', service='network')
206 @testtools.skipUnless(
207 CONF.neutron_plugin_options.snat_rules_apply_to_nested_networks,
208 "Backend doesn't enable nested SNAT.")
209 def test_nested_snat_external_ip(self):
210 """Check connectivity to an external IP from a nested network."""
211 gateway_external_ip = self._get_external_gateway()
212
213 if not gateway_external_ip:
214 raise self.skipTest("IPv4 gateway is not configured for public "
215 "network or public_network_id is not "
216 "configured")
217 proxy = self._create_server()
218 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
219 CONF.validation.image_ssh_user,
220 pkey=self.keypair['private_key'])
221
222 # Create a nested router
223 router = self.create_router(
224 router_name=data_utils.rand_name('router'),
225 admin_state_up=True)
226
227 # Attach outer subnet to it
228 outer_port = self.create_port(self.network)
229 self.client.add_router_interface_with_port_id(router['id'],
230 outer_port['id'])
231
232 # Attach a nested subnet to it
233 network = self.create_network()
234 subnet = self.create_subnet(network)
235 self.create_router_interface(router['id'], subnet['id'])
236
237 # Set up static routes in both directions
238 self.client.update_extra_routes(
239 self.router['id'],
240 outer_port['fixed_ips'][0]['ip_address'], subnet['cidr'])
241 self.client.update_extra_routes(
242 router['id'], self.subnet['gateway_ip'], '0.0.0.0/0')
243
244 # Create a server inside the nested network
245 src_server = self._create_server(create_floating_ip=False,
246 network=network)
247
248 # Validate that it can access external gw ip (via nested snat)
249 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
250 ssh_client = ssh.Client(src_server_ip,
251 CONF.validation.image_ssh_user,
252 pkey=self.keypair['private_key'],
253 proxy_client=proxy_client)
254 self.check_remote_connectivity(ssh_client,
255 gateway_external_ip,
256 servers=[proxy, src_server])
257
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800258
Hongbin Lu965b03d2018-04-25 22:32:30 +0000259class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
260 base.BaseTempestTestCase):
261 same_network = True
262
263 @classmethod
264 @utils.requires_ext(extension="router", service="network")
265 @utils.requires_ext(extension="fip-port-details", service="network")
266 def resource_setup(cls):
267 super(FloatingIPPortDetailsTest, cls).resource_setup()
268
269 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
270 def test_floatingip_port_details(self):
271 """Tests the following:
272
273 1. Create a port with floating ip in Neutron.
274 2. Create two servers in Nova.
275 3. Attach the port to the server.
276 4. Detach the port from the server.
277 5. Attach the port to the second server.
278 6. Detach the port from the second server.
279 """
280 port = self.create_port(self.network)
281 fip = self.create_and_associate_floatingip(port['id'])
282 server1 = self._create_server(create_floating_ip=False)
283 server2 = self._create_server(create_floating_ip=False)
284
285 for server in [server1, server2]:
286 # attach the port to the server
287 self.create_interface(
288 server['server']['id'], port_id=port['id'])
289 waiters.wait_for_interface_status(
290 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600291 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000292 fip = self.client.show_floatingip(fip['id'])['floatingip']
Roman Safronov4d6ae072023-10-31 17:49:35 +0200293 server_data = self.os_admin.servers_client.show_server(
294 server['server']['id'])['server']
295 zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
Hongbin Lu965b03d2018-04-25 22:32:30 +0000296 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600297 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Roman Safronov4d6ae072023-10-31 17:49:35 +0200298 device_id=server['server']['id'],
299 device_owner=zone)
elajkat38d90512021-12-13 14:21:30 +0100300 LOG.debug('Port check for server %s and FIP %s finished, '
301 'lets detach port %s from server!',
302 server['server']['id'], fip['id'], port['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000303
304 # detach the port from the server; this is a cast in the compute
305 # API so we have to poll the port until the device_id is unset.
306 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600307 port = self._wait_for_port_detach(port['id'])
elajkat38d90512021-12-13 14:21:30 +0100308 LOG.debug('Port %s has been detached from server %s, lets check '
309 'the status of port in FIP %s details!',
310 port['id'], server['server']['id'], fip['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600311 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000312 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600313 fip, port, status=lib_constants.PORT_STATUS_DOWN,
314 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000315
316 def _check_port_details(self, fip, port, status, device_id, device_owner):
317 self.assertIn('port_details', fip)
318 port_details = fip['port_details']
319 self.assertEqual(port['name'], port_details['name'])
320 self.assertEqual(port['network_id'], port_details['network_id'])
321 self.assertEqual(port['mac_address'], port_details['mac_address'])
322 self.assertEqual(port['admin_state_up'],
323 port_details['admin_state_up'])
324 self.assertEqual(status, port_details['status'])
325 self.assertEqual(device_id, port_details['device_id'])
326 self.assertEqual(device_owner, port_details['device_owner'])
327
328 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
329 """Waits for the port's device_id to be unset.
330
331 :param port_id: The id of the port being detached.
332 :returns: The final port dict from the show_port response.
333 """
334 port = self.client.show_port(port_id)['port']
335 device_id = port['device_id']
336 start = int(time.time())
337
338 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
339 # None, but it's not contractual so handle Falsey either way.
340 while device_id:
341 time.sleep(interval)
342 port = self.client.show_port(port_id)['port']
343 device_id = port['device_id']
344
345 timed_out = int(time.time()) - start >= timeout
346
347 if device_id and timed_out:
348 message = ('Port %s failed to detach (device_id %s) within '
349 'the required time (%s s).' %
350 (port_id, device_id, timeout))
351 raise exceptions.TimeoutException(message)
352
353 return port
354
Brian Haleyaf347da2018-09-14 11:24:00 -0600355 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
356 """Waits for the fip's attached port status to be 'DOWN'.
357
358 :param fip_id: The id of the floating IP.
359 :returns: The final fip dict from the show_floatingip response.
360 """
361 fip = self.client.show_floatingip(fip_id)['floatingip']
362 self.assertIn('port_details', fip)
363 port_details = fip['port_details']
364 status = port_details['status']
365 start = int(time.time())
366
367 while status != lib_constants.PORT_STATUS_DOWN:
368 time.sleep(interval)
369 fip = self.client.show_floatingip(fip_id)['floatingip']
370 self.assertIn('port_details', fip)
371 port_details = fip['port_details']
372 status = port_details['status']
373
374 timed_out = int(time.time()) - start >= timeout
375
376 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100377 port_id = fip.get("port_id")
378 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600379 message = ('Floating IP %s attached port status failed to '
380 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100381 'the required time (%s s). Port details: %s' %
382 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600383 raise exceptions.TimeoutException(message)
384
elajkat38d90512021-12-13 14:21:30 +0100385 LOG.debug('Port %s attached to FIP %s is down after %s!',
386 fip.get("port_id"), fip_id, int(time.time()) - start)
Brian Haleyaf347da2018-09-14 11:24:00 -0600387 return fip
388
Hongbin Lu965b03d2018-04-25 22:32:30 +0000389
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800390class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900391 test_qos.QoSTestMixin,
392 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800393
394 same_network = True
395
396 @classmethod
397 @utils.requires_ext(extension="router", service="network")
398 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900399 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800400 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
401 def resource_setup(cls):
402 super(FloatingIPQosTest, cls).resource_setup()
403
Eduardo Olivares98772912021-02-19 12:36:21 +0100404 @classmethod
405 def setup_clients(cls):
406 super(FloatingIPQosTest, cls).setup_clients()
407 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300408 cls.qos_bw_limit_rule_client = \
409 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100410
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800411 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
412 def test_qos(self):
413 """Test floating IP is binding to a QoS policy with
414
415 ingress and egress bandwidth limit rules. And it applied correctly
416 by sending a file from the instance to the test node.
417 Then calculating the bandwidth every ~1 sec by the number of bits
418 received / elapsed time.
419 """
420
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200421 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
422
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800423 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100424
425 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800426 policy_id = self._create_qos_policy()
427 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100428
429 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300430 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
431 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
432 'direction': lib_constants.INGRESS_DIRECTION}
433 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
434 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800435
zahlabut7ebb66e2021-09-01 22:39:49 +0300436 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
437 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
438 'direction': lib_constants.EGRESS_DIRECTION}
439 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
440 qos_policy_id=policy_id, **rule_data)
441
442 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800443 policy_id)
444 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
445
446 fip = self.os_admin.network_client.get_floatingip(
447 self.fip['id'])['floatingip']
448 self.assertEqual(self.port['id'], fip['port_id'])
449
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100450 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800451 self.os_admin.network_client.update_floatingip(
452 self.fip['id'],
453 qos_policy_id=policy_id)
454
455 fip = self.os_admin.network_client.get_floatingip(
456 self.fip['id'])['floatingip']
457 self.assertEqual(policy_id, fip['qos_policy_id'])
458
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100459 # Basic test, Check that actual BW while downloading file
460 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800461 common_utils.wait_until_true(lambda: self._check_bw(
462 ssh_client,
463 self.fip['floating_ip_address'],
464 port=self.NC_PORT),
465 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100466 sleep=1,
467 exception=RuntimeError(
468 'Failed scenario: "Create a QoS policy associated with FIP" '
469 'Actual BW is not as expected!'))
470
471 # As admin user update QoS rules
472 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300473 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
474 policy_id, rule['id'],
475 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
476 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100477
478 # Check that actual BW while downloading file
479 # is as expected (Update BW)
480 common_utils.wait_until_true(lambda: self._check_bw(
481 ssh_client,
482 self.fip['floating_ip_address'],
483 port=self.NC_PORT,
484 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
485 timeout=120,
486 sleep=1,
487 exception=RuntimeError(
488 'Failed scenario: "Update QoS policy associated with FIP" '
489 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000490
491
492class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
493 base.BaseTempestTestCase):
494
495 same_network = None
496
497 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
498 def test_floating_ip_update(self):
499 """Test updating FIP with another port.
500
501 The test creates two servers and attaches floating ip to first server.
502 Then it checks server is accesible using the FIP. FIP is then
503 associated with the second server and connectivity is checked again.
504 """
505 ports = [self.create_port(
506 self.network, security_groups=[self.secgroup['id']])
507 for i in range(2)]
508
509 servers = []
510 for port in ports:
511 name = data_utils.rand_name("server-%s" % port['id'][:8])
512 server = self.create_server(
513 name=name,
514 flavor_ref=CONF.compute.flavor_ref,
515 key_name=self.keypair['name'],
516 image_ref=CONF.compute.image_ref,
517 networks=[{'port': port['id']}])['server']
518 server['name'] = name
519 servers.append(server)
520 for server in servers:
521 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200522 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000523
524 self.fip = self.create_floatingip(port=ports[0])
525 self.check_connectivity(self.fip['floating_ip_address'],
526 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100527 self.keypair['private_key'],
528 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000529 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
530
531 def _wait_for_fip_associated():
532 try:
533 self.check_servers_hostnames(servers[-1:], log_errors=False)
534 except (AssertionError, exceptions.SSHTimeout):
535 return False
536 return True
537
538 # The FIP is now associated with the port of the second server.
539 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200540 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000541 except common_utils.WaitTimeout:
542 self._log_console_output(servers[-1:])
543 self.fail(
544 "Server %s is not accessible via its floating ip %s" % (
545 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300546
547
548class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
549 credentials = ['primary', 'admin']
550
551 @classmethod
552 @utils.requires_ext(extension="router", service="network")
553 def skip_checks(cls):
554 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
555
556 def _create_keypair_and_secgroup(self):
557 self.keypair = self.create_keypair()
558 self.secgroup = self.create_security_group()
559 self.create_loginable_secgroup_rule(
560 secgroup_id=self.secgroup['id'])
561 self.create_pingable_secgroup_rule(
562 secgroup_id=self.secgroup['id'])
563
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000564 def _delete_floating_ip(self, fip_address):
565 ip_address = fip_address['floating_ip_address']
566
567 def _fip_is_free():
568 fips = self.os_admin.network_client.list_floatingips()
569 for fip in fips['floatingips']:
570 if ip_address == fip['floating_ip_address']:
571 return False
572 return True
573
574 self.delete_floatingip(fip_address)
575 try:
576 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
577 except common_utils.WaitTimeout:
578 self.fail("Can't reuse IP address %s because it is not free" %
579 ip_address)
580
581 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
582 delete_fip_ids=None):
583 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300584 if fip_addresses:
585 self.assertEqual(servers_num, len(fip_addresses),
586 ('Number of specified fip addresses '
587 'does not match the number of servers'))
588 network = self.create_network()
589 subnet = self.create_subnet(network)
590 router = self.create_router_by_client()
591 self.create_router_interface(router['id'], subnet['id'])
592
593 fips = []
594 for server in range(servers_num):
595 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000596 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300597 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000598 self._create_server_and_fip(network=network,
599 fip_address=fip,
600 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300601 return fips
602
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000603 def _create_server_and_fip(self, network, fip_address=None,
604 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300605 server = self.create_server(
606 flavor_ref=CONF.compute.flavor_ref,
607 image_ref=CONF.compute.image_ref,
608 key_name=self.keypair['name'],
609 networks=[{'uuid': network['id']}],
610 security_groups=[{'name': self.secgroup['name']}])
611 waiters.wait_for_server_status(self.os_primary.servers_client,
612 server['server']['id'],
613 constants.SERVER_STATUS_ACTIVE)
614 port = self.client.list_ports(
615 network_id=network['id'],
616 device_id=server['server']['id'])['ports'][0]
617
618 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000619 if delete_fip_address:
620 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300621 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000622 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300623 client=self.os_admin.network_client,
624 port=port)
625 self.addCleanup(
626 self.delete_floatingip, fip, self.os_admin.network_client)
627 else:
628 fip = self.create_floatingip(port=port)
629 return fip
630
631 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
632 for fip in [mutable_fip, permanent_fip]:
633 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
634 CONF.validation.image_ssh_user,
635 pkey=self.keypair['private_key'])
636 self.check_remote_connectivity(
637 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
638 self.check_remote_connectivity(
639 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
640
641 @testtools.skipUnless(CONF.network.public_network_id,
642 'The public_network_id option must be specified.')
643 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
644 def test_reuse_ip_address_with_other_fip_on_other_router(self):
645 """Reuse IP address by another floating IP on another router
646
647 Scenario:
648 1. Create and connect a router to the external network.
649 2. Create and connect an internal network to the router.
650 3. Create and connect 2 VMs to the internal network.
651 4. Create FIPs in the external network for the VMs.
652 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000653 6. Create and connect one more router to the external network.
654 7. Create and connect an internal network to the second router.
655 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300656 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000657 9. Delete VM2 FIP but save IP address that it used. The FIP is
658 deleted just before the creation of the new IP to "reserve" the
659 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300660 10. Create a FIP for the VM3 in the external network with
Elod Illesf2e985e2023-11-06 19:30:29 +0100661 the same IP address that was used for VM2.
Roman Safronov29c2dff2019-04-02 22:01:23 +0300662 11. Make sure that now VM1 is able to reach VM3 using the FIP.
663
664 Note, the scenario passes only in case corresponding
665 ARP update was sent to the external network when reusing same IP
666 address for another FIP.
667 """
668
669 self._create_keypair_and_secgroup()
670 [mutable_fip, permanent_fip] = (
671 self._create_network_and_servers(servers_num=2))
672 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300673 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000674 servers_num=1, fip_addresses=[mutable_fip],
675 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300676 self._check_fips_connectivity(mutable_fip, permanent_fip)