blob: f22239678d06095da1ef23f3a6574540df425c9d [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
207
Hongbin Lu965b03d2018-04-25 22:32:30 +0000208class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
209 base.BaseTempestTestCase):
210 same_network = True
211
212 @classmethod
213 @utils.requires_ext(extension="router", service="network")
214 @utils.requires_ext(extension="fip-port-details", service="network")
215 def resource_setup(cls):
216 super(FloatingIPPortDetailsTest, cls).resource_setup()
217
218 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
219 def test_floatingip_port_details(self):
220 """Tests the following:
221
222 1. Create a port with floating ip in Neutron.
223 2. Create two servers in Nova.
224 3. Attach the port to the server.
225 4. Detach the port from the server.
226 5. Attach the port to the second server.
227 6. Detach the port from the second server.
228 """
229 port = self.create_port(self.network)
230 fip = self.create_and_associate_floatingip(port['id'])
231 server1 = self._create_server(create_floating_ip=False)
232 server2 = self._create_server(create_floating_ip=False)
233
234 for server in [server1, server2]:
235 # attach the port to the server
236 self.create_interface(
237 server['server']['id'], port_id=port['id'])
238 waiters.wait_for_interface_status(
239 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600240 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000241 fip = self.client.show_floatingip(fip['id'])['floatingip']
Roman Safronov4d6ae072023-10-31 17:49:35 +0200242 server_data = self.os_admin.servers_client.show_server(
243 server['server']['id'])['server']
244 zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
Hongbin Lu965b03d2018-04-25 22:32:30 +0000245 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600246 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Roman Safronov4d6ae072023-10-31 17:49:35 +0200247 device_id=server['server']['id'],
248 device_owner=zone)
elajkat38d90512021-12-13 14:21:30 +0100249 LOG.debug('Port check for server %s and FIP %s finished, '
250 'lets detach port %s from server!',
251 server['server']['id'], fip['id'], port['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000252
253 # detach the port from the server; this is a cast in the compute
254 # API so we have to poll the port until the device_id is unset.
255 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600256 port = self._wait_for_port_detach(port['id'])
elajkat38d90512021-12-13 14:21:30 +0100257 LOG.debug('Port %s has been detached from server %s, lets check '
258 'the status of port in FIP %s details!',
259 port['id'], server['server']['id'], fip['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600260 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000261 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600262 fip, port, status=lib_constants.PORT_STATUS_DOWN,
263 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000264
265 def _check_port_details(self, fip, port, status, device_id, device_owner):
266 self.assertIn('port_details', fip)
267 port_details = fip['port_details']
268 self.assertEqual(port['name'], port_details['name'])
269 self.assertEqual(port['network_id'], port_details['network_id'])
270 self.assertEqual(port['mac_address'], port_details['mac_address'])
271 self.assertEqual(port['admin_state_up'],
272 port_details['admin_state_up'])
273 self.assertEqual(status, port_details['status'])
274 self.assertEqual(device_id, port_details['device_id'])
275 self.assertEqual(device_owner, port_details['device_owner'])
276
277 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
278 """Waits for the port's device_id to be unset.
279
280 :param port_id: The id of the port being detached.
281 :returns: The final port dict from the show_port response.
282 """
283 port = self.client.show_port(port_id)['port']
284 device_id = port['device_id']
285 start = int(time.time())
286
287 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
288 # None, but it's not contractual so handle Falsey either way.
289 while device_id:
290 time.sleep(interval)
291 port = self.client.show_port(port_id)['port']
292 device_id = port['device_id']
293
294 timed_out = int(time.time()) - start >= timeout
295
296 if device_id and timed_out:
297 message = ('Port %s failed to detach (device_id %s) within '
298 'the required time (%s s).' %
299 (port_id, device_id, timeout))
300 raise exceptions.TimeoutException(message)
301
302 return port
303
Brian Haleyaf347da2018-09-14 11:24:00 -0600304 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
305 """Waits for the fip's attached port status to be 'DOWN'.
306
307 :param fip_id: The id of the floating IP.
308 :returns: The final fip dict from the show_floatingip response.
309 """
310 fip = self.client.show_floatingip(fip_id)['floatingip']
311 self.assertIn('port_details', fip)
312 port_details = fip['port_details']
313 status = port_details['status']
314 start = int(time.time())
315
316 while status != lib_constants.PORT_STATUS_DOWN:
317 time.sleep(interval)
318 fip = self.client.show_floatingip(fip_id)['floatingip']
319 self.assertIn('port_details', fip)
320 port_details = fip['port_details']
321 status = port_details['status']
322
323 timed_out = int(time.time()) - start >= timeout
324
325 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100326 port_id = fip.get("port_id")
327 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600328 message = ('Floating IP %s attached port status failed to '
329 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100330 'the required time (%s s). Port details: %s' %
331 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600332 raise exceptions.TimeoutException(message)
333
elajkat38d90512021-12-13 14:21:30 +0100334 LOG.debug('Port %s attached to FIP %s is down after %s!',
335 fip.get("port_id"), fip_id, int(time.time()) - start)
Brian Haleyaf347da2018-09-14 11:24:00 -0600336 return fip
337
Hongbin Lu965b03d2018-04-25 22:32:30 +0000338
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800339class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900340 test_qos.QoSTestMixin,
341 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800342
343 same_network = True
344
345 @classmethod
346 @utils.requires_ext(extension="router", service="network")
347 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900348 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800349 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
350 def resource_setup(cls):
351 super(FloatingIPQosTest, cls).resource_setup()
352
Eduardo Olivares98772912021-02-19 12:36:21 +0100353 @classmethod
354 def setup_clients(cls):
355 super(FloatingIPQosTest, cls).setup_clients()
356 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300357 cls.qos_bw_limit_rule_client = \
358 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100359
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800360 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
361 def test_qos(self):
362 """Test floating IP is binding to a QoS policy with
363
364 ingress and egress bandwidth limit rules. And it applied correctly
365 by sending a file from the instance to the test node.
366 Then calculating the bandwidth every ~1 sec by the number of bits
367 received / elapsed time.
368 """
369
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200370 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
371
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800372 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100373
374 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800375 policy_id = self._create_qos_policy()
376 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100377
378 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300379 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
380 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
381 'direction': lib_constants.INGRESS_DIRECTION}
382 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
383 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800384
zahlabut7ebb66e2021-09-01 22:39:49 +0300385 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
386 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
387 'direction': lib_constants.EGRESS_DIRECTION}
388 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
389 qos_policy_id=policy_id, **rule_data)
390
391 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800392 policy_id)
393 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
394
395 fip = self.os_admin.network_client.get_floatingip(
396 self.fip['id'])['floatingip']
397 self.assertEqual(self.port['id'], fip['port_id'])
398
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100399 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800400 self.os_admin.network_client.update_floatingip(
401 self.fip['id'],
402 qos_policy_id=policy_id)
403
404 fip = self.os_admin.network_client.get_floatingip(
405 self.fip['id'])['floatingip']
406 self.assertEqual(policy_id, fip['qos_policy_id'])
407
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100408 # Basic test, Check that actual BW while downloading file
409 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800410 common_utils.wait_until_true(lambda: self._check_bw(
411 ssh_client,
412 self.fip['floating_ip_address'],
413 port=self.NC_PORT),
414 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100415 sleep=1,
416 exception=RuntimeError(
417 'Failed scenario: "Create a QoS policy associated with FIP" '
418 'Actual BW is not as expected!'))
419
420 # As admin user update QoS rules
421 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300422 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
423 policy_id, rule['id'],
424 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
425 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100426
427 # Check that actual BW while downloading file
428 # is as expected (Update BW)
429 common_utils.wait_until_true(lambda: self._check_bw(
430 ssh_client,
431 self.fip['floating_ip_address'],
432 port=self.NC_PORT,
433 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
434 timeout=120,
435 sleep=1,
436 exception=RuntimeError(
437 'Failed scenario: "Update QoS policy associated with FIP" '
438 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000439
440
441class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
442 base.BaseTempestTestCase):
443
444 same_network = None
445
446 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
447 def test_floating_ip_update(self):
448 """Test updating FIP with another port.
449
450 The test creates two servers and attaches floating ip to first server.
451 Then it checks server is accesible using the FIP. FIP is then
452 associated with the second server and connectivity is checked again.
453 """
454 ports = [self.create_port(
455 self.network, security_groups=[self.secgroup['id']])
456 for i in range(2)]
457
458 servers = []
459 for port in ports:
460 name = data_utils.rand_name("server-%s" % port['id'][:8])
461 server = self.create_server(
462 name=name,
463 flavor_ref=CONF.compute.flavor_ref,
464 key_name=self.keypair['name'],
465 image_ref=CONF.compute.image_ref,
466 networks=[{'port': port['id']}])['server']
467 server['name'] = name
468 servers.append(server)
469 for server in servers:
470 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200471 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000472
473 self.fip = self.create_floatingip(port=ports[0])
474 self.check_connectivity(self.fip['floating_ip_address'],
475 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100476 self.keypair['private_key'],
477 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000478 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
479
480 def _wait_for_fip_associated():
481 try:
482 self.check_servers_hostnames(servers[-1:], log_errors=False)
483 except (AssertionError, exceptions.SSHTimeout):
484 return False
485 return True
486
487 # The FIP is now associated with the port of the second server.
488 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200489 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000490 except common_utils.WaitTimeout:
491 self._log_console_output(servers[-1:])
492 self.fail(
493 "Server %s is not accessible via its floating ip %s" % (
494 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300495
496
497class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
498 credentials = ['primary', 'admin']
499
500 @classmethod
501 @utils.requires_ext(extension="router", service="network")
502 def skip_checks(cls):
503 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
504
505 def _create_keypair_and_secgroup(self):
506 self.keypair = self.create_keypair()
507 self.secgroup = self.create_security_group()
508 self.create_loginable_secgroup_rule(
509 secgroup_id=self.secgroup['id'])
510 self.create_pingable_secgroup_rule(
511 secgroup_id=self.secgroup['id'])
512
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000513 def _delete_floating_ip(self, fip_address):
514 ip_address = fip_address['floating_ip_address']
515
516 def _fip_is_free():
517 fips = self.os_admin.network_client.list_floatingips()
518 for fip in fips['floatingips']:
519 if ip_address == fip['floating_ip_address']:
520 return False
521 return True
522
523 self.delete_floatingip(fip_address)
524 try:
525 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
526 except common_utils.WaitTimeout:
527 self.fail("Can't reuse IP address %s because it is not free" %
528 ip_address)
529
530 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
531 delete_fip_ids=None):
532 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300533 if fip_addresses:
534 self.assertEqual(servers_num, len(fip_addresses),
535 ('Number of specified fip addresses '
536 'does not match the number of servers'))
537 network = self.create_network()
538 subnet = self.create_subnet(network)
539 router = self.create_router_by_client()
540 self.create_router_interface(router['id'], subnet['id'])
541
542 fips = []
543 for server in range(servers_num):
544 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000545 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300546 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000547 self._create_server_and_fip(network=network,
548 fip_address=fip,
549 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300550 return fips
551
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000552 def _create_server_and_fip(self, network, fip_address=None,
553 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300554 server = self.create_server(
555 flavor_ref=CONF.compute.flavor_ref,
556 image_ref=CONF.compute.image_ref,
557 key_name=self.keypair['name'],
558 networks=[{'uuid': network['id']}],
559 security_groups=[{'name': self.secgroup['name']}])
560 waiters.wait_for_server_status(self.os_primary.servers_client,
561 server['server']['id'],
562 constants.SERVER_STATUS_ACTIVE)
563 port = self.client.list_ports(
564 network_id=network['id'],
565 device_id=server['server']['id'])['ports'][0]
566
567 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000568 if delete_fip_address:
569 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300570 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000571 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300572 client=self.os_admin.network_client,
573 port=port)
574 self.addCleanup(
575 self.delete_floatingip, fip, self.os_admin.network_client)
576 else:
577 fip = self.create_floatingip(port=port)
578 return fip
579
580 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
581 for fip in [mutable_fip, permanent_fip]:
582 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
583 CONF.validation.image_ssh_user,
584 pkey=self.keypair['private_key'])
585 self.check_remote_connectivity(
586 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
587 self.check_remote_connectivity(
588 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
589
590 @testtools.skipUnless(CONF.network.public_network_id,
591 'The public_network_id option must be specified.')
592 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
593 def test_reuse_ip_address_with_other_fip_on_other_router(self):
594 """Reuse IP address by another floating IP on another router
595
596 Scenario:
597 1. Create and connect a router to the external network.
598 2. Create and connect an internal network to the router.
599 3. Create and connect 2 VMs to the internal network.
600 4. Create FIPs in the external network for the VMs.
601 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000602 6. Create and connect one more router to the external network.
603 7. Create and connect an internal network to the second router.
604 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300605 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000606 9. Delete VM2 FIP but save IP address that it used. The FIP is
607 deleted just before the creation of the new IP to "reserve" the
608 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300609 10. Create a FIP for the VM3 in the external network with
Elod Illesf2e985e2023-11-06 19:30:29 +0100610 the same IP address that was used for VM2.
Roman Safronov29c2dff2019-04-02 22:01:23 +0300611 11. Make sure that now VM1 is able to reach VM3 using the FIP.
612
613 Note, the scenario passes only in case corresponding
614 ARP update was sent to the external network when reusing same IP
615 address for another FIP.
616 """
617
618 self._create_keypair_and_secgroup()
619 [mutable_fip, permanent_fip] = (
620 self._create_network_and_servers(servers_num=2))
621 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300622 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000623 servers_num=1, fip_addresses=[mutable_fip],
624 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300625 self._check_fips_connectivity(mutable_fip, permanent_fip)