blob: 8b8a4a1bcf4455baa0cc1874aa5d825583455114 [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
Chandan Kumarc125fd12017-11-15 19:41:01 +053020from tempest.common import utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090021from tempest.common import waiters
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090022from tempest.lib.common.utils import data_utils
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +000023from tempest.lib import decorators
Slawek Kaplonski168e5012018-10-04 14:31:19 +020024from tempest.lib import exceptions
Ihar Hrachyshkace9c4862018-01-18 18:26:14 +000025import testscenarios
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +090026from testscenarios.scenarios import multiply_scenarios
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
39
40
41load_tests = testscenarios.load_tests_apply_scenarios
42
43
44class FloatingIpTestCasesMixin(object):
45 credentials = ['primary', 'admin']
46
47 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +053048 @utils.requires_ext(extension="router", service="network")
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090049 def resource_setup(cls):
50 super(FloatingIpTestCasesMixin, cls).resource_setup()
51 cls.network = cls.create_network()
52 cls.subnet = cls.create_subnet(cls.network)
53 cls.router = cls.create_router_by_client()
54 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
55 cls.keypair = cls.create_keypair()
56
rajat294495c042017-06-28 15:37:16 +053057 cls.secgroup = cls.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +053058 name=data_utils.rand_name('secgroup'))['security_group']
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090059 cls.security_groups.append(cls.secgroup)
60 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
61 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
62
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090063 if cls.same_network:
64 cls._dest_network = cls.network
65 else:
66 cls._dest_network = cls._create_dest_network()
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090067
68 @classmethod
Itzik Browna31510f2018-01-19 11:09:48 +020069 def _get_external_gateway(cls):
70 if CONF.network.public_network_id:
71 subnets = cls.os_admin.network_client.list_subnets(
72 network_id=CONF.network.public_network_id)
73
74 for subnet in subnets['subnets']:
Brian Haley33ef4602018-04-26 14:37:49 -040075 if (subnet['gateway_ip'] and
76 subnet['ip_version'] == lib_constants.IP_VERSION_4):
Itzik Browna31510f2018-01-19 11:09:48 +020077 return subnet['gateway_ip']
78
79 @classmethod
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090080 def _create_dest_network(cls):
81 network = cls.create_network()
Federico Ressi0ddc93b2018-04-09 12:01:48 +020082 subnet = cls.create_subnet(network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090083 cls.create_router_interface(cls.router['id'], subnet['id'])
84 return network
85
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000086 def _create_server(self, create_floating_ip=True, network=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090087 if network is None:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000088 network = self.network
89 port = self.create_port(network, security_groups=[self.secgroup['id']])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090090 if create_floating_ip:
Federico Ressi3dfa94c2018-07-06 09:46:39 +020091 fip = self.create_floatingip(port=port)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090092 else:
93 fip = None
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000094 server = self.create_server(
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090095 flavor_ref=CONF.compute.flavor_ref,
96 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000097 key_name=self.keypair['name'],
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090098 networks=[{'port': port['id']}])['server']
rajat294495c042017-06-28 15:37:16 +053099 waiters.wait_for_server_status(self.os_primary.servers_client,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900100 server['id'],
101 constants.SERVER_STATUS_ACTIVE)
102 return {'port': port, 'fip': fip, 'server': server}
103
104 def _test_east_west(self):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900105 # The proxy VM is used to control the source VM when it doesn't
106 # have a floating-ip.
107 if self.src_has_fip:
108 proxy = None
109 proxy_client = None
110 else:
111 proxy = self._create_server()
112 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
113 CONF.validation.image_ssh_user,
114 pkey=self.keypair['private_key'])
115
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900116 # Source VM
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900117 if self.src_has_fip:
118 src_server = self._create_server()
119 src_server_ip = src_server['fip']['floating_ip_address']
120 else:
121 src_server = self._create_server(create_floating_ip=False)
122 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
123 ssh_client = ssh.Client(src_server_ip,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900124 CONF.validation.image_ssh_user,
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900125 pkey=self.keypair['private_key'],
126 proxy_client=proxy_client)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900127
128 # Destination VM
129 if self.dest_has_fip:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000130 dest_server = self._create_server(network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900131 else:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000132 dest_server = self._create_server(create_floating_ip=False,
133 network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900134
135 # Check connectivity
136 self.check_remote_connectivity(ssh_client,
137 dest_server['port']['fixed_ips'][0]['ip_address'])
138 if self.dest_has_fip:
139 self.check_remote_connectivity(ssh_client,
140 dest_server['fip']['floating_ip_address'])
141
142
143class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
144 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900145 scenarios = multiply_scenarios([
146 ('SRC with FIP', dict(src_has_fip=True)),
147 ('SRC without FIP', dict(src_has_fip=False)),
148 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900149 ('DEST with FIP', dict(dest_has_fip=True)),
150 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900151 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900152
153 same_network = True
154
Brian Haleyba800452017-12-14 10:30:48 -0500155 @common_utils.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000156 @decorators.idempotent_id('05c4e3b3-7319-4052-90ad-e8916436c23b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900157 def test_east_west(self):
158 self._test_east_west()
159
160
161class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
162 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900163 scenarios = multiply_scenarios([
164 ('SRC with FIP', dict(src_has_fip=True)),
165 ('SRC without FIP', dict(src_has_fip=False)),
166 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900167 ('DEST with FIP', dict(dest_has_fip=True)),
168 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900169 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900170
171 same_network = False
172
Brian Haleyba800452017-12-14 10:30:48 -0500173 @common_utils.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000174 @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900175 def test_east_west(self):
176 self._test_east_west()
Itzik Browna31510f2018-01-19 11:09:48 +0200177
178
179class DefaultSnatToExternal(FloatingIpTestCasesMixin,
180 base.BaseTempestTestCase):
181 same_network = True
182
183 @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
184 def test_snat_external_ip(self):
185 """Check connectivity to an external IP"""
186 gateway_external_ip = self._get_external_gateway()
187
188 if not gateway_external_ip:
189 raise self.skipTest("IPv4 gateway is not configured for public "
190 "network or public_network_id is not "
191 "configured")
192 proxy = self._create_server()
193 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
194 CONF.validation.image_ssh_user,
195 pkey=self.keypair['private_key'])
196 src_server = self._create_server(create_floating_ip=False)
197 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
198 ssh_client = ssh.Client(src_server_ip,
199 CONF.validation.image_ssh_user,
200 pkey=self.keypair['private_key'],
201 proxy_client=proxy_client)
202 self.check_remote_connectivity(ssh_client,
203 gateway_external_ip)
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800204
205
Hongbin Lu965b03d2018-04-25 22:32:30 +0000206class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
207 base.BaseTempestTestCase):
208 same_network = True
209
210 @classmethod
211 @utils.requires_ext(extension="router", service="network")
212 @utils.requires_ext(extension="fip-port-details", service="network")
213 def resource_setup(cls):
214 super(FloatingIPPortDetailsTest, cls).resource_setup()
215
Slawek Kaplonski5ddcf332019-03-26 18:00:58 +0100216 @common_utils.unstable_test("bug 1815585")
Hongbin Lu965b03d2018-04-25 22:32:30 +0000217 @decorators.idempotent_id('a663aeee-dd81-492b-a207-354fd6284dbe')
218 def test_floatingip_port_details(self):
219 """Tests the following:
220
221 1. Create a port with floating ip in Neutron.
222 2. Create two servers in Nova.
223 3. Attach the port to the server.
224 4. Detach the port from the server.
225 5. Attach the port to the second server.
226 6. Detach the port from the second server.
227 """
228 port = self.create_port(self.network)
229 fip = self.create_and_associate_floatingip(port['id'])
230 server1 = self._create_server(create_floating_ip=False)
231 server2 = self._create_server(create_floating_ip=False)
232
233 for server in [server1, server2]:
234 # attach the port to the server
235 self.create_interface(
236 server['server']['id'], port_id=port['id'])
237 waiters.wait_for_interface_status(
238 self.os_primary.interfaces_client, server['server']['id'],
Brian Haleyaf347da2018-09-14 11:24:00 -0600239 port['id'], lib_constants.PORT_STATUS_ACTIVE)
Hongbin Lu965b03d2018-04-25 22:32:30 +0000240 fip = self.client.show_floatingip(fip['id'])['floatingip']
241 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600242 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
Hongbin Lu965b03d2018-04-25 22:32:30 +0000243 device_id=server['server']['id'], device_owner='compute:nova')
244
245 # detach the port from the server; this is a cast in the compute
246 # API so we have to poll the port until the device_id is unset.
247 self.delete_interface(server['server']['id'], port['id'])
Brian Haleyaf347da2018-09-14 11:24:00 -0600248 port = self._wait_for_port_detach(port['id'])
249 fip = self._wait_for_fip_port_down(fip['id'])
Hongbin Lu965b03d2018-04-25 22:32:30 +0000250 self._check_port_details(
Brian Haleyaf347da2018-09-14 11:24:00 -0600251 fip, port, status=lib_constants.PORT_STATUS_DOWN,
252 device_id='', device_owner='')
Hongbin Lu965b03d2018-04-25 22:32:30 +0000253
254 def _check_port_details(self, fip, port, status, device_id, device_owner):
255 self.assertIn('port_details', fip)
256 port_details = fip['port_details']
257 self.assertEqual(port['name'], port_details['name'])
258 self.assertEqual(port['network_id'], port_details['network_id'])
259 self.assertEqual(port['mac_address'], port_details['mac_address'])
260 self.assertEqual(port['admin_state_up'],
261 port_details['admin_state_up'])
262 self.assertEqual(status, port_details['status'])
263 self.assertEqual(device_id, port_details['device_id'])
264 self.assertEqual(device_owner, port_details['device_owner'])
265
266 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
267 """Waits for the port's device_id to be unset.
268
269 :param port_id: The id of the port being detached.
270 :returns: The final port dict from the show_port response.
271 """
272 port = self.client.show_port(port_id)['port']
273 device_id = port['device_id']
274 start = int(time.time())
275
276 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
277 # None, but it's not contractual so handle Falsey either way.
278 while device_id:
279 time.sleep(interval)
280 port = self.client.show_port(port_id)['port']
281 device_id = port['device_id']
282
283 timed_out = int(time.time()) - start >= timeout
284
285 if device_id and timed_out:
286 message = ('Port %s failed to detach (device_id %s) within '
287 'the required time (%s s).' %
288 (port_id, device_id, timeout))
289 raise exceptions.TimeoutException(message)
290
291 return port
292
Brian Haleyaf347da2018-09-14 11:24:00 -0600293 def _wait_for_fip_port_down(self, fip_id, timeout=120, interval=10):
294 """Waits for the fip's attached port status to be 'DOWN'.
295
296 :param fip_id: The id of the floating IP.
297 :returns: The final fip dict from the show_floatingip response.
298 """
299 fip = self.client.show_floatingip(fip_id)['floatingip']
300 self.assertIn('port_details', fip)
301 port_details = fip['port_details']
302 status = port_details['status']
303 start = int(time.time())
304
305 while status != lib_constants.PORT_STATUS_DOWN:
306 time.sleep(interval)
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
312 timed_out = int(time.time()) - start >= timeout
313
314 if status != lib_constants.PORT_STATUS_DOWN and timed_out:
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100315 port_id = fip.get("port_id")
316 port = self.os_admin.network_client.show_port(port_id)['port']
Brian Haleyaf347da2018-09-14 11:24:00 -0600317 message = ('Floating IP %s attached port status failed to '
318 'transition to DOWN (current status %s) within '
Slawek Kaplonski7451ad72019-02-25 12:02:49 +0100319 'the required time (%s s). Port details: %s' %
320 (fip_id, status, timeout, port))
Brian Haleyaf347da2018-09-14 11:24:00 -0600321 raise exceptions.TimeoutException(message)
322
323 return fip
324
Hongbin Lu965b03d2018-04-25 22:32:30 +0000325
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800326class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900327 test_qos.QoSTestMixin,
328 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800329
330 same_network = True
331
332 @classmethod
333 @utils.requires_ext(extension="router", service="network")
334 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900335 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800336 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
337 def resource_setup(cls):
338 super(FloatingIPQosTest, cls).resource_setup()
339
340 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
341 def test_qos(self):
342 """Test floating IP is binding to a QoS policy with
343
344 ingress and egress bandwidth limit rules. And it applied correctly
345 by sending a file from the instance to the test node.
346 Then calculating the bandwidth every ~1 sec by the number of bits
347 received / elapsed time.
348 """
349
350 self._test_basic_resources()
351 policy_id = self._create_qos_policy()
352 ssh_client = self._create_ssh_client()
353 self.os_admin.network_client.create_bandwidth_limit_rule(
354 policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
355 max_burst_kbps=constants.LIMIT_KILO_BYTES,
356 direction=lib_constants.INGRESS_DIRECTION)
357 self.os_admin.network_client.create_bandwidth_limit_rule(
358 policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
359 max_burst_kbps=constants.LIMIT_KILO_BYTES,
360 direction=lib_constants.EGRESS_DIRECTION)
361
362 rules = self.os_admin.network_client.list_bandwidth_limit_rules(
363 policy_id)
364 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
365
366 fip = self.os_admin.network_client.get_floatingip(
367 self.fip['id'])['floatingip']
368 self.assertEqual(self.port['id'], fip['port_id'])
369
370 self.os_admin.network_client.update_floatingip(
371 self.fip['id'],
372 qos_policy_id=policy_id)
373
374 fip = self.os_admin.network_client.get_floatingip(
375 self.fip['id'])['floatingip']
376 self.assertEqual(policy_id, fip['qos_policy_id'])
377
378 self._create_file_for_bw_tests(ssh_client)
379 common_utils.wait_until_true(lambda: self._check_bw(
380 ssh_client,
381 self.fip['floating_ip_address'],
382 port=self.NC_PORT),
383 timeout=120,
384 sleep=1)
Jakub Libosvar031fd5a2019-07-15 16:10:07 +0000385
386
387class TestFloatingIPUpdate(FloatingIpTestCasesMixin,
388 base.BaseTempestTestCase):
389
390 same_network = None
391
392 @decorators.idempotent_id('1bdd849b-03dd-4b8f-994f-457cf8a36f93')
393 def test_floating_ip_update(self):
394 """Test updating FIP with another port.
395
396 The test creates two servers and attaches floating ip to first server.
397 Then it checks server is accesible using the FIP. FIP is then
398 associated with the second server and connectivity is checked again.
399 """
400 ports = [self.create_port(
401 self.network, security_groups=[self.secgroup['id']])
402 for i in range(2)]
403
404 servers = []
405 for port in ports:
406 name = data_utils.rand_name("server-%s" % port['id'][:8])
407 server = self.create_server(
408 name=name,
409 flavor_ref=CONF.compute.flavor_ref,
410 key_name=self.keypair['name'],
411 image_ref=CONF.compute.image_ref,
412 networks=[{'port': port['id']}])['server']
413 server['name'] = name
414 servers.append(server)
415 for server in servers:
416 self.wait_for_server_active(server)
417
418 self.fip = self.create_floatingip(port=ports[0])
419 self.check_connectivity(self.fip['floating_ip_address'],
420 CONF.validation.image_ssh_user,
421 self.keypair['private_key'])
422 self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
423
424 def _wait_for_fip_associated():
425 try:
426 self.check_servers_hostnames(servers[-1:], log_errors=False)
427 except (AssertionError, exceptions.SSHTimeout):
428 return False
429 return True
430
431 # The FIP is now associated with the port of the second server.
432 try:
433 common_utils.wait_until_true(_wait_for_fip_associated,
434 timeout=15, sleep=3)
435 except common_utils.WaitTimeout:
436 self._log_console_output(servers[-1:])
437 self.fail(
438 "Server %s is not accessible via its floating ip %s" % (
439 servers[-1]['id'], self.fip['id']))
Roman Safronov29c2dff2019-04-02 22:01:23 +0300440
441
442class FloatingIpMultipleRoutersTest(base.BaseTempestTestCase):
443 credentials = ['primary', 'admin']
444
445 @classmethod
446 @utils.requires_ext(extension="router", service="network")
447 def skip_checks(cls):
448 super(FloatingIpMultipleRoutersTest, cls).skip_checks()
449
450 def _create_keypair_and_secgroup(self):
451 self.keypair = self.create_keypair()
452 self.secgroup = self.create_security_group()
453 self.create_loginable_secgroup_rule(
454 secgroup_id=self.secgroup['id'])
455 self.create_pingable_secgroup_rule(
456 secgroup_id=self.secgroup['id'])
457
458 def _create_network_and_servers(self, servers_num=1, fip_addresses=None):
459 if fip_addresses:
460 self.assertEqual(servers_num, len(fip_addresses),
461 ('Number of specified fip addresses '
462 'does not match the number of servers'))
463 network = self.create_network()
464 subnet = self.create_subnet(network)
465 router = self.create_router_by_client()
466 self.create_router_interface(router['id'], subnet['id'])
467
468 fips = []
469 for server in range(servers_num):
470 fip = fip_addresses[server] if fip_addresses else None
471 fips.append(
472 self._create_server_and_fip(
473 network=network, fip_address=fip))
474 return fips
475
476 def _create_server_and_fip(self, network, fip_address=None):
477 server = self.create_server(
478 flavor_ref=CONF.compute.flavor_ref,
479 image_ref=CONF.compute.image_ref,
480 key_name=self.keypair['name'],
481 networks=[{'uuid': network['id']}],
482 security_groups=[{'name': self.secgroup['name']}])
483 waiters.wait_for_server_status(self.os_primary.servers_client,
484 server['server']['id'],
485 constants.SERVER_STATUS_ACTIVE)
486 port = self.client.list_ports(
487 network_id=network['id'],
488 device_id=server['server']['id'])['ports'][0]
489
490 if fip_address:
491 fip = self.create_floatingip(
492 floating_ip_address=fip_address,
493 client=self.os_admin.network_client,
494 port=port)
495 self.addCleanup(
496 self.delete_floatingip, fip, self.os_admin.network_client)
497 else:
498 fip = self.create_floatingip(port=port)
499 return fip
500
501 def _check_fips_connectivity(self, mutable_fip, permanent_fip):
502 for fip in [mutable_fip, permanent_fip]:
503 fip['ssh_client'] = ssh.Client(fip['floating_ip_address'],
504 CONF.validation.image_ssh_user,
505 pkey=self.keypair['private_key'])
506 self.check_remote_connectivity(
507 permanent_fip['ssh_client'], mutable_fip['floating_ip_address'])
508 self.check_remote_connectivity(
509 mutable_fip['ssh_client'], permanent_fip['floating_ip_address'])
510
511 @testtools.skipUnless(CONF.network.public_network_id,
512 'The public_network_id option must be specified.')
513 @decorators.idempotent_id('b0382ab3-3c86-4415-84e3-649a8b040dab')
514 def test_reuse_ip_address_with_other_fip_on_other_router(self):
515 """Reuse IP address by another floating IP on another router
516
517 Scenario:
518 1. Create and connect a router to the external network.
519 2. Create and connect an internal network to the router.
520 3. Create and connect 2 VMs to the internal network.
521 4. Create FIPs in the external network for the VMs.
522 5. Make sure that VM1 can ping VM2 FIP address.
523 6. Delete VM2 FIP but save IP address that it used.
524 7. Create and connect one more router to the external network.
525 8. Create and connect an internal network to the second router.
526 9. Create and connect a VM (VM3) to the internal network of
527 the second router.
528 10. Create a FIP for the VM3 in the external network with
529 the same IP address that was used for VM2.
530 11. Make sure that now VM1 is able to reach VM3 using the FIP.
531
532 Note, the scenario passes only in case corresponding
533 ARP update was sent to the external network when reusing same IP
534 address for another FIP.
535 """
536
537 self._create_keypair_and_secgroup()
538 [mutable_fip, permanent_fip] = (
539 self._create_network_and_servers(servers_num=2))
540 self._check_fips_connectivity(mutable_fip, permanent_fip)
541 ip_address = mutable_fip['floating_ip_address']
542 self.delete_floatingip(mutable_fip)
543
544 def _fip_is_free():
545 fips = self.os_admin.network_client.list_floatingips(
546 )['floatingips']
547 for fip in fips:
548 if ip_address == fip['floating_ip_address']:
549 return False
550 return True
551
552 try:
553 common_utils.wait_until_true(lambda: _fip_is_free(),
554 timeout=30, sleep=5)
555 except common_utils.WaitTimeout:
556 self.fail("Can't reuse IP address because it is not free")
557
558 [mutable_fip] = self._create_network_and_servers(
559 servers_num=1, fip_addresses=[ip_address])
560 self._check_fips_connectivity(mutable_fip, permanent_fip)