blob: 6d855f1b5182231461ed0eadfb04f053692db918 [file] [log] [blame]
Kevin Bentona305d592016-09-19 04:26:10 -07001# All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Federico Ressi06ef8542018-10-25 15:23:52 +020015import collections
16
17from neutron_lib import constants
Kevin Benton07c90562017-02-27 01:53:16 -080018from oslo_log import log as logging
Chandan Kumarc125fd12017-11-15 19:41:01 +053019from tempest.common import utils as tutils
Itzik Brownbac51dc2016-10-31 12:25:04 +000020from tempest.lib.common.utils import data_utils
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +000021from tempest.lib import decorators
Genadi Chereshnyae91b69c2017-07-16 09:51:58 +030022import testtools
Kevin Bentona305d592016-09-19 04:26:10 -070023
Federico Ressi06ef8542018-10-25 15:23:52 +020024from neutron_tempest_plugin.common import ip
Chandan Kumar667d3d32017-09-22 12:24:06 +053025from neutron_tempest_plugin.common import ssh
26from neutron_tempest_plugin.common import utils
27from neutron_tempest_plugin import config
28from neutron_tempest_plugin.scenario import base
Federico Ressi06ef8542018-10-25 15:23:52 +020029
Kevin Bentona305d592016-09-19 04:26:10 -070030
Kevin Benton07c90562017-02-27 01:53:16 -080031LOG = logging.getLogger(__name__)
Kevin Bentona305d592016-09-19 04:26:10 -070032CONF = config.CONF
Kevin Bentona305d592016-09-19 04:26:10 -070033
Federico Ressi06ef8542018-10-25 15:23:52 +020034
35ServerWithTrunkPort = collections.namedtuple(
36 'ServerWithTrunkPort',
37 ['port', 'subport', 'trunk', 'floating_ip', 'server',
38 'ssh_client'])
Jakub Libosvar6d397d32016-12-30 10:57:52 -050039
40
Kevin Bentona305d592016-09-19 04:26:10 -070041class TrunkTest(base.BaseTempestTestCase):
42 credentials = ['primary']
43 force_tenant_isolation = False
44
45 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +053046 @tutils.requires_ext(extension="trunk", service="network")
Kevin Bentona305d592016-09-19 04:26:10 -070047 def resource_setup(cls):
48 super(TrunkTest, cls).resource_setup()
49 # setup basic topology for servers we can log into
Federico Ressi06ef8542018-10-25 15:23:52 +020050 cls.rand_name = data_utils.rand_name(
51 cls.__name__.rsplit('.', 1)[-1])
52 cls.network = cls.create_network(name=cls.rand_name)
53 cls.subnet = cls.create_subnet(network=cls.network,
54 name=cls.rand_name)
Huifeng Le1c9f40b2018-11-07 01:14:21 +080055 cls.router = cls.create_router_by_client()
56 cls.create_router_interface(cls.router['id'], cls.subnet['id'])
Federico Ressi06ef8542018-10-25 15:23:52 +020057 cls.keypair = cls.create_keypair(name=cls.rand_name)
Kevin Bentona305d592016-09-19 04:26:10 -070058
Federico Ressi06ef8542018-10-25 15:23:52 +020059 def setUp(self):
60 super(TrunkTest, self).setUp()
61 self.security_group = self.create_security_group(name=self.rand_name)
62 self.create_loginable_secgroup_rule(self.security_group['id'])
Kevin Bentona305d592016-09-19 04:26:10 -070063
Federico Ressi06ef8542018-10-25 15:23:52 +020064 def _create_server_with_network(self, network, use_advanced_image=False):
65 port = self._create_server_port(network=network)
66 floating_ip = self.create_floatingip(port=port)
67 ssh_client = self._create_ssh_client(
68 floating_ip=floating_ip, use_advanced_image=use_advanced_image)
69 server = self._create_server(port=port,
70 use_advanced_image=use_advanced_image)
71 return ServerWithTrunkPort(port=port, subport=None, trunk=None,
72 floating_ip=floating_ip, server=server,
73 ssh_client=ssh_client)
74
75 def _create_server_with_trunk_port(self, subport_network=None,
76 segmentation_id=None,
77 use_advanced_image=False):
78 port = self._create_server_port()
79 floating_ip = self.create_floatingip(port=port)
80 ssh_client = self._create_ssh_client(
81 floating_ip=floating_ip, use_advanced_image=use_advanced_image)
82
83 subport = None
84 subports = None
85 if subport_network:
86 subport = self._create_server_port(
87 network=subport_network, mac_address=port['mac_address'])
88 subports = [{'port_id': subport['id'],
89 'segmentation_type': 'vlan',
90 'segmentation_id': segmentation_id}]
91 trunk = self.create_trunk(port=port, subports=subports)
92
93 server = self._create_server(port=port,
94 use_advanced_image=use_advanced_image)
95 return ServerWithTrunkPort(port=port, subport=subport, trunk=trunk,
96 floating_ip=floating_ip, server=server,
97 ssh_client=ssh_client)
98
99 def _create_server_port(self, network=None, **params):
100 network = network or self.network
101 return self.create_port(network=network, name=self.rand_name,
102 security_groups=[self.security_group['id']],
103 **params)
104
105 def _create_server(self, port, use_advanced_image=False, **params):
Slawek Kaplonskida17f002018-10-11 18:35:23 +0200106 if use_advanced_image:
107 flavor_ref = CONF.neutron_plugin_options.advanced_image_flavor_ref
108 image_ref = CONF.neutron_plugin_options.advanced_image_ref
Federico Ressi06ef8542018-10-25 15:23:52 +0200109 else:
110 flavor_ref = CONF.compute.flavor_ref
111 image_ref = CONF.compute.image_ref
112 return self.create_server(flavor_ref=flavor_ref,
113 image_ref=image_ref,
114 key_name=self.keypair['name'],
115 networks=[{'port': port['id']}],
116 **params)['server']
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500117
Federico Ressi06ef8542018-10-25 15:23:52 +0200118 def _show_port(self, port, update=False):
119 observed = self.client.show_port(port['id'])['port']
120 if update:
121 port.update(observed)
122 return observed
Kevin Bentona305d592016-09-19 04:26:10 -0700123
Federico Ressi06ef8542018-10-25 15:23:52 +0200124 def _show_trunk(self, trunk, update=False):
125 observed = self.client.show_trunk(trunk['id'])['trunk']
126 if update:
127 trunk.update(observed)
128 return observed
Kevin Bentona305d592016-09-19 04:26:10 -0700129
Federico Ressi06ef8542018-10-25 15:23:52 +0200130 def _is_trunk_status(self, trunk, status, update=False):
131 return self._show_trunk(trunk, update)['status'] == status
Kevin Bentona305d592016-09-19 04:26:10 -0700132
Federico Ressi06ef8542018-10-25 15:23:52 +0200133 def _is_port_status(self, port, status, update=False):
134 return self._show_port(port, update)['status'] == status
135
136 def _wait_for_port(self, port, status=constants.ACTIVE):
137 utils.wait_until_true(
138 lambda: self._is_port_status(port, status),
139 exception=RuntimeError(
140 "Timed out waiting for port {!r} to transition to get "
141 "status {!r}.".format(port['id'], status)))
142
143 def _wait_for_trunk(self, trunk, status=constants.ACTIVE):
144 utils.wait_until_true(
145 lambda: self._is_trunk_status(trunk, status),
146 exception=RuntimeError(
147 "Timed out waiting for trunk {!r} to transition to get "
148 "status {!r}.".format(trunk['id'], status)))
149
150 def _create_ssh_client(self, floating_ip, use_advanced_image=False):
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800151 if use_advanced_image:
Federico Ressi06ef8542018-10-25 15:23:52 +0200152 username = CONF.neutron_plugin_options.advanced_image_ssh_user
153 else:
154 username = CONF.validation.image_ssh_user
155 return ssh.Client(host=floating_ip['floating_ip_address'],
156 username=username,
157 pkey=self.keypair['private_key'])
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800158
Federico Ressi06ef8542018-10-25 15:23:52 +0200159 def _assert_has_ssh_connectivity(self, ssh_client):
160 ssh_client.exec_command("true")
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800161
Federico Ressi06ef8542018-10-25 15:23:52 +0200162 def _configure_vlan_subport(self, vm, vlan_tag, vlan_subnet):
163 self.wait_for_server_active(server=vm.server)
164 self._wait_for_trunk(trunk=vm.trunk)
165 self._wait_for_port(port=vm.port)
166 self._wait_for_port(port=vm.subport)
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800167
Federico Ressi06ef8542018-10-25 15:23:52 +0200168 ip_command = ip.IPCommand(ssh_client=vm.ssh_client)
169 for address in ip_command.list_addresses(port=vm.port):
170 port_iface = address.device.name
171 break
172 else:
173 self.fail("Parent port fixed IP not found on server.")
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500174
Federico Ressi06ef8542018-10-25 15:23:52 +0200175 subport_iface = ip_command.configure_vlan_subport(
176 port=vm.port, subport=vm.subport, vlan_tag=vlan_tag,
177 subnets=[vlan_subnet])
178 for address in ip_command.list_addresses(port=vm.subport):
179 self.assertEqual(subport_iface, address.device.name)
180 self.assertEqual(port_iface, address.device.parent)
181 break
182 else:
183 self.fail("Sub-port fixed IP not found on server.")
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500184
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000185 @decorators.idempotent_id('bb13fe28-f152-4000-8131-37890a40c79e')
Kevin Bentona305d592016-09-19 04:26:10 -0700186 def test_trunk_subport_lifecycle(self):
187 """Test trunk creation and subport transition to ACTIVE status.
188
189 This is a basic test for the trunk extension to ensure that we
190 can create a trunk, attach it to a server, add/remove subports,
191 while ensuring the status transitions as appropriate.
192
193 This test does not assert any dataplane behavior for the subports.
194 It's just a high-level check to ensure the agents claim to have
195 wired the port correctly and that the trunk port itself maintains
196 connectivity.
197 """
Federico Ressi06ef8542018-10-25 15:23:52 +0200198 vm1 = self._create_server_with_trunk_port()
199 vm2 = self._create_server_with_trunk_port()
200 for vm in (vm1, vm2):
201 self.wait_for_server_active(server=vm.server)
202 self._wait_for_trunk(vm.trunk)
203 self._assert_has_ssh_connectivity(vm.ssh_client)
204
Kevin Bentona305d592016-09-19 04:26:10 -0700205 # create a few more networks and ports for subports
Yariv Rachmanifed6f862017-12-19 11:55:25 +0200206 # check limit of networks per project
Federico Ressi06ef8542018-10-25 15:23:52 +0200207 segment_ids = range(
208 3, 3 + CONF.neutron_plugin_options.max_networks_per_project)
209 tagged_networks = [self.create_network() for _ in segment_ids]
210 tagged_ports = [self.create_port(network=network)
211 for network in tagged_networks]
212 subports = [{'port_id': tagged_ports[i]['id'],
213 'segmentation_type': 'vlan',
214 'segmentation_id': segment_id}
215 for i, segment_id in enumerate(segment_ids)]
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500216
Federico Ressi06ef8542018-10-25 15:23:52 +0200217 # add all subports to server1
218 self.client.add_subports(vm1.trunk['id'], subports)
219 self._wait_for_trunk(vm1.trunk)
220 for port in tagged_ports:
221 self._wait_for_port(port)
222
223 # ensure main data-plane wasn't interrupted
224 self._assert_has_ssh_connectivity(vm1.ssh_client)
225
226 # move subports over to other server
227 self.client.remove_subports(vm1.trunk['id'], subports)
228 # ensure all subports go down
229 for port in tagged_ports:
230 self._wait_for_port(port, status=constants.DOWN)
231
232 self.client.add_subports(vm2.trunk['id'], subports)
233
234 # wait for both trunks to go back to ACTIVE
235 for vm in [vm1, vm2]:
236 self._wait_for_trunk(vm.trunk)
237
238 # ensure subports come up on other trunk
239 for port in tagged_ports:
240 self._wait_for_port(port)
241
242 # final connectivity check
243 for vm in [vm1, vm2]:
244 self._wait_for_trunk(vm.trunk)
245 self._assert_has_ssh_connectivity(vm1.ssh_client)
246
Yariv Rachmanifa1081a2018-11-21 12:46:57 +0200247 @testtools.skipUnless(
248 (CONF.neutron_plugin_options.advanced_image_ref or
249 CONF.neutron_plugin_options.default_image_is_advanced),
250 "Advanced image is required to run this test.")
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +0000251 @decorators.idempotent_id('a8a02c9b-b453-49b5-89a2-cce7da66aafb')
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500252 def test_subport_connectivity(self):
253 vlan_tag = 10
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500254 vlan_network = self.create_network()
Federico Ressi06ef8542018-10-25 15:23:52 +0200255 vlan_subnet = self.create_subnet(network=vlan_network, gateway=None)
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500256
Yariv Rachmanifa1081a2018-11-21 12:46:57 +0200257 use_advanced_image = (
258 not CONF.neutron_plugin_options.default_image_is_advanced)
259
260 vm1 = self._create_server_with_trunk_port(
261 subport_network=vlan_network,
262 segmentation_id=vlan_tag,
263 use_advanced_image=use_advanced_image)
264 vm2 = self._create_server_with_trunk_port(
265 subport_network=vlan_network,
266 segmentation_id=vlan_tag,
267 use_advanced_image=use_advanced_image)
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500268
Federico Ressi06ef8542018-10-25 15:23:52 +0200269 for vm in [vm1, vm2]:
270 self._configure_vlan_subport(vm=vm,
271 vlan_tag=vlan_tag,
272 vlan_subnet=vlan_subnet)
Jakub Libosvar6d397d32016-12-30 10:57:52 -0500273
Kevin Benton6f1f9d52017-03-01 09:14:45 -0800274 # Ping from server1 to server2 via VLAN interface should fail because
275 # we haven't allowed ICMP
276 self.check_remote_connectivity(
Federico Ressi06ef8542018-10-25 15:23:52 +0200277 vm1.ssh_client,
278 vm2.subport['fixed_ips'][0]['ip_address'],
279 should_succeed=False)
280
281 # allow intra-security-group traffic
282 self.create_pingable_secgroup_rule(self.security_group['id'])
Kevin Benton6f1f9d52017-03-01 09:14:45 -0800283 self.check_remote_connectivity(
Federico Ressi06ef8542018-10-25 15:23:52 +0200284 vm1.ssh_client,
285 vm2.subport['fixed_ips'][0]['ip_address'])
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800286
287 @testtools.skipUnless(
Yariv Rachmanifa1081a2018-11-21 12:46:57 +0200288 (CONF.neutron_plugin_options.advanced_image_ref or
289 CONF.neutron_plugin_options.default_image_is_advanced),
Federico Ressi06ef8542018-10-25 15:23:52 +0200290 "Advanced image is required to run this test.")
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800291 @testtools.skipUnless(
Federico Ressi06ef8542018-10-25 15:23:52 +0200292 CONF.neutron_plugin_options.q_agent == "linuxbridge",
293 "Linux bridge agent is required to run this test.")
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800294 @decorators.idempotent_id('d61cbdf6-1896-491c-b4b4-871caf7fbffe')
295 def test_parent_port_connectivity_after_trunk_deleted_lb(self):
296 vlan_tag = 10
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800297 vlan_network = self.create_network()
298 vlan_subnet = self.create_subnet(vlan_network)
299 self.create_router_interface(self.router['id'], vlan_subnet['id'])
300
Yariv Rachmanifa1081a2018-11-21 12:46:57 +0200301 use_advanced_image = (
302 not CONF.neutron_plugin_options.default_image_is_advanced)
303
Federico Ressi06ef8542018-10-25 15:23:52 +0200304 # Create servers
305 trunk_network_server = self._create_server_with_trunk_port(
306 subport_network=vlan_network,
307 segmentation_id=vlan_tag,
Yariv Rachmanifa1081a2018-11-21 12:46:57 +0200308 use_advanced_image=use_advanced_image)
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800309 normal_network_server = self._create_server_with_network(self.network)
310 vlan_network_server = self._create_server_with_network(vlan_network)
311
Federico Ressi06ef8542018-10-25 15:23:52 +0200312 self._configure_vlan_subport(vm=trunk_network_server,
313 vlan_tag=vlan_tag,
314 vlan_subnet=vlan_subnet)
315 for vm in [normal_network_server, vlan_network_server]:
316 self.wait_for_server_active(vm.server)
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800317
Federico Ressi06ef8542018-10-25 15:23:52 +0200318 # allow ICMP traffic
319 self.create_pingable_secgroup_rule(self.security_group['id'])
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800320
321 # Ping from trunk_network_server to normal_network_server
322 # via parent port
323 self.check_remote_connectivity(
Federico Ressi06ef8542018-10-25 15:23:52 +0200324 trunk_network_server.ssh_client,
325 normal_network_server.port['fixed_ips'][0]['ip_address'],
326 should_succeed=True)
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800327
328 # Ping from trunk_network_server to vlan_network_server via VLAN
329 # interface should success
330 self.check_remote_connectivity(
Federico Ressi06ef8542018-10-25 15:23:52 +0200331 trunk_network_server.ssh_client,
332 vlan_network_server.port['fixed_ips'][0]['ip_address'],
333 should_succeed=True)
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800334
335 # Delete the trunk
Federico Ressi06ef8542018-10-25 15:23:52 +0200336 self.delete_trunk(
337 trunk_network_server.trunk,
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800338 detach_parent_port=False)
Federico Ressi06ef8542018-10-25 15:23:52 +0200339 LOG.debug("Trunk %s is deleted.",
340 trunk_network_server.trunk['id'])
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800341
342 # Ping from trunk_network_server to normal_network_server
343 # via parent port success after trunk deleted
344 self.check_remote_connectivity(
Federico Ressi06ef8542018-10-25 15:23:52 +0200345 trunk_network_server.ssh_client,
346 normal_network_server.port['fixed_ips'][0]['ip_address'],
347 should_succeed=True)
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800348
349 # Ping from trunk_network_server to vlan_network_server via VLAN
350 # interface should fail after trunk deleted
351 self.check_remote_connectivity(
Federico Ressi06ef8542018-10-25 15:23:52 +0200352 trunk_network_server.ssh_client,
353 vlan_network_server.port['fixed_ips'][0]['ip_address'],
354 should_succeed=False)