blob: b3503924942d1ab224e7bb8bd3efa1521fc1b99e [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)
40 cls.create_router_and_interface(cls.subnet['id'])
41 cls.keypair = cls.create_keypair()
Itzik Brownbac51dc2016-10-31 12:25:04 +000042 cls.secgroup = cls.manager.network_client.create_security_group(
43 name=data_utils.rand_name('secgroup-'))
44 cls.security_groups.append(cls.secgroup['security_group'])
45 cls.create_loginable_secgroup_rule(
46 secgroup_id=cls.secgroup['security_group']['id'])
Kevin Bentona305d592016-09-19 04:26:10 -070047
48 def _create_server_with_trunk_port(self):
Itzik Brownbac51dc2016-10-31 12:25:04 +000049 port = self.create_port(self.network, security_groups=[
50 self.secgroup['security_group']['id']])
Kevin Bentona305d592016-09-19 04:26:10 -070051 trunk = self.client.create_trunk(port['id'], subports=[])['trunk']
52 fip = self.create_and_associate_floatingip(port['id'])
53 server = self.create_server(
54 flavor_ref=CONF.compute.flavor_ref,
55 image_ref=CONF.compute.image_ref,
56 key_name=self.keypair['name'],
Itzik Brownbac51dc2016-10-31 12:25:04 +000057 networks=[{'port': port['id']}],
58 security_groups=[{'name': self.secgroup[
59 'security_group']['name']}])['server']
Kevin Bentona305d592016-09-19 04:26:10 -070060 self.addCleanup(self._detach_and_delete_trunk, server, trunk)
61 return {'port': port, 'trunk': trunk, 'fip': fip,
62 'server': server}
63
64 def _detach_and_delete_trunk(self, server, trunk):
65 # we have to detach the interface from the server before
66 # the trunk can be deleted.
67 self.manager.compute.InterfacesClient().delete_interface(
68 server['id'], trunk['port_id'])
69
70 def is_port_detached():
71 p = self.client.show_port(trunk['port_id'])['port']
72 return p['device_id'] == ''
73 utils.wait_until_true(is_port_detached)
74 self.client.delete_trunk(trunk['id'])
75
76 def _is_port_down(self, port_id):
77 p = self.client.show_port(port_id)['port']
78 return p['status'] == 'DOWN'
79
80 def _is_port_active(self, port_id):
81 p = self.client.show_port(port_id)['port']
82 return p['status'] == 'ACTIVE'
83
84 def _is_trunk_active(self, trunk_id):
85 t = self.client.show_trunk(trunk_id)['trunk']
86 return t['status'] == 'ACTIVE'
87
88 @test.idempotent_id('bb13fe28-f152-4000-8131-37890a40c79e')
89 def test_trunk_subport_lifecycle(self):
90 """Test trunk creation and subport transition to ACTIVE status.
91
92 This is a basic test for the trunk extension to ensure that we
93 can create a trunk, attach it to a server, add/remove subports,
94 while ensuring the status transitions as appropriate.
95
96 This test does not assert any dataplane behavior for the subports.
97 It's just a high-level check to ensure the agents claim to have
98 wired the port correctly and that the trunk port itself maintains
99 connectivity.
100 """
101 server1 = self._create_server_with_trunk_port()
102 server2 = self._create_server_with_trunk_port()
103 for server in (server1, server2):
104 waiters.wait_for_server_status(self.manager.servers_client,
105 server['server']['id'],
106 constants.SERVER_STATUS_ACTIVE)
107 self.check_connectivity(server['fip']['floating_ip_address'],
108 CONF.validation.image_ssh_user,
109 self.keypair['private_key'])
110 trunk1_id, trunk2_id = server1['trunk']['id'], server2['trunk']['id']
111 # trunks should transition to ACTIVE without any subports
112 utils.wait_until_true(
113 lambda: self._is_trunk_active(trunk1_id),
114 exception=RuntimeError("Timed out waiting for trunk %s to "
115 "transition to ACTIVE." % trunk1_id))
116 utils.wait_until_true(
117 lambda: self._is_trunk_active(trunk2_id),
118 exception=RuntimeError("Timed out waiting for trunk %s to "
119 "transition to ACTIVE." % trunk2_id))
120 # create a few more networks and ports for subports
121 subports = [{'port_id': self.create_port(self.create_network())['id'],
122 'segmentation_type': 'vlan', 'segmentation_id': seg_id}
123 for seg_id in range(3, 7)]
124 # add all subports to server1
125 self.client.add_subports(trunk1_id, subports)
126 # ensure trunk transitions to ACTIVE
127 utils.wait_until_true(
128 lambda: self._is_trunk_active(trunk1_id),
129 exception=RuntimeError("Timed out waiting for trunk %s to "
130 "transition to ACTIVE." % trunk1_id))
131 # ensure all underlying subports transitioned to ACTIVE
132 for s in subports:
133 utils.wait_until_true(lambda: self._is_port_active(s['port_id']))
134 # ensure main dataplane wasn't interrupted
135 self.check_connectivity(server1['fip']['floating_ip_address'],
136 CONF.validation.image_ssh_user,
137 self.keypair['private_key'])
138 # move subports over to other server
139 self.client.remove_subports(trunk1_id, subports)
140 # ensure all subports go down
141 for s in subports:
142 utils.wait_until_true(
143 lambda: self._is_port_down(s['port_id']),
144 exception=RuntimeError("Timed out waiting for subport %s to "
145 "transition to DOWN." % s['port_id']))
146 self.client.add_subports(trunk2_id, subports)
147 # wait for both trunks to go back to ACTIVE
148 utils.wait_until_true(
149 lambda: self._is_trunk_active(trunk1_id),
150 exception=RuntimeError("Timed out waiting for trunk %s to "
151 "transition to ACTIVE." % trunk1_id))
152 utils.wait_until_true(
153 lambda: self._is_trunk_active(trunk2_id),
154 exception=RuntimeError("Timed out waiting for trunk %s to "
155 "transition to ACTIVE." % trunk2_id))
156 # ensure subports come up on other trunk
157 for s in subports:
158 utils.wait_until_true(
159 lambda: self._is_port_active(s['port_id']),
160 exception=RuntimeError("Timed out waiting for subport %s to "
161 "transition to ACTIVE." % s['port_id']))
162 # final connectivity check
163 self.check_connectivity(server1['fip']['floating_ip_address'],
164 CONF.validation.image_ssh_user,
165 self.keypair['private_key'])
166 self.check_connectivity(server2['fip']['floating_ip_address'],
167 CONF.validation.image_ssh_user,
168 self.keypair['private_key'])