blob: a5afc73fc89e0961737916f75074bdf5ae0cf281 [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
Chandan Kumarc125fd12017-11-15 19:41:01 +053021from tempest.common import utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090022from tempest.common import waiters
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090023from tempest.lib.common.utils import data_utils
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +000024from tempest.lib import decorators
Slawek Kaplonski168e5012018-10-04 14:31:19 +020025from tempest.lib import exceptions
Ihar Hrachyshkace9c4862018-01-18 18:26:14 +000026import testscenarios
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +090027from testscenarios.scenarios import multiply_scenarios
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
40
41
42load_tests = testscenarios.load_tests_apply_scenarios
43
44
45class FloatingIpTestCasesMixin(object):
46 credentials = ['primary', 'admin']
47
48 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +053049 @utils.requires_ext(extension="router", service="network")
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090050 def resource_setup(cls):
51 super(FloatingIpTestCasesMixin, cls).resource_setup()
52 cls.network = cls.create_network()
53 cls.subnet = cls.create_subnet(cls.network)
54 cls.router = cls.create_router_by_client()
55 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
56 cls.keypair = cls.create_keypair()
57
rajat294495c042017-06-28 15:37:16 +053058 cls.secgroup = cls.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +053059 name=data_utils.rand_name('secgroup'))['security_group']
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090060 cls.security_groups.append(cls.secgroup)
61 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
62 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
63
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090064 if cls.same_network:
65 cls._dest_network = cls.network
66 else:
67 cls._dest_network = cls._create_dest_network()
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090068
69 @classmethod
Itzik Browna31510f2018-01-19 11:09:48 +020070 def _get_external_gateway(cls):
71 if CONF.network.public_network_id:
72 subnets = cls.os_admin.network_client.list_subnets(
73 network_id=CONF.network.public_network_id)
74
75 for subnet in subnets['subnets']:
Brian Haley33ef4602018-04-26 14:37:49 -040076 if (subnet['gateway_ip'] and
77 subnet['ip_version'] == lib_constants.IP_VERSION_4):
Itzik Browna31510f2018-01-19 11:09:48 +020078 return subnet['gateway_ip']
79
80 @classmethod
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090081 def _create_dest_network(cls):
82 network = cls.create_network()
Federico Ressi0ddc93b2018-04-09 12:01:48 +020083 subnet = cls.create_subnet(network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090084 cls.create_router_interface(cls.router['id'], subnet['id'])
85 return network
86
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000087 def _create_server(self, create_floating_ip=True, network=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090088 if network is None:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000089 network = self.network
90 port = self.create_port(network, security_groups=[self.secgroup['id']])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090091 if create_floating_ip:
Federico Ressi3dfa94c2018-07-06 09:46:39 +020092 fip = self.create_floatingip(port=port)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090093 else:
94 fip = None
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000095 server = self.create_server(
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090096 flavor_ref=CONF.compute.flavor_ref,
97 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000098 key_name=self.keypair['name'],
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090099 networks=[{'port': port['id']}])['server']
rajat294495c042017-06-28 15:37:16 +0530100 waiters.wait_for_server_status(self.os_primary.servers_client,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900101 server['id'],
102 constants.SERVER_STATUS_ACTIVE)
103 return {'port': port, 'fip': fip, 'server': server}
104
105 def _test_east_west(self):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900106 # The proxy VM is used to control the source VM when it doesn't
107 # have a floating-ip.
108 if self.src_has_fip:
109 proxy = None
110 proxy_client = None
111 else:
112 proxy = self._create_server()
113 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
114 CONF.validation.image_ssh_user,
115 pkey=self.keypair['private_key'])
116
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900117 # Source VM
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900118 if self.src_has_fip:
119 src_server = self._create_server()
120 src_server_ip = src_server['fip']['floating_ip_address']
121 else:
122 src_server = self._create_server(create_floating_ip=False)
123 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
124 ssh_client = ssh.Client(src_server_ip,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900125 CONF.validation.image_ssh_user,
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900126 pkey=self.keypair['private_key'],
127 proxy_client=proxy_client)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900128
129 # Destination VM
130 if self.dest_has_fip:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000131 dest_server = self._create_server(network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900132 else:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000133 dest_server = self._create_server(create_floating_ip=False,
134 network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900135
136 # Check connectivity
137 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100138 dest_server['port']['fixed_ips'][0]['ip_address'],
139 servers=[src_server, dest_server])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900140 if self.dest_has_fip:
141 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100142 dest_server['fip']['floating_ip_address'],
143 servers=[src_server, dest_server])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900144
145
146class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
147 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900148 scenarios = multiply_scenarios([
149 ('SRC with FIP', dict(src_has_fip=True)),
150 ('SRC without FIP', dict(src_has_fip=False)),
151 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900152 ('DEST with FIP', dict(dest_has_fip=True)),
153 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900154 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900155
156 same_network = True
157
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +0000158 @test.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000159 @decorators.idempotent_id('05c4e3b3-7319-4052-90ad-e8916436c23b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900160 def test_east_west(self):
161 self._test_east_west()
162
163
164class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
165 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900166 scenarios = multiply_scenarios([
167 ('SRC with FIP', dict(src_has_fip=True)),
168 ('SRC without FIP', dict(src_has_fip=False)),
169 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900170 ('DEST with FIP', dict(dest_has_fip=True)),
171 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900172 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900173
174 same_network = False
175
Rodolfo Alonso Hernandez4dea8062020-01-16 16:32:59 +0000176 @test.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000177 @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900178 def test_east_west(self):
179 self._test_east_west()
Itzik Browna31510f2018-01-19 11:09:48 +0200180
181
182class DefaultSnatToExternal(FloatingIpTestCasesMixin,
183 base.BaseTempestTestCase):
184 same_network = True
185
186 @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
187 def test_snat_external_ip(self):
188 """Check connectivity to an external IP"""
189 gateway_external_ip = self._get_external_gateway()
190
191 if not gateway_external_ip:
192 raise self.skipTest("IPv4 gateway is not configured for public "
193 "network or public_network_id is not "
194 "configured")
195 proxy = self._create_server()
196 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
197 CONF.validation.image_ssh_user,
198 pkey=self.keypair['private_key'])
199 src_server = self._create_server(create_floating_ip=False)
200 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
201 ssh_client = ssh.Client(src_server_ip,
202 CONF.validation.image_ssh_user,
203 pkey=self.keypair['private_key'],
204 proxy_client=proxy_client)
205 self.check_remote_connectivity(ssh_client,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100206 gateway_external_ip,
207 servers=[proxy, src_server])
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800208
209
Hongbin Lu965b03d2018-04-25 22:32:30 +0000210class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
211 base.BaseTempestTestCase):
212 same_network = True
213
214 @classmethod
215 @utils.requires_ext(extension="router", service="network")
216 @utils.requires_ext(extension="fip-port-details", service="network")
217 def resource_setup(cls):
218 super(FloatingIPPortDetailsTest, cls).resource_setup()
219
220 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
221 def test_floatingip_port_details(self):
222 """Tests the following:
223
224 1. Create a port with floating ip in Neutron.
225 2. Create two servers in Nova.
226 3. Attach the port to the server.
227 4. Detach the port from the server.
228 5. Attach the port to the second server.
229 6. Detach the port from the second server.
230 """
231 port = self.create_port(self.network)
232 fip = self.create_and_associate_floatingip(port['id'])
233 server1 = self._create_server(create_floating_ip=False)
234 server2 = self._create_server(create_floating_ip=False)
235
236 for server in [server1, server2]:
237 # attach the port to the server
238 self.create_interface(
239 server['server']['id'], port_id=port['id'])
240 waiters.wait_for_interface_status(
241 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600242 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000243 fip = self.client.show_floatingip(fip['id'])['floatingip']
244 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600245 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Hongbin Lu965b03d2018-04-25 22:32:30 +0000246 device_id=server['server']['id'], device_owner='compute:nova')
247
248 # detach the port from the server; this is a cast in the compute
249 # API so we have to poll the port until the device_id is unset.
250 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600251 port = self._wait_for_port_detach(port['id'])
252 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000253 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600254 fip, port, status=lib_constants.PORT_STATUS_DOWN,
255 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000256
257 def _check_port_details(self, fip, port, status, device_id, device_owner):
258 self.assertIn('port_details', fip)
259 port_details = fip['port_details']
260 self.assertEqual(port['name'], port_details['name'])
261 self.assertEqual(port['network_id'], port_details['network_id'])
262 self.assertEqual(port['mac_address'], port_details['mac_address'])
263 self.assertEqual(port['admin_state_up'],
264 port_details['admin_state_up'])
265 self.assertEqual(status, port_details['status'])
266 self.assertEqual(device_id, port_details['device_id'])
267 self.assertEqual(device_owner, port_details['device_owner'])
268
269 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
270 """Waits for the port's device_id to be unset.
271
272 :param port_id: The id of the port being detached.
273 :returns: The final port dict from the show_port response.
274 """
275 port = self.client.show_port(port_id)['port']
276 device_id = port['device_id']
277 start = int(time.time())
278
279 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
280 # None, but it's not contractual so handle Falsey either way.
281 while device_id:
282 time.sleep(interval)
283 port = self.client.show_port(port_id)['port']
284 device_id = port['device_id']
285
286 timed_out = int(time.time()) - start >= timeout
287
288 if device_id and timed_out:
289 message = ('Port %s failed to detach (device_id %s) within '
290 'the required time (%s s).' %
291 (port_id, device_id, timeout))
292 raise exceptions.TimeoutException(message)
293
294 return port
295
Brian Haleyaf347da2018-09-14 11:24:00 -0600296 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
297 """Waits for the fip's attached port status to be 'DOWN'.
298
299 :param fip_id: The id of the floating IP.
300 :returns: The final fip dict from the show_floatingip response.
301 """
302 fip = self.client.show_floatingip(fip_id)['floatingip']
303 self.assertIn('port_details', fip)
304 port_details = fip['port_details']
305 status = port_details['status']
306 start = int(time.time())
307
308 while status != lib_constants.PORT_STATUS_DOWN:
309 time.sleep(interval)
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
315 timed_out = int(time.time()) - start >= timeout
316
317 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100318 port_id = fip.get("port_id")
319 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600320 message = ('Floating IP %s attached port status failed to '
321 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100322 'the required time (%s s). Port details: %s' %
323 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600324 raise exceptions.TimeoutException(message)
325
326 return fip
327
Hongbin Lu965b03d2018-04-25 22:32:30 +0000328
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800329class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900330 test_qos.QoSTestMixin,
331 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800332
333 same_network = True
334
335 @classmethod
336 @utils.requires_ext(extension="router", service="network")
337 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900338 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800339 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
340 def resource_setup(cls):
341 super(FloatingIPQosTest, cls).resource_setup()
342
Eduardo Olivares98772912021-02-19 12:36:21 +0100343 @classmethod
344 def setup_clients(cls):
345 super(FloatingIPQosTest, cls).setup_clients()
346 cls.admin_client = cls.os_admin.network_client
zahlabut7ebb66e2021-09-01 22:39:49 +0300347 cls.qos_bw_limit_rule_client = \
348 cls.os_admin.qos_limit_bandwidth_rules_client
Eduardo Olivares98772912021-02-19 12:36:21 +0100349
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800350 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
351 def test_qos(self):
352 """Test floating IP is binding to a QoS policy with
353
354 ingress and egress bandwidth limit rules. And it applied correctly
355 by sending a file from the instance to the test node.
356 Then calculating the bandwidth every ~1 sec by the number of bits
357 received / elapsed time.
358 """
359
Slawek Kaplonski71a462b2020-10-21 12:59:18 +0200360 self.skip_if_no_extension_enabled_in_l3_agents("fip_qos")
361
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800362 self._test_basic_resources()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100363
364 # Create a new QoS policy
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800365 policy_id = self._create_qos_policy()
366 ssh_client = self._create_ssh_client()
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100367
368 # As admin user create a new QoS rules
zahlabut7ebb66e2021-09-01 22:39:49 +0300369 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
370 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
371 'direction': lib_constants.INGRESS_DIRECTION}
372 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
373 qos_policy_id=policy_id, **rule_data)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800374
zahlabut7ebb66e2021-09-01 22:39:49 +0300375 rule_data = {'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND,
376 'max_burst_kbps': constants.LIMIT_KILO_BYTES,
377 'direction': lib_constants.EGRESS_DIRECTION}
378 self.qos_bw_limit_rule_client.create_limit_bandwidth_rule(
379 qos_policy_id=policy_id, **rule_data)
380
381 rules = self.qos_bw_limit_rule_client.list_limit_bandwidth_rules(
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800382 policy_id)
383 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
384
385 fip = self.os_admin.network_client.get_floatingip(
386 self.fip['id'])['floatingip']
387 self.assertEqual(self.port['id'], fip['port_id'])
388
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100389 # Associate QoS to the FIP
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800390 self.os_admin.network_client.update_floatingip(
391 self.fip['id'],
392 qos_policy_id=policy_id)
393
394 fip = self.os_admin.network_client.get_floatingip(
395 self.fip['id'])['floatingip']
396 self.assertEqual(policy_id, fip['qos_policy_id'])
397
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100398 # Basic test, Check that actual BW while downloading file
399 # is as expected (Original BW)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800400 common_utils.wait_until_true(lambda: self._check_bw(
401 ssh_client,
402 self.fip['floating_ip_address'],
403 port=self.NC_PORT),
404 timeout=120,
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100405 sleep=1,
406 exception=RuntimeError(
407 'Failed scenario: "Create a QoS policy associated with FIP" '
408 'Actual BW is not as expected!'))
409
410 # As admin user update QoS rules
411 for rule in rules['bandwidth_limit_rules']:
zahlabut7ebb66e2021-09-01 22:39:49 +0300412 self.qos_bw_limit_rule_client.update_limit_bandwidth_rule(
413 policy_id, rule['id'],
414 **{'max_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2,
415 'max_burst_kbps': constants.LIMIT_KILO_BITS_PER_SECOND * 2})
Eduardo Olivaresf5a40d92021-02-23 09:17:49 +0100416
417 # Check that actual BW while downloading file
418 # is as expected (Update BW)
419 common_utils.wait_until_true(lambda: self._check_bw(
420 ssh_client,
421 self.fip['floating_ip_address'],
422 port=self.NC_PORT,
423 expected_bw=test_qos.QoSTestMixin.LIMIT_BYTES_SEC * 2),
424 timeout=120,
425 sleep=1,
426 exception=RuntimeError(
427 'Failed scenario: "Update QoS policy associated with FIP" '
428 'Actual BW is not as expected!'))
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000429
430
431class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
432 base.BaseTempestTestCase):
433
434 same_network = None
435
436 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
437 def test_floating_ip_update(self):
438 """Test updating FIP with another port.
439
440 The test creates two servers and attaches floating ip to first server.
441 Then it checks server is accesible using the FIP. FIP is then
442 associated with the second server and connectivity is checked again.
443 """
444 ports = [self.create_port(
445 self.network, security_groups=[self.secgroup['id']])
446 for i in range(2)]
447
448 servers = []
449 for port in ports:
450 name = data_utils.rand_name("server-%s" % port['id'][:8])
451 server = self.create_server(
452 name=name,
453 flavor_ref=CONF.compute.flavor_ref,
454 key_name=self.keypair['name'],
455 image_ref=CONF.compute.image_ref,
456 networks=[{'port': port['id']}])['server']
457 server['name'] = name
458 servers.append(server)
459 for server in servers:
460 self.wait_for_server_active(server)
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200461 self.wait_for_guest_os_ready(server)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000462
463 self.fip = self.create_floatingip(port=ports[0])
464 self.check_connectivity(self.fip['floating_ip_address'],
465 CONF.validation.image_ssh_user,
Slawek Kaplonskie58219b2019-12-09 12:10:55 +0100466 self.keypair['private_key'],
467 servers=servers)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000468 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
469
470 def _wait_for_fip_associated():
471 try:
472 self.check_servers_hostnames(servers[-1:], log_errors=False)
473 except (AssertionError, exceptions.SSHTimeout):
474 return False
475 return True
476
477 # The FIP is now associated with the port of the second server.
478 try:
Slawek Kaplonski0e9edc52020-09-30 16:54:13 +0200479 common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000480 except common_utils.WaitTimeout:
481 self._log_console_output(servers[-1:])
482 self.fail(
483 "Server %s is not accessible via its floating ip %s" % (
484 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300485
486
487class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
488 credentials = ['primary', 'admin']
489
490 @classmethod
491 @utils.requires_ext(extension="router", service="network")
492 def skip_checks(cls):
493 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
494
495 def _create_keypair_and_secgroup(self):
496 self.keypair = self.create_keypair()
497 self.secgroup = self.create_security_group()
498 self.create_loginable_secgroup_rule(
499 secgroup_id=self.secgroup['id'])
500 self.create_pingable_secgroup_rule(
501 secgroup_id=self.secgroup['id'])
502
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000503 def _delete_floating_ip(self, fip_address):
504 ip_address = fip_address['floating_ip_address']
505
506 def _fip_is_free():
507 fips = self.os_admin.network_client.list_floatingips()
508 for fip in fips['floatingips']:
509 if ip_address == fip['floating_ip_address']:
510 return False
511 return True
512
513 self.delete_floatingip(fip_address)
514 try:
515 common_utils.wait_until_true(_fip_is_free, timeout=30, sleep=5)
516 except common_utils.WaitTimeout:
517 self.fail("Can't reuse IP address %s because it is not free" %
518 ip_address)
519
520 def _create_network_and_servers(self, servers_num=1, fip_addresses=None,
521 delete_fip_ids=None):
522 delete_fip_ids = delete_fip_ids or []
Roman Safronov29c2dff2019-04-02 22:01:23 +0300523 if fip_addresses:
524 self.assertEqual(servers_num, len(fip_addresses),
525 ('Number of specified fip addresses '
526 'does not match the number of servers'))
527 network = self.create_network()
528 subnet = self.create_subnet(network)
529 router = self.create_router_by_client()
530 self.create_router_interface(router['id'], subnet['id'])
531
532 fips = []
533 for server in range(servers_num):
534 fip = fip_addresses[server] if fip_addresses else None
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000535 delete_fip = fip['id'] in delete_fip_ids if fip else False
Roman Safronov29c2dff2019-04-02 22:01:23 +0300536 fips.append(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000537 self._create_server_and_fip(network=network,
538 fip_address=fip,
539 delete_fip_address=delete_fip))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300540 return fips
541
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000542 def _create_server_and_fip(self, network, fip_address=None,
543 delete_fip_address=False):
Roman Safronov29c2dff2019-04-02 22:01:23 +0300544 server = self.create_server(
545 flavor_ref=CONF.compute.flavor_ref,
546 image_ref=CONF.compute.image_ref,
547 key_name=self.keypair['name'],
548 networks=[{'uuid': network['id']}],
549 security_groups=[{'name': self.secgroup['name']}])
550 waiters.wait_for_server_status(self.os_primary.servers_client,
551 server['server']['id'],
552 constants.SERVER_STATUS_ACTIVE)
553 port = self.client.list_ports(
554 network_id=network['id'],
555 device_id=server['server']['id'])['ports'][0]
556
557 if fip_address:
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000558 if delete_fip_address:
559 self._delete_floating_ip(fip_address)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300560 fip = self.create_floatingip(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000561 floating_ip_address=fip_address['floating_ip_address'],
Roman Safronov29c2dff2019-04-02 22:01:23 +0300562 client=self.os_admin.network_client,
563 port=port)
564 self.addCleanup(
565 self.delete_floatingip, fip, self.os_admin.network_client)
566 else:
567 fip = self.create_floatingip(port=port)
568 return fip
569
570 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
571 for fip in [mutable_fip, permanent_fip]:
572 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
573 CONF.validation.image_ssh_user,
574 pkey=self.keypair['private_key'])
575 self.check_remote_connectivity(
576 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
577 self.check_remote_connectivity(
578 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
579
580 @testtools.skipUnless(CONF.network.public_network_id,
581 'The public_network_id option must be specified.')
582 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
583 def test_reuse_ip_address_with_other_fip_on_other_router(self):
584 """Reuse IP address by another floating IP on another router
585
586 Scenario:
587 1. Create and connect a router to the external network.
588 2. Create and connect an internal network to the router.
589 3. Create and connect 2 VMs to the internal network.
590 4. Create FIPs in the external network for the VMs.
591 5. Make sure that VM1 can ping VM2 FIP address.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000592 6. Create and connect one more router to the external network.
593 7. Create and connect an internal network to the second router.
594 8. Create and connect a VM (VM3) to the internal network of
Roman Safronov29c2dff2019-04-02 22:01:23 +0300595 the second router.
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000596 9. Delete VM2 FIP but save IP address that it used. The FIP is
597 deleted just before the creation of the new IP to "reserve" the
598 IP address associated (see LP#1880976).
Roman Safronov29c2dff2019-04-02 22:01:23 +0300599 10. Create a FIP for the VM3 in the external network with
600 the same IP address that was used for VM2.
601 11. Make sure that now VM1 is able to reach VM3 using the FIP.
602
603 Note, the scenario passes only in case corresponding
604 ARP update was sent to the external network when reusing same IP
605 address for another FIP.
606 """
607
608 self._create_keypair_and_secgroup()
609 [mutable_fip, permanent_fip] = (
610 self._create_network_and_servers(servers_num=2))
611 self._check_fips_connectivity(mutable_fip, permanent_fip)
Roman Safronov29c2dff2019-04-02 22:01:23 +0300612 [mutable_fip] = self._create_network_and_servers(
Rodolfo Alonso Hernandez71583982020-05-27 16:25:59 +0000613 servers_num=1, fip_addresses=[mutable_fip],
614 delete_fip_ids=[mutable_fip['id']])
Roman Safronov29c2dff2019-04-02 22:01:23 +0300615 self._check_fips_connectivity(mutable_fip, permanent_fip)