blob: 504af12eeec71c457995b8a716454c610a8752d8 [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
Ihar Hrachyshkace9c4862018-01-18 18:26:14 +000024import testscenarios
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +090025from testscenarios.scenarios import multiply_scenarios
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090026
LIU Yulong5ba88ef2017-12-22 10:50:15 +080027from neutron_tempest_plugin.api import base as base_api
Chandan Kumar667d3d32017-09-22 12:24:06 +053028from neutron_tempest_plugin.common import ssh
Brian Haleyba800452017-12-14 10:30:48 -050029from neutron_tempest_plugin.common import utils as common_utils
Chandan Kumar667d3d32017-09-22 12:24:06 +053030from neutron_tempest_plugin import config
Hongbin Lu965b03d2018-04-25 22:32:30 +000031from neutron_tempest_plugin import exceptions
Chandan Kumar667d3d32017-09-22 12:24:06 +053032from neutron_tempest_plugin.scenario import base
33from neutron_tempest_plugin.scenario import constants
LIU Yulong5ba88ef2017-12-22 10:50:15 +080034from neutron_tempest_plugin.scenario import test_qos
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090035
36
37CONF = config.CONF
38
39
40load_tests = testscenarios.load_tests_apply_scenarios
41
42
43class FloatingIpTestCasesMixin(object):
44 credentials = ['primary', 'admin']
45
46 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +053047 @utils.requires_ext(extension="router", service="network")
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090048 def resource_setup(cls):
49 super(FloatingIpTestCasesMixin, cls).resource_setup()
50 cls.network = cls.create_network()
51 cls.subnet = cls.create_subnet(cls.network)
52 cls.router = cls.create_router_by_client()
53 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
54 cls.keypair = cls.create_keypair()
55
rajat294495c042017-06-28 15:37:16 +053056 cls.secgroup = cls.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +053057 name=data_utils.rand_name('secgroup'))['security_group']
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090058 cls.security_groups.append(cls.secgroup)
59 cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
60 cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
61
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090062 if cls.same_network:
63 cls._dest_network = cls.network
64 else:
65 cls._dest_network = cls._create_dest_network()
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090066
67 @classmethod
Itzik Browna31510f2018-01-19 11:09:48 +020068 def _get_external_gateway(cls):
69 if CONF.network.public_network_id:
70 subnets = cls.os_admin.network_client.list_subnets(
71 network_id=CONF.network.public_network_id)
72
73 for subnet in subnets['subnets']:
Brian Haley33ef4602018-04-26 14:37:49 -040074 if (subnet['gateway_ip'] and
75 subnet['ip_version'] == lib_constants.IP_VERSION_4):
Itzik Browna31510f2018-01-19 11:09:48 +020076 return subnet['gateway_ip']
77
78 @classmethod
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090079 def _create_dest_network(cls):
80 network = cls.create_network()
Federico Ressi0ddc93b2018-04-09 12:01:48 +020081 subnet = cls.create_subnet(network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090082 cls.create_router_interface(cls.router['id'], subnet['id'])
83 return network
84
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000085 def _create_server(self, create_floating_ip=True, network=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090086 if network is None:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000087 network = self.network
88 port = self.create_port(network, security_groups=[self.secgroup['id']])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090089 if create_floating_ip:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000090 fip = self.create_and_associate_floatingip(port['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090091 else:
92 fip = None
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000093 server = self.create_server(
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090094 flavor_ref=CONF.compute.flavor_ref,
95 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000096 key_name=self.keypair['name'],
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090097 networks=[{'port': port['id']}])['server']
rajat294495c042017-06-28 15:37:16 +053098 waiters.wait_for_server_status(self.os_primary.servers_client,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090099 server['id'],
100 constants.SERVER_STATUS_ACTIVE)
101 return {'port': port, 'fip': fip, 'server': server}
102
103 def _test_east_west(self):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900104 # The proxy VM is used to control the source VM when it doesn't
105 # have a floating-ip.
106 if self.src_has_fip:
107 proxy = None
108 proxy_client = None
109 else:
110 proxy = self._create_server()
111 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
112 CONF.validation.image_ssh_user,
113 pkey=self.keypair['private_key'])
114
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900115 # Source VM
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900116 if self.src_has_fip:
117 src_server = self._create_server()
118 src_server_ip = src_server['fip']['floating_ip_address']
119 else:
120 src_server = self._create_server(create_floating_ip=False)
121 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
122 ssh_client = ssh.Client(src_server_ip,
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900123 CONF.validation.image_ssh_user,
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900124 pkey=self.keypair['private_key'],
125 proxy_client=proxy_client)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900126
127 # Destination VM
128 if self.dest_has_fip:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000129 dest_server = self._create_server(network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900130 else:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000131 dest_server = self._create_server(create_floating_ip=False,
132 network=self._dest_network)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900133
134 # Check connectivity
135 self.check_remote_connectivity(ssh_client,
136 dest_server['port']['fixed_ips'][0]['ip_address'])
137 if self.dest_has_fip:
138 self.check_remote_connectivity(ssh_client,
139 dest_server['fip']['floating_ip_address'])
140
141
142class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
143 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900144 scenarios = multiply_scenarios([
145 ('SRC with FIP', dict(src_has_fip=True)),
146 ('SRC without FIP', dict(src_has_fip=False)),
147 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900148 ('DEST with FIP', dict(dest_has_fip=True)),
149 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900150 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900151
152 same_network = True
153
Brian Haleyba800452017-12-14 10:30:48 -0500154 @common_utils.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000155 @decorators.idempotent_id('05c4e3b3-7319-4052-90ad-e8916436c23b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900156 def test_east_west(self):
157 self._test_east_west()
158
159
160class FloatingIpSeparateNetwork(FloatingIpTestCasesMixin,
161 base.BaseTempestTestCase):
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900162 scenarios = multiply_scenarios([
163 ('SRC with FIP', dict(src_has_fip=True)),
164 ('SRC without FIP', dict(src_has_fip=False)),
165 ], [
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900166 ('DEST with FIP', dict(dest_has_fip=True)),
167 ('DEST without FIP', dict(dest_has_fip=False)),
YAMAMOTO Takashi60faf4f2017-01-25 08:03:07 +0900168 ])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900169
170 same_network = False
171
Brian Haleyba800452017-12-14 10:30:48 -0500172 @common_utils.unstable_test("bug 1717302")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000173 @decorators.idempotent_id('f18f0090-3289-4783-b956-a0f8ac511e8b')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900174 def test_east_west(self):
175 self._test_east_west()
Itzik Browna31510f2018-01-19 11:09:48 +0200176
177
178class DefaultSnatToExternal(FloatingIpTestCasesMixin,
179 base.BaseTempestTestCase):
180 same_network = True
181
182 @decorators.idempotent_id('3d73ea1a-27c6-45a9-b0f8-04a283d9d764')
183 def test_snat_external_ip(self):
184 """Check connectivity to an external IP"""
185 gateway_external_ip = self._get_external_gateway()
186
187 if not gateway_external_ip:
188 raise self.skipTest("IPv4 gateway is not configured for public "
189 "network or public_network_id is not "
190 "configured")
191 proxy = self._create_server()
192 proxy_client = ssh.Client(proxy['fip']['floating_ip_address'],
193 CONF.validation.image_ssh_user,
194 pkey=self.keypair['private_key'])
195 src_server = self._create_server(create_floating_ip=False)
196 src_server_ip = src_server['port']['fixed_ips'][0]['ip_address']
197 ssh_client = ssh.Client(src_server_ip,
198 CONF.validation.image_ssh_user,
199 pkey=self.keypair['private_key'],
200 proxy_client=proxy_client)
201 self.check_remote_connectivity(ssh_client,
202 gateway_external_ip)
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'],
237 port['id'], 'ACTIVE')
238 fip = self.client.show_floatingip(fip['id'])['floatingip']
239 self._check_port_details(
240 fip, port, status='ACTIVE',
241 device_id=server['server']['id'], device_owner='compute:nova')
242
243 # detach the port from the server; this is a cast in the compute
244 # API so we have to poll the port until the device_id is unset.
245 self.delete_interface(server['server']['id'], port['id'])
246 self._wait_for_port_detach(port['id'])
247 fip = self.client.show_floatingip(fip['id'])['floatingip']
248 self._check_port_details(
249 fip, port, status='DOWN', device_id='', device_owner='')
250
251 def _check_port_details(self, fip, port, status, device_id, device_owner):
252 self.assertIn('port_details', fip)
253 port_details = fip['port_details']
254 self.assertEqual(port['name'], port_details['name'])
255 self.assertEqual(port['network_id'], port_details['network_id'])
256 self.assertEqual(port['mac_address'], port_details['mac_address'])
257 self.assertEqual(port['admin_state_up'],
258 port_details['admin_state_up'])
259 self.assertEqual(status, port_details['status'])
260 self.assertEqual(device_id, port_details['device_id'])
261 self.assertEqual(device_owner, port_details['device_owner'])
262
263 def _wait_for_port_detach(self, port_id, timeout=120, interval=10):
264 """Waits for the port's device_id to be unset.
265
266 :param port_id: The id of the port being detached.
267 :returns: The final port dict from the show_port response.
268 """
269 port = self.client.show_port(port_id)['port']
270 device_id = port['device_id']
271 start = int(time.time())
272
273 # NOTE(mriedem): Nova updates the port's device_id to '' rather than
274 # None, but it's not contractual so handle Falsey either way.
275 while device_id:
276 time.sleep(interval)
277 port = self.client.show_port(port_id)['port']
278 device_id = port['device_id']
279
280 timed_out = int(time.time()) - start >= timeout
281
282 if device_id and timed_out:
283 message = ('Port %s failed to detach (device_id %s) within '
284 'the required time (%s s).' %
285 (port_id, device_id, timeout))
286 raise exceptions.TimeoutException(message)
287
288 return port
289
290
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800291class FloatingIPQosTest(FloatingIpTestCasesMixin,
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900292 test_qos.QoSTestMixin,
293 base.BaseTempestTestCase):
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800294
295 same_network = True
296
297 @classmethod
298 @utils.requires_ext(extension="router", service="network")
299 @utils.requires_ext(extension="qos", service="network")
YAMAMOTO Takashi9c072a02018-03-22 22:49:09 +0900300 @utils.requires_ext(extension="qos-fip", service="network")
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800301 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
302 def resource_setup(cls):
303 super(FloatingIPQosTest, cls).resource_setup()
304
305 @decorators.idempotent_id('5eb48aea-eaba-4c20-8a6f-7740070a0aa3')
306 def test_qos(self):
307 """Test floating IP is binding to a QoS policy with
308
309 ingress and egress bandwidth limit rules. And it applied correctly
310 by sending a file from the instance to the test node.
311 Then calculating the bandwidth every ~1 sec by the number of bits
312 received / elapsed time.
313 """
314
315 self._test_basic_resources()
316 policy_id = self._create_qos_policy()
317 ssh_client = self._create_ssh_client()
318 self.os_admin.network_client.create_bandwidth_limit_rule(
319 policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
320 max_burst_kbps=constants.LIMIT_KILO_BYTES,
321 direction=lib_constants.INGRESS_DIRECTION)
322 self.os_admin.network_client.create_bandwidth_limit_rule(
323 policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
324 max_burst_kbps=constants.LIMIT_KILO_BYTES,
325 direction=lib_constants.EGRESS_DIRECTION)
326
327 rules = self.os_admin.network_client.list_bandwidth_limit_rules(
328 policy_id)
329 self.assertEqual(2, len(rules['bandwidth_limit_rules']))
330
331 fip = self.os_admin.network_client.get_floatingip(
332 self.fip['id'])['floatingip']
333 self.assertEqual(self.port['id'], fip['port_id'])
334
335 self.os_admin.network_client.update_floatingip(
336 self.fip['id'],
337 qos_policy_id=policy_id)
338
339 fip = self.os_admin.network_client.get_floatingip(
340 self.fip['id'])['floatingip']
341 self.assertEqual(policy_id, fip['qos_policy_id'])
342
343 self._create_file_for_bw_tests(ssh_client)
344 common_utils.wait_until_true(lambda: self._check_bw(
345 ssh_client,
346 self.fip['floating_ip_address'],
347 port=self.NC_PORT),
348 timeout=120,
349 sleep=1)