blob: b031bee5bbb35cad65ae383d971c2015eb72c725 [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
LIU Yulong5ba88ef2017-12-22 10:50:15 +080018from neutron_lib import constants as lib_constants
19from neutron_lib.services.qos import constants as qos_consts
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +000020from neutron_lib.utils import test
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
Ihar Hrachyshkace9c4862018-01-18 18:26:14 +000027import testscenarios
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +090028from testscenarios.scenarios import multiply_scenarios
Roman Safronov29c2dff2019-04-02 22:01:23 +030029import testtools
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090030
LIU Yulong5ba88ef2017-12-22 10:50:15 +080031from neutron_tempest_plugin.api import base as base_api
Chandan Kumar667d3d32017-09-22 12:24:06 +053032from neutron_tempest_plugin.common import ssh
Brian Haleyba800452017-12-14 10:30:48 -050033from neutron_tempest_plugin.common import utils as common_utils
Chandan Kumar667d3d32017-09-22 12:24:06 +053034from neutron_tempest_plugin import config
35from neutron_tempest_plugin.scenario import base
36from neutron_tempest_plugin.scenario import constants
LIU Yulong5ba88ef2017-12-22 10:50:15 +080037from neutron_tempest_plugin.scenario import test_qos
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090038
39
40CONF = config.CONF
elajkat38d90512021-12-13 14:21:30 +010041LOG = log.getLogger(__name__)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090042
43
44load_tests = testscenarios.load_tests_apply_scenarios
45
46
47class FloatingIpTestCasesMixin(object):
48 credentials = ['primary', 'admin']
49
50 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +053051 @utils.requires_ext(extension="router", service="network")
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090052 def resource_setup(cls):
53 super(FloatingIpTestCasesMixin, cls).resource_setup()
54 cls.network = cls.create_network()
55 cls.subnet = cls.create_subnet(cls.network)
56 cls.router = cls.create_router_by_client()
57 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
58 cls.keypair = cls.create_keypair()
59
rajat294495c042017-06-28 15:37:16 +053060 cls.secgroup = cls.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +053061 name=data_utils.rand_name('secgroup'))['security_group']
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090062 cls.security_groups.append(cls.secgroup)
63 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
64 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
65
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090066 if cls.same_network:
67 cls._dest_network = cls.network
68 else:
69 cls._dest_network = cls._create_dest_network()
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090070
71 @classmethod
Itzik Browna31510f2018-01-19 11:09:48 +020072 def _get_external_gateway(cls):
73 if CONF.network.public_network_id:
74 subnets = cls.os_admin.network_client.list_subnets(
75 network_id=CONF.network.public_network_id)
76
77 for subnet in subnets['subnets']:
Brian Haley33ef4602018-04-26 14:37:49 -040078 if (subnet['gateway_ip'] and
79 subnet['ip_version'] == lib_constants.IP_VERSION_4):
Itzik Browna31510f2018-01-19 11:09:48 +020080 return subnet['gateway_ip']
81
82 @classmethod
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090083 def _create_dest_network(cls):
84 network = cls.create_network()
Federico Ressi0ddc93b2018-04-09 12:01:48 +020085 subnet = cls.create_subnet(network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090086 cls.create_router_interface(cls.router['id'], subnet['id'])
87 return network
88
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000089 def _create_server(self, create_floating_ip=True, network=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090090 if network is None:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000091 network = self.network
92 port = self.create_port(network, security_groups=[self.secgroup['id']])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090093 if create_floating_ip:
Federico Ressi3dfa94c2018-07-06 09:46:39 +020094 fip = self.create_floatingip(port=port)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090095 else:
96 fip = None
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000097 server = self.create_server(
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090098 flavor_ref=CONF.compute.flavor_ref,
99 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000100 key_name=self.keypair['name'],
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900101 networks=[{'port': port['id']}])['server']
rajat294495c042017-06-28 15:37:16 +0530102 waiters.wait_for_server_status(self.os_primary.servers_client,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900103 server['id'],
104 constants.SERVER_STATUS_ACTIVE)
105 return {'port': port, 'fip': fip, 'server': server}
106
107 def _test_east_west(self):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900108 # The proxy VM is used to control the source VM when it doesn't
109 # have a floating-ip.
110 if self.src_has_fip:
111 proxy = None
112 proxy_client = None
113 else:
114 proxy = self._create_server()
115 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
116 CONF.validation.image_ssh_user,
117 pkey=self.keypair['private_key'])
118
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900119 # Source VM
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900120 if self.src_has_fip:
121 src_server = self._create_server()
122 src_server_ip = src_server['fip']['floating_ip_address']
123 else:
124 src_server = self._create_server(create_floating_ip=False)
125 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
126 ssh_client = ssh.Client(src_server_ip,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900127 CONF.validation.image_ssh_user,
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900128 pkey=self.keypair['private_key'],
129 proxy_client=proxy_client)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900130
131 # Destination VM
132 if self.dest_has_fip:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000133 dest_server = self._create_server(network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900134 else:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000135 dest_server = self._create_server(create_floating_ip=False,
136 network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900137
138 # Check connectivity
139 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100140 dest_server['port']['fixed_ips'][0]['ip_address'],
141 servers=[src_server, dest_server])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900142 if self.dest_has_fip:
143 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100144 dest_server['fip']['floating_ip_address'],
145 servers=[src_server, dest_server])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900146
147
148class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
149 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900150 scenarios = multiply_scenarios([
151 ('SRC with FIP', dict(src_has_fip=True)),
152 ('SRC without FIP', dict(src_has_fip=False)),
153 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900154 ('DEST with FIP', dict(dest_has_fip=True)),
155 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900156 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900157
158 same_network = True
159
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +0000160 @test.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000161 @decorators.idempotent_id('05c4e3b3-7319-4052-90ad-e8916436c23b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900162 def test_east_west(self):
163 self._test_east_west()
164
165
166class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
167 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900168 scenarios = multiply_scenarios([
169 ('SRC with FIP', dict(src_has_fip=True)),
170 ('SRC without FIP', dict(src_has_fip=False)),
171 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900172 ('DEST with FIP', dict(dest_has_fip=True)),
173 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900174 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900175
176 same_network = False
177
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +0000178 @test.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000179 @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900180 def test_east_west(self):
181 self._test_east_west()
Itzik Browna31510f2018-01-19 11:09:48 +0200182
183
184class DefaultSnatToExternal(FloatingIpTestCasesMixin,
185 base.BaseTempestTestCase):
186 same_network = True
187
188 @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
189 def test_snat_external_ip(self):
190 """Check connectivity to an external IP"""
191 gateway_external_ip = self._get_external_gateway()
192
193 if not gateway_external_ip:
194 raise self.skipTest("IPv4 gateway is not configured for public "
195 "network or public_network_id is not "
196 "configured")
197 proxy = self._create_server()
198 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
199 CONF.validation.image_ssh_user,
200 pkey=self.keypair['private_key'])
201 src_server = self._create_server(create_floating_ip=False)
202 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
203 ssh_client = ssh.Client(src_server_ip,
204 CONF.validation.image_ssh_user,
205 pkey=self.keypair['private_key'],
206 proxy_client=proxy_client)
207 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100208 gateway_external_ip,
209 servers=[proxy, src_server])
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800210
211
Hongbin Lu965b03d2018-04-25 22:32:30 +0000212class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
213 base.BaseTempestTestCase):
214 same_network = True
215
216 @classmethod
217 @utils.requires_ext(extension="router", service="network")
218 @utils.requires_ext(extension="fip-port-details", service="network")
219 def resource_setup(cls):
220 super(FloatingIPPortDetailsTest, cls).resource_setup()
221
222 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
223 def test_floatingip_port_details(self):
224 """Tests the following:
225
226 1. Create a port with floating ip in Neutron.
227 2. Create two servers in Nova.
228 3. Attach the port to the server.
229 4. Detach the port from the server.
230 5. Attach the port to the second server.
231 6. Detach the port from the second server.
232 """
233 port = self.create_port(self.network)
234 fip = self.create_and_associate_floatingip(port['id'])
235 server1 = self._create_server(create_floating_ip=False)
236 server2 = self._create_server(create_floating_ip=False)
237
238 for server in [server1, server2]:
239 # attach the port to the server
240 self.create_interface(
241 server['server']['id'], port_id=port['id'])
242 waiters.wait_for_interface_status(
243 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600244 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000245 fip = self.client.show_floatingip(fip['id'])['floatingip']
Roman Safronov55e4a7f2023-10-31 17:49:35 +0200246 server_data = self.os_admin.servers_client.show_server(
247 server['server']['id'])['server']
248 zone = 'compute:' + server_data['OS-EXT-AZ:availability_zone']
Hongbin Lu965b03d2018-04-25 22:32:30 +0000249 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600250 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Roman Safronov55e4a7f2023-10-31 17:49:35 +0200251 device_id=server['server']['id'],
252 device_owner=zone)
elajkat38d90512021-12-13 14:21:30 +0100253 LOG.debug('Port check for server %s and FIP %s finished, '
254 'lets detach port %s from server!',
255 server['server']['id'], fip['id'], port['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000256
257 # detach the port from the server; this is a cast in the compute
258 # API so we have to poll the port until the device_id is unset.
259 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600260 port = self._wait_for_port_detach(port['id'])
elajkat38d90512021-12-13 14:21:30 +0100261 LOG.debug('Port %s has been detached from server %s, lets check '
262 'the status of port in FIP %s details!',
263 port['id'], server['server']['id'], fip['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600264 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000265 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600266 fip, port, status=lib_constants.PORT_STATUS_DOWN,
267 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000268
269 def _check_port_details(self, fip, port, status, device_id, device_owner):
270 self.assertIn('port_details', fip)
271 port_details = fip['port_details']
272 self.assertEqual(port['name'], port_details['name'])
273 self.assertEqual(port['network_id'], port_details['network_id'])
274 self.assertEqual(port['mac_address'], port_details['mac_address'])
275 self.assertEqual(port['admin_state_up'],
276 port_details['admin_state_up'])
277 self.assertEqual(status, port_details['status'])
278 self.assertEqual(device_id, port_details['device_id'])
279 self.assertEqual(device_owner, port_details['device_owner'])
280
281 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
282 """Waits for the port's device_id to be unset.
283
284 :param port_id: The id of the port being detached.
285 :returns: The final port dict from the show_port response.
286 """
287 port = self.client.show_port(port_id)['port']
288 device_id = port['device_id']
289 start = int(time.time())
290
291 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
292 # None, but it's not contractual so handle Falsey either way.
293 while device_id:
294 time.sleep(interval)
295 port = self.client.show_port(port_id)['port']
296 device_id = port['device_id']
297
298 timed_out = int(time.time()) - start >= timeout
299
300 if device_id and timed_out:
301 message = ('Port %s failed to detach (device_id %s) within '
302 'the required time (%s s).' %
303 (port_id, device_id, timeout))
304 raise exceptions.TimeoutException(message)
305
306 return port
307
Brian Haleyaf347da2018-09-14 11:24:00 -0600308 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
309 """Waits for the fip's attached port status to be 'DOWN'.
310
311 :param fip_id: The id of the floating IP.
312 :returns: The final fip dict from the show_floatingip response.
313 """
314 fip = self.client.show_floatingip(fip_id)['floatingip']
315 self.assertIn('port_details', fip)
316 port_details = fip['port_details']
317 status = port_details['status']
318 start = int(time.time())
319
320 while status != lib_constants.PORT_STATUS_DOWN:
321 time.sleep(interval)
322 fip = self.client.show_floatingip(fip_id)['floatingip']
323 self.assertIn('port_details', fip)
324 port_details = fip['port_details']
325 status = port_details['status']
326
327 timed_out = int(time.time()) - start >= timeout
328
329 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100330 port_id = fip.get("port_id")
331 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600332 message = ('Floating IP %s attached port status failed to '
333 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100334 'the required time (%s s). Port details: %s' %
335 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600336 raise exceptions.TimeoutException(message)
337
elajkat38d90512021-12-13 14:21:30 +0100338 LOG.debug('Port %s attached to FIP %s is down after %s!',
339 fip.get("port_id"), fip_id, int(time.time()) - start)
Brian Haleyaf347da2018-09-14 11:24:00 -0600340 return fip
341
Hongbin Lu965b03d2018-04-25 22:32:30 +0000342
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800343class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900344 test_qos.QoSTestMixin,
345 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800346
347 same_network = True
348
349 @classmethod
350 @utils.requires_ext(extension="router", service="network")
351 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900352 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800353 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
354 def resource_setup(cls):
355 super(FloatingIPQosTest, cls).resource_setup()
356
Eduardo Olivares98772912021-02-19 12:36:21 +0100357 @classmethod
358 def setup_clients(cls):
359 super(FloatingIPQosTest, cls).setup_clients()
360 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300361 cls.qos_bw_limit_rule_client = \
362 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100363
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800364 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
365 def test_qos(self):
366 """Test floating IP is binding to a QoS policy with
367
368 ingress and egress bandwidth limit rules. And it applied correctly
369 by sending a file from the instance to the test node.
370 Then calculating the bandwidth every ~1 sec by the number of bits
371 received / elapsed time.
372 """
373
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200374 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
375
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800376 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100377
378 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800379 policy_id = self._create_qos_policy()
380 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100381
382 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300383 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
384 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
385 'direction': lib_constants.INGRESS_DIRECTION}
386 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
387 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800388
zahlabut7ebb66e2021-09-01 22:39:49 +0300389 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
390 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
391 'direction': lib_constants.EGRESS_DIRECTION}
392 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
393 qos_policy_id=policy_id, **rule_data)
394
395 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800396 policy_id)
397 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
398
399 fip = self.os_admin.network_client.get_floatingip(
400 self.fip['id'])['floatingip']
401 self.assertEqual(self.port['id'], fip['port_id'])
402
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100403 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800404 self.os_admin.network_client.update_floatingip(
405 self.fip['id'],
406 qos_policy_id=policy_id)
407
408 fip = self.os_admin.network_client.get_floatingip(
409 self.fip['id'])['floatingip']
410 self.assertEqual(policy_id, fip['qos_policy_id'])
411
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100412 # Basic test, Check that actual BW while downloading file
413 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800414 common_utils.wait_until_true(lambda: self._check_bw(
415 ssh_client,
416 self.fip['floating_ip_address'],
417 port=self.NC_PORT),
418 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100419 sleep=1,
420 exception=RuntimeError(
421 'Failed scenario: "Create a QoS policy associated with FIP" '
422 'Actual BW is not as expected!'))
423
424 # As admin user update QoS rules
425 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300426 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
427 policy_id, rule['id'],
428 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
429 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100430
431 # Check that actual BW while downloading file
432 # is as expected (Update BW)
433 common_utils.wait_until_true(lambda: self._check_bw(
434 ssh_client,
435 self.fip['floating_ip_address'],
436 port=self.NC_PORT,
437 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
438 timeout=120,
439 sleep=1,
440 exception=RuntimeError(
441 'Failed scenario: "Update QoS policy associated with FIP" '
442 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000443
444
445class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
446 base.BaseTempestTestCase):
447
448 same_network = None
449
450 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
451 def test_floating_ip_update(self):
452 """Test updating FIP with another port.
453
454 The test creates two servers and attaches floating ip to first server.
455 Then it checks server is accesible using the FIP. FIP is then
456 associated with the second server and connectivity is checked again.
457 """
458 ports = [self.create_port(
459 self.network, security_groups=[self.secgroup['id']])
460 for i in range(2)]
461
462 servers = []
463 for port in ports:
464 name = data_utils.rand_name("server-%s" % port['id'][:8])
465 server = self.create_server(
466 name=name,
467 flavor_ref=CONF.compute.flavor_ref,
468 key_name=self.keypair['name'],
469 image_ref=CONF.compute.image_ref,
470 networks=[{'port': port['id']}])['server']
471 server['name'] = name
472 servers.append(server)
473 for server in servers:
474 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200475 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000476
477 self.fip = self.create_floatingip(port=ports[0])
478 self.check_connectivity(self.fip['floating_ip_address'],
479 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100480 self.keypair['private_key'],
481 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000482 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
483
484 def _wait_for_fip_associated():
485 try:
486 self.check_servers_hostnames(servers[-1:], log_errors=False)
Vasyl Saienko8512fb92021-12-22 11:54:04 +0200487 # NOTE(vsaienko): it might take some time by neutron to update VIP
488 # retry on any exception here.
489 except Exception:
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000490 return False
491 return True
492
493 # The FIP is now associated with the port of the second server.
494 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200495 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000496 except common_utils.WaitTimeout:
497 self._log_console_output(servers[-1:])
498 self.fail(
499 "Server %s is not accessible via its floating ip %s" % (
500 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300501
502
503class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
504 credentials = ['primary', 'admin']
505
506 @classmethod
507 @utils.requires_ext(extension="router", service="network")
508 def skip_checks(cls):
509 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
510
511 def _create_keypair_and_secgroup(self):
512 self.keypair = self.create_keypair()
513 self.secgroup = self.create_security_group()
514 self.create_loginable_secgroup_rule(
515 secgroup_id=self.secgroup['id'])
516 self.create_pingable_secgroup_rule(
517 secgroup_id=self.secgroup['id'])
518
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000519 def _delete_floating_ip(self, fip_address):
520 ip_address = fip_address['floating_ip_address']
521
522 def _fip_is_free():
523 fips = self.os_admin.network_client.list_floatingips()
524 for fip in fips['floatingips']:
525 if ip_address == fip['floating_ip_address']:
526 return False
527 return True
528
529 self.delete_floatingip(fip_address)
530 try:
531 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
532 except common_utils.WaitTimeout:
533 self.fail("Can't reuse IP address %s because it is not free" %
534 ip_address)
535
536 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
537 delete_fip_ids=None):
538 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300539 if fip_addresses:
540 self.assertEqual(servers_num, len(fip_addresses),
541 ('Number of specified fip addresses '
542 'does not match the number of servers'))
543 network = self.create_network()
544 subnet = self.create_subnet(network)
545 router = self.create_router_by_client()
546 self.create_router_interface(router['id'], subnet['id'])
547
548 fips = []
549 for server in range(servers_num):
550 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000551 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300552 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000553 self._create_server_and_fip(network=network,
554 fip_address=fip,
555 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300556 return fips
557
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000558 def _create_server_and_fip(self, network, fip_address=None,
559 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300560 server = self.create_server(
561 flavor_ref=CONF.compute.flavor_ref,
562 image_ref=CONF.compute.image_ref,
563 key_name=self.keypair['name'],
564 networks=[{'uuid': network['id']}],
565 security_groups=[{'name': self.secgroup['name']}])
566 waiters.wait_for_server_status(self.os_primary.servers_client,
567 server['server']['id'],
568 constants.SERVER_STATUS_ACTIVE)
569 port = self.client.list_ports(
570 network_id=network['id'],
571 device_id=server['server']['id'])['ports'][0]
572
573 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000574 if delete_fip_address:
575 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300576 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000577 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300578 client=self.os_admin.network_client,
579 port=port)
580 self.addCleanup(
581 self.delete_floatingip, fip, self.os_admin.network_client)
582 else:
583 fip = self.create_floatingip(port=port)
584 return fip
585
586 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
587 for fip in [mutable_fip, permanent_fip]:
588 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
589 CONF.validation.image_ssh_user,
590 pkey=self.keypair['private_key'])
591 self.check_remote_connectivity(
592 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
593 self.check_remote_connectivity(
594 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
595
596 @testtools.skipUnless(CONF.network.public_network_id,
597 'The public_network_id option must be specified.')
598 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
599 def test_reuse_ip_address_with_other_fip_on_other_router(self):
600 """Reuse IP address by another floating IP on another router
601
602 Scenario:
603 1. Create and connect a router to the external network.
604 2. Create and connect an internal network to the router.
605 3. Create and connect 2 VMs to the internal network.
606 4. Create FIPs in the external network for the VMs.
607 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000608 6. Create and connect one more router to the external network.
609 7. Create and connect an internal network to the second router.
610 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300611 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000612 9. Delete VM2 FIP but save IP address that it used. The FIP is
613 deleted just before the creation of the new IP to "reserve" the
614 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300615 10. Create a FIP for the VM3 in the external network with
616 the same IP address that was used for VM2.
617 11. Make sure that now VM1 is able to reach VM3 using the FIP.
618
619 Note, the scenario passes only in case corresponding
620 ARP update was sent to the external network when reusing same IP
621 address for another FIP.
622 """
623
624 self._create_keypair_and_secgroup()
625 [mutable_fip, permanent_fip] = (
626 self._create_network_and_servers(servers_num=2))
627 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300628 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000629 servers_num=1, fip_addresses=[mutable_fip],
630 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300631 self._check_fips_connectivity(mutable_fip, permanent_fip)