blob: d8ef7da86378a2a1f8c2389465c04921ac57aba0 [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
204
Hongbin Lu965b03d2018-04-25 22:32:30 +0000205class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
206 base.BaseTempestTestCase):
207 same_network = True
208
209 @classmethod
210 @utils.requires_ext(extension="router", service="network")
211 @utils.requires_ext(extension="fip-port-details", service="network")
212 def resource_setup(cls):
213 super(FloatingIPPortDetailsTest, cls).resource_setup()
214
215 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
216 def test_floatingip_port_details(self):
217 """Tests the following:
218
219 1. Create a port with floating ip in Neutron.
220 2. Create two servers in Nova.
221 3. Attach the port to the server.
222 4. Detach the port from the server.
223 5. Attach the port to the second server.
224 6. Detach the port from the second server.
225 """
226 port = self.create_port(self.network)
227 fip = self.create_and_associate_floatingip(port['id'])
228 server1 = self._create_server(create_floating_ip=False)
229 server2 = self._create_server(create_floating_ip=False)
230
231 for server in [server1, server2]:
232 # attach the port to the server
233 self.create_interface(
234 server['server']['id'], port_id=port['id'])
235 waiters.wait_for_interface_status(
236 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600237 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000238 fip = self.client.show_floatingip(fip['id'])['floatingip']
Roman Safronov4d6ae072023-10-31 17:49:35 +0200239 server_data = self.os_admin.servers_client.show_server(
240 server['server']['id'])['server']
241 zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
Hongbin Lu965b03d2018-04-25 22:32:30 +0000242 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600243 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Roman Safronov4d6ae072023-10-31 17:49:35 +0200244 device_id=server['server']['id'],
245 device_owner=zone)
elajkat38d90512021-12-13 14:21:30 +0100246 LOG.debug('Port check for server %s and FIP %s finished, '
247 'lets detach port %s from server!',
248 server['server']['id'], fip['id'], port['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000249
250 # detach the port from the server; this is a cast in the compute
251 # API so we have to poll the port until the device_id is unset.
252 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600253 port = self._wait_for_port_detach(port['id'])
elajkat38d90512021-12-13 14:21:30 +0100254 LOG.debug('Port %s has been detached from server %s, lets check '
255 'the status of port in FIP %s details!',
256 port['id'], server['server']['id'], fip['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600257 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000258 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600259 fip, port, status=lib_constants.PORT_STATUS_DOWN,
260 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000261
262 def _check_port_details(self, fip, port, status, device_id, device_owner):
263 self.assertIn('port_details', fip)
264 port_details = fip['port_details']
265 self.assertEqual(port['name'], port_details['name'])
266 self.assertEqual(port['network_id'], port_details['network_id'])
267 self.assertEqual(port['mac_address'], port_details['mac_address'])
268 self.assertEqual(port['admin_state_up'],
269 port_details['admin_state_up'])
270 self.assertEqual(status, port_details['status'])
271 self.assertEqual(device_id, port_details['device_id'])
272 self.assertEqual(device_owner, port_details['device_owner'])
273
274 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
275 """Waits for the port's device_id to be unset.
276
277 :param port_id: The id of the port being detached.
278 :returns: The final port dict from the show_port response.
279 """
280 port = self.client.show_port(port_id)['port']
281 device_id = port['device_id']
282 start = int(time.time())
283
284 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
285 # None, but it's not contractual so handle Falsey either way.
286 while device_id:
287 time.sleep(interval)
288 port = self.client.show_port(port_id)['port']
289 device_id = port['device_id']
290
291 timed_out = int(time.time()) - start >= timeout
292
293 if device_id and timed_out:
294 message = ('Port %s failed to detach (device_id %s) within '
295 'the required time (%s s).' %
296 (port_id, device_id, timeout))
297 raise exceptions.TimeoutException(message)
298
299 return port
300
Brian Haleyaf347da2018-09-14 11:24:00 -0600301 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
302 """Waits for the fip's attached port status to be 'DOWN'.
303
304 :param fip_id: The id of the floating IP.
305 :returns: The final fip dict from the show_floatingip response.
306 """
307 fip = self.client.show_floatingip(fip_id)['floatingip']
308 self.assertIn('port_details', fip)
309 port_details = fip['port_details']
310 status = port_details['status']
311 start = int(time.time())
312
313 while status != lib_constants.PORT_STATUS_DOWN:
314 time.sleep(interval)
315 fip = self.client.show_floatingip(fip_id)['floatingip']
316 self.assertIn('port_details', fip)
317 port_details = fip['port_details']
318 status = port_details['status']
319
320 timed_out = int(time.time()) - start >= timeout
321
322 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100323 port_id = fip.get("port_id")
324 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600325 message = ('Floating IP %s attached port status failed to '
326 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100327 'the required time (%s s). Port details: %s' %
328 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600329 raise exceptions.TimeoutException(message)
330
elajkat38d90512021-12-13 14:21:30 +0100331 LOG.debug('Port %s attached to FIP %s is down after %s!',
332 fip.get("port_id"), fip_id, int(time.time()) - start)
Brian Haleyaf347da2018-09-14 11:24:00 -0600333 return fip
334
Hongbin Lu965b03d2018-04-25 22:32:30 +0000335
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800336class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900337 test_qos.QoSTestMixin,
338 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800339
340 same_network = True
341
342 @classmethod
343 @utils.requires_ext(extension="router", service="network")
344 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900345 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800346 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
347 def resource_setup(cls):
348 super(FloatingIPQosTest, cls).resource_setup()
349
Eduardo Olivares98772912021-02-19 12:36:21 +0100350 @classmethod
351 def setup_clients(cls):
352 super(FloatingIPQosTest, cls).setup_clients()
353 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300354 cls.qos_bw_limit_rule_client = \
355 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100356
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800357 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
358 def test_qos(self):
359 """Test floating IP is binding to a QoS policy with
360
361 ingress and egress bandwidth limit rules. And it applied correctly
362 by sending a file from the instance to the test node.
363 Then calculating the bandwidth every ~1 sec by the number of bits
364 received / elapsed time.
365 """
366
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200367 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
368
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800369 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100370
371 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800372 policy_id = self._create_qos_policy()
373 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100374
375 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300376 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
377 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
378 'direction': lib_constants.INGRESS_DIRECTION}
379 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
380 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800381
zahlabut7ebb66e2021-09-01 22:39:49 +0300382 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
383 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
384 'direction': lib_constants.EGRESS_DIRECTION}
385 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
386 qos_policy_id=policy_id, **rule_data)
387
388 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800389 policy_id)
390 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
391
392 fip = self.os_admin.network_client.get_floatingip(
393 self.fip['id'])['floatingip']
394 self.assertEqual(self.port['id'], fip['port_id'])
395
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100396 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800397 self.os_admin.network_client.update_floatingip(
398 self.fip['id'],
399 qos_policy_id=policy_id)
400
401 fip = self.os_admin.network_client.get_floatingip(
402 self.fip['id'])['floatingip']
403 self.assertEqual(policy_id, fip['qos_policy_id'])
404
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100405 # Basic test, Check that actual BW while downloading file
406 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800407 common_utils.wait_until_true(lambda: self._check_bw(
408 ssh_client,
409 self.fip['floating_ip_address'],
410 port=self.NC_PORT),
411 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100412 sleep=1,
413 exception=RuntimeError(
414 'Failed scenario: "Create a QoS policy associated with FIP" '
415 'Actual BW is not as expected!'))
416
417 # As admin user update QoS rules
418 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300419 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
420 policy_id, rule['id'],
421 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
422 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100423
424 # Check that actual BW while downloading file
425 # is as expected (Update BW)
426 common_utils.wait_until_true(lambda: self._check_bw(
427 ssh_client,
428 self.fip['floating_ip_address'],
429 port=self.NC_PORT,
430 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
431 timeout=120,
432 sleep=1,
433 exception=RuntimeError(
434 'Failed scenario: "Update QoS policy associated with FIP" '
435 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000436
437
438class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
439 base.BaseTempestTestCase):
440
441 same_network = None
442
443 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
444 def test_floating_ip_update(self):
445 """Test updating FIP with another port.
446
447 The test creates two servers and attaches floating ip to first server.
448 Then it checks server is accesible using the FIP. FIP is then
449 associated with the second server and connectivity is checked again.
450 """
451 ports = [self.create_port(
452 self.network, security_groups=[self.secgroup['id']])
453 for i in range(2)]
454
455 servers = []
456 for port in ports:
457 name = data_utils.rand_name("server-%s" % port['id'][:8])
458 server = self.create_server(
459 name=name,
460 flavor_ref=CONF.compute.flavor_ref,
461 key_name=self.keypair['name'],
462 image_ref=CONF.compute.image_ref,
463 networks=[{'port': port['id']}])['server']
464 server['name'] = name
465 servers.append(server)
466 for server in servers:
467 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200468 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000469
470 self.fip = self.create_floatingip(port=ports[0])
471 self.check_connectivity(self.fip['floating_ip_address'],
472 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100473 self.keypair['private_key'],
474 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000475 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
476
477 def _wait_for_fip_associated():
478 try:
479 self.check_servers_hostnames(servers[-1:], log_errors=False)
480 except (AssertionError, exceptions.SSHTimeout):
481 return False
482 return True
483
484 # The FIP is now associated with the port of the second server.
485 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200486 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000487 except common_utils.WaitTimeout:
488 self._log_console_output(servers[-1:])
489 self.fail(
490 "Server %s is not accessible via its floating ip %s" % (
491 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300492
493
494class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
495 credentials = ['primary', 'admin']
496
497 @classmethod
498 @utils.requires_ext(extension="router", service="network")
499 def skip_checks(cls):
500 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
501
502 def _create_keypair_and_secgroup(self):
503 self.keypair = self.create_keypair()
504 self.secgroup = self.create_security_group()
505 self.create_loginable_secgroup_rule(
506 secgroup_id=self.secgroup['id'])
507 self.create_pingable_secgroup_rule(
508 secgroup_id=self.secgroup['id'])
509
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000510 def _delete_floating_ip(self, fip_address):
511 ip_address = fip_address['floating_ip_address']
512
513 def _fip_is_free():
514 fips = self.os_admin.network_client.list_floatingips()
515 for fip in fips['floatingips']:
516 if ip_address == fip['floating_ip_address']:
517 return False
518 return True
519
520 self.delete_floatingip(fip_address)
521 try:
522 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
523 except common_utils.WaitTimeout:
524 self.fail("Can't reuse IP address %s because it is not free" %
525 ip_address)
526
527 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
528 delete_fip_ids=None):
529 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300530 if fip_addresses:
531 self.assertEqual(servers_num, len(fip_addresses),
532 ('Number of specified fip addresses '
533 'does not match the number of servers'))
534 network = self.create_network()
535 subnet = self.create_subnet(network)
536 router = self.create_router_by_client()
537 self.create_router_interface(router['id'], subnet['id'])
538
539 fips = []
540 for server in range(servers_num):
541 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000542 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300543 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000544 self._create_server_and_fip(network=network,
545 fip_address=fip,
546 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300547 return fips
548
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000549 def _create_server_and_fip(self, network, fip_address=None,
550 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300551 server = self.create_server(
552 flavor_ref=CONF.compute.flavor_ref,
553 image_ref=CONF.compute.image_ref,
554 key_name=self.keypair['name'],
555 networks=[{'uuid': network['id']}],
556 security_groups=[{'name': self.secgroup['name']}])
557 waiters.wait_for_server_status(self.os_primary.servers_client,
558 server['server']['id'],
559 constants.SERVER_STATUS_ACTIVE)
560 port = self.client.list_ports(
561 network_id=network['id'],
562 device_id=server['server']['id'])['ports'][0]
563
564 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000565 if delete_fip_address:
566 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300567 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000568 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300569 client=self.os_admin.network_client,
570 port=port)
571 self.addCleanup(
572 self.delete_floatingip, fip, self.os_admin.network_client)
573 else:
574 fip = self.create_floatingip(port=port)
575 return fip
576
577 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
578 for fip in [mutable_fip, permanent_fip]:
579 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
580 CONF.validation.image_ssh_user,
581 pkey=self.keypair['private_key'])
582 self.check_remote_connectivity(
583 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
584 self.check_remote_connectivity(
585 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
586
587 @testtools.skipUnless(CONF.network.public_network_id,
588 'The public_network_id option must be specified.')
589 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
590 def test_reuse_ip_address_with_other_fip_on_other_router(self):
591 """Reuse IP address by another floating IP on another router
592
593 Scenario:
594 1. Create and connect a router to the external network.
595 2. Create and connect an internal network to the router.
596 3. Create and connect 2 VMs to the internal network.
597 4. Create FIPs in the external network for the VMs.
598 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000599 6. Create and connect one more router to the external network.
600 7. Create and connect an internal network to the second router.
601 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300602 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000603 9. Delete VM2 FIP but save IP address that it used. The FIP is
604 deleted just before the creation of the new IP to "reserve" the
605 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300606 10. Create a FIP for the VM3 in the external network with
Elod Illesf2e985e2023-11-06 19:30:29 +0100607 the same IP address that was used for VM2.
Roman Safronov29c2dff2019-04-02 22:01:23 +0300608 11. Make sure that now VM1 is able to reach VM3 using the FIP.
609
610 Note, the scenario passes only in case corresponding
611 ARP update was sent to the external network when reusing same IP
612 address for another FIP.
613 """
614
615 self._create_keypair_and_secgroup()
616 [mutable_fip, permanent_fip] = (
617 self._create_network_and_servers(servers_num=2))
618 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300619 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000620 servers_num=1, fip_addresses=[mutable_fip],
621 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300622 self._check_fips_connectivity(mutable_fip, permanent_fip)