blob: 408ef79d177995fc8ca7761d2452624aa5f261d9 [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
15from oslo_log import log as logging
16from tempest.common import waiters
Itzik Brownbac51dc2016-10-31 12:25:04 +000017from tempest.lib.common.utils import data_utils
Kevin Bentona305d592016-09-19 04:26:10 -070018from tempest import test
19
20from neutron.common import utils
21from neutron.tests.tempest import config
22from neutron.tests.tempest.scenario import base
23from neutron.tests.tempest.scenario import constants
24
25CONF = config.CONF
26LOG = logging.getLogger(__name__)
27
28
29class TrunkTest(base.BaseTempestTestCase):
30 credentials = ['primary']
31 force_tenant_isolation = False
32
33 @classmethod
34 @test.requires_ext(extension="trunk", service="network")
35 def resource_setup(cls):
36 super(TrunkTest, cls).resource_setup()
37 # setup basic topology for servers we can log into
38 cls.network = cls.create_network()
39 cls.subnet = cls.create_subnet(cls.network)
Genadi Chereshnyac0411e92016-07-11 16:59:42 +030040 router = cls.create_router_by_client()
41 cls.create_router_interface(router['id'], cls.subnet['id'])
Kevin Bentona305d592016-09-19 04:26:10 -070042 cls.keypair = cls.create_keypair()
Itzik Brownbac51dc2016-10-31 12:25:04 +000043 cls.secgroup = cls.manager.network_client.create_security_group(
44 name=data_utils.rand_name('secgroup-'))
45 cls.security_groups.append(cls.secgroup['security_group'])
46 cls.create_loginable_secgroup_rule(
47 secgroup_id=cls.secgroup['security_group']['id'])
Kevin Bentona305d592016-09-19 04:26:10 -070048
49 def _create_server_with_trunk_port(self):
Itzik Brownbac51dc2016-10-31 12:25:04 +000050 port = self.create_port(self.network, security_groups=[
51 self.secgroup['security_group']['id']])
Kevin Bentona305d592016-09-19 04:26:10 -070052 trunk = self.client.create_trunk(port['id'], subports=[])['trunk']
53 fip = self.create_and_associate_floatingip(port['id'])
54 server = self.create_server(
55 flavor_ref=CONF.compute.flavor_ref,
56 image_ref=CONF.compute.image_ref,
57 key_name=self.keypair['name'],
Itzik Brownbac51dc2016-10-31 12:25:04 +000058 networks=[{'port': port['id']}],
59 security_groups=[{'name': self.secgroup[
60 'security_group']['name']}])['server']
Kevin Bentona305d592016-09-19 04:26:10 -070061 self.addCleanup(self._detach_and_delete_trunk, server, trunk)
62 return {'port': port, 'trunk': trunk, 'fip': fip,
63 'server': server}
64
65 def _detach_and_delete_trunk(self, server, trunk):
66 # we have to detach the interface from the server before
67 # the trunk can be deleted.
68 self.manager.compute.InterfacesClient().delete_interface(
69 server['id'], trunk['port_id'])
70
71 def is_port_detached():
72 p = self.client.show_port(trunk['port_id'])['port']
73 return p['device_id'] == ''
74 utils.wait_until_true(is_port_detached)
75 self.client.delete_trunk(trunk['id'])
76
77 def _is_port_down(self, port_id):
78 p = self.client.show_port(port_id)['port']
79 return p['status'] == 'DOWN'
80
81 def _is_port_active(self, port_id):
82 p = self.client.show_port(port_id)['port']
83 return p['status'] == 'ACTIVE'
84
85 def _is_trunk_active(self, trunk_id):
86 t = self.client.show_trunk(trunk_id)['trunk']
87 return t['status'] == 'ACTIVE'
88
89 @test.idempotent_id('bb13fe28-f152-4000-8131-37890a40c79e')
90 def test_trunk_subport_lifecycle(self):
91 """Test trunk creation and subport transition to ACTIVE status.
92
93 This is a basic test for the trunk extension to ensure that we
94 can create a trunk, attach it to a server, add/remove subports,
95 while ensuring the status transitions as appropriate.
96
97 This test does not assert any dataplane behavior for the subports.
98 It's just a high-level check to ensure the agents claim to have
99 wired the port correctly and that the trunk port itself maintains
100 connectivity.
101 """
102 server1 = self._create_server_with_trunk_port()
103 server2 = self._create_server_with_trunk_port()
104 for server in (server1, server2):
105 waiters.wait_for_server_status(self.manager.servers_client,
106 server['server']['id'],
107 constants.SERVER_STATUS_ACTIVE)
108 self.check_connectivity(server['fip']['floating_ip_address'],
109 CONF.validation.image_ssh_user,
110 self.keypair['private_key'])
111 trunk1_id, trunk2_id = server1['trunk']['id'], server2['trunk']['id']
112 # trunks should transition to ACTIVE without any subports
113 utils.wait_until_true(
114 lambda: self._is_trunk_active(trunk1_id),
115 exception=RuntimeError("Timed out waiting for trunk %s to "
116 "transition to ACTIVE." % trunk1_id))
117 utils.wait_until_true(
118 lambda: self._is_trunk_active(trunk2_id),
119 exception=RuntimeError("Timed out waiting for trunk %s to "
120 "transition to ACTIVE." % trunk2_id))
121 # create a few more networks and ports for subports
122 subports = [{'port_id': self.create_port(self.create_network())['id'],
123 'segmentation_type': 'vlan', 'segmentation_id': seg_id}
124 for seg_id in range(3, 7)]
125 # add all subports to server1
126 self.client.add_subports(trunk1_id, subports)
127 # ensure trunk transitions to ACTIVE
128 utils.wait_until_true(
129 lambda: self._is_trunk_active(trunk1_id),
130 exception=RuntimeError("Timed out waiting for trunk %s to "
131 "transition to ACTIVE." % trunk1_id))
132 # ensure all underlying subports transitioned to ACTIVE
133 for s in subports:
134 utils.wait_until_true(lambda: self._is_port_active(s['port_id']))
135 # ensure main dataplane wasn't interrupted
136 self.check_connectivity(server1['fip']['floating_ip_address'],
137 CONF.validation.image_ssh_user,
138 self.keypair['private_key'])
139 # move subports over to other server
140 self.client.remove_subports(trunk1_id, subports)
141 # ensure all subports go down
142 for s in subports:
143 utils.wait_until_true(
144 lambda: self._is_port_down(s['port_id']),
145 exception=RuntimeError("Timed out waiting for subport %s to "
146 "transition to DOWN." % s['port_id']))
147 self.client.add_subports(trunk2_id, subports)
148 # wait for both trunks to go back to ACTIVE
149 utils.wait_until_true(
150 lambda: self._is_trunk_active(trunk1_id),
151 exception=RuntimeError("Timed out waiting for trunk %s to "
152 "transition to ACTIVE." % trunk1_id))
153 utils.wait_until_true(
154 lambda: self._is_trunk_active(trunk2_id),
155 exception=RuntimeError("Timed out waiting for trunk %s to "
156 "transition to ACTIVE." % trunk2_id))
157 # ensure subports come up on other trunk
158 for s in subports:
159 utils.wait_until_true(
160 lambda: self._is_port_active(s['port_id']),
161 exception=RuntimeError("Timed out waiting for subport %s to "
162 "transition to ACTIVE." % s['port_id']))
163 # final connectivity check
164 self.check_connectivity(server1['fip']['floating_ip_address'],
165 CONF.validation.image_ssh_user,
166 self.keypair['private_key'])
167 self.check_connectivity(server2['fip']['floating_ip_address'],
168 CONF.validation.image_ssh_user,
169 self.keypair['private_key'])