blob: 4b2cbbabbe78f76661474ac32380d00829ddafe5 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Sean Dague6dbc6da2013-05-08 17:49:46 -04002# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
Sean Dague6dbc6da2013-05-08 17:49:46 -040017import subprocess
18
Sean Dague6dbc6da2013-05-08 17:49:46 -040019import netaddr
Doug Hellmann583ce2c2015-03-11 14:55:46 +000020from oslo_log import log
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +030021from oslo_serialization import jsonutils as json
Yatin Kumbhareee4924c2016-06-09 15:12:06 +053022from oslo_utils import netutils
Matthew Treinish96e9e882014-06-09 18:37:19 -040023import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040024
lanoux5fc14522015-09-21 08:17:35 +000025from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070026from tempest.common import image as common_image
Fei Long Wangd39431f2015-05-14 11:30:48 +120027from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090028from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000029from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000030from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000031from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020032from tempest import exceptions
Jordan Pittier9e227c52016-02-09 14:35:18 +010033from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050034from tempest.lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040035import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040036
Matthew Treinish6c072292014-01-29 19:15:52 +000037CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040038
Attila Fazekasfb7552a2013-08-27 13:02:26 +020039LOG = log.getLogger(__name__)
40
Sean Dague6dbc6da2013-05-08 17:49:46 -040041
Andrea Frittoli2e733b52014-07-16 14:12:11 +010042class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010043 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010044
Andrea Frittolib21de6c2015-02-06 20:12:38 +000045 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000046
47 @classmethod
48 def setup_clients(cls):
49 super(ScenarioTest, cls).setup_clients()
Andrea Frittoli247058f2014-07-16 16:09:22 +010050 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070051 cls.flavors_client = cls.manager.flavors_client
John Warrene74890a2015-11-11 15:18:01 -050052 cls.compute_floating_ips_client = (
53 cls.manager.compute_floating_ips_client)
Jordan Pittier1d2e40f2016-01-05 18:49:14 +010054 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -040055 # Check if glance v1 is available to determine which client to use.
56 if CONF.image_feature_enabled.api_v1:
57 cls.image_client = cls.manager.image_client
58 elif CONF.image_feature_enabled.api_v2:
59 cls.image_client = cls.manager.image_client_v2
60 else:
Matthew Treinish4217a702016-10-07 17:27:11 -040061 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -040062 'Either api_v1 or api_v2 must be True in '
63 '[image-feature-enabled].')
nithya-ganesan882595e2014-07-29 18:51:07 +000064 # Compute image client
Ghanshyamae76c122015-12-22 13:41:35 +090065 cls.compute_images_client = cls.manager.compute_images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010066 cls.keypairs_client = cls.manager.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010067 # Nova security groups client
John Warrenf2345512015-12-10 13:39:30 -050068 cls.compute_security_groups_client = (
69 cls.manager.compute_security_groups_client)
John Warren5cdbf422016-01-05 12:42:43 -050070 cls.compute_security_group_rules_client = (
71 cls.manager.compute_security_group_rules_client)
Andrea Frittoli247058f2014-07-16 16:09:22 +010072 cls.servers_client = cls.manager.servers_client
Yair Fried1fc32a12014-08-04 09:11:30 +030073 cls.interface_client = cls.manager.interfaces_client
74 # Neutron network client
John Warren94d8faf2015-09-15 12:22:24 -040075 cls.networks_client = cls.manager.networks_client
John Warren49c0fe52015-10-22 12:35:54 -040076 cls.ports_client = cls.manager.ports_client
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +000077 cls.routers_client = cls.manager.routers_client
John Warren3961acd2015-10-02 14:38:53 -040078 cls.subnets_client = cls.manager.subnets_client
John Warrenfbf2a892015-11-17 12:36:14 -050079 cls.floating_ips_client = cls.manager.floating_ips_client
John Warrenf9606e92015-12-10 12:12:42 -050080 cls.security_groups_client = cls.manager.security_groups_client
John Warren456d9ae2016-01-12 15:36:33 -050081 cls.security_group_rules_client = (
82 cls.manager.security_group_rules_client)
Andrea Frittoli2e733b52014-07-16 14:12:11 +010083
Jordan Pittierc3f76be2016-10-11 17:06:21 +020084 if CONF.volume_feature_enabled.api_v2:
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +030085 cls.volumes_client = cls.manager.volumes_v2_client
86 cls.snapshots_client = cls.manager.snapshots_v2_client
Jordan Pittierc3f76be2016-10-11 17:06:21 +020087 else:
88 cls.volumes_client = cls.manager.volumes_client
89 cls.snapshots_client = cls.manager.snapshots_client
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +030090
Jordan Pittierf672b7d2016-06-20 18:50:40 +020091 # ## Test functions library
92 #
93 # The create_[resource] functions only return body and discard the
94 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +010095
Lenny Verkhovsky136376f2016-06-29 14:33:34 +030096 def _create_port(self, network_id, client=None, namestart='port-quotatest',
97 **kwargs):
98 if not client:
99 client = self.ports_client
100 name = data_utils.rand_name(namestart)
101 result = client.create_port(
102 name=name,
103 network_id=network_id,
104 **kwargs)
105 self.assertIsNotNone(result, 'Unable to allocate port')
106 port = result['port']
107 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
108 client.delete_port, port['id'])
109 return port
110
Yair Frieddb6c9e92014-08-06 08:53:13 +0300111 def create_keypair(self, client=None):
112 if not client:
113 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100114 name = data_utils.rand_name(self.__class__.__name__)
115 # We don't need to create a keypair by pubkey in scenario
Ken'ichi Ohmichie364bce2015-07-17 10:27:59 +0000116 body = client.create_keypair(name=name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300117 self.addCleanup(client.delete_keypair, name)
ghanshyamdee01f22015-08-17 11:41:47 +0900118 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100119
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530120 def create_server(self, name=None, image_id=None, flavor=None,
lanoux5fc14522015-09-21 08:17:35 +0000121 validatable=False, wait_until=None,
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200122 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000123 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100124
lanoux5fc14522015-09-21 08:17:35 +0000125 This wrapper utility calls the common create test server and
126 returns a test server. The purpose of this wrapper is to minimize
127 the impact on the code of the tests already using this
128 function.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100129 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100130
lanoux5fc14522015-09-21 08:17:35 +0000131 # NOTE(jlanoux): As a first step, ssh checks in the scenario
132 # tests need to be run regardless of the run_validation and
133 # validatable parameters and thus until the ssh validation job
134 # becomes voting in CI. The test resources management and IP
135 # association are taken care of in the scenario tests.
136 # Therefore, the validatable parameter is set to false in all
137 # those tests. In this way create_server just return a standard
138 # server and the scenario tests always perform ssh checks.
139
140 # Needed for the cross_tenant_traffic test:
141 if clients is None:
142 clients = self.manager
143
zhufl24208c22016-10-25 15:23:48 +0800144 if name is None:
145 name = data_utils.rand_name(self.__class__.__name__ + "-server")
146
lanoux5fc14522015-09-21 08:17:35 +0000147 vnic_type = CONF.network.port_vnic_type
148
149 # If vnic_type is configured create port for
150 # every network
151 if vnic_type:
152 ports = []
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300153
lanoux5fc14522015-09-21 08:17:35 +0000154 create_port_body = {'binding:vnic_type': vnic_type,
155 'namestart': 'port-smoke'}
156 if kwargs:
157 # Convert security group names to security group ids
158 # to pass to create_port
159 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300160 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500161 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000162 ).get('security_groups')
163 sec_dict = dict([(s['name'], s['id'])
164 for s in security_groups])
165
166 sec_groups_names = [s['name'] for s in kwargs.pop(
167 'security_groups')]
168 security_groups_ids = [sec_dict[s]
169 for s in sec_groups_names]
170
171 if security_groups_ids:
172 create_port_body[
173 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300174 networks = kwargs.pop('networks', [])
175 else:
176 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000177
178 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300179 # for the project's private networks and create a port.
180 # The same behaviour as we would expect when passing
181 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000182 if not networks:
183 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300184 **{'router:external': False, 'fields': 'id'})['networks']
185
186 # It's net['uuid'] if networks come from kwargs
187 # and net['id'] if they come from
188 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000189 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000190 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300191 if 'port' not in net:
192 port = self._create_port(network_id=net_id,
193 client=clients.ports_client,
194 **create_port_body)
195 ports.append({'port': port['id']})
196 else:
197 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000198 if ports:
199 kwargs['networks'] = ports
200 self.ports = ports
201
202 tenant_network = self.get_tenant_network()
203
204 body, servers = compute.create_test_server(
205 clients,
206 tenant_network=tenant_network,
207 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530208 name=name, flavor=flavor,
209 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000210
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200211 self.addCleanup(waiters.wait_for_server_termination,
212 clients.servers_client, body['id'])
213 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
214 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000215 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100216 return server
217
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100218 def create_volume(self, size=None, name=None, snapshot_id=None,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100219 imageRef=None, volume_type=None):
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700220 if size is None:
221 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500222 if imageRef:
223 image = self.compute_images_client.show_image(imageRef)['image']
224 min_disk = image.get('minDisk')
225 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100226 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800227 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900228 kwargs = {'display_name': name,
229 'snapshot_id': snapshot_id,
230 'imageRef': imageRef,
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700231 'volume_type': volume_type,
232 'size': size}
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900233 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700234
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100235 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
236 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100237 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100238 self.volumes_client.delete_volume, volume['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100239
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300240 # NOTE(e0ne): Cinder API v2 uses name instead of display_name
241 if 'display_name' in volume:
242 self.assertEqual(name, volume['display_name'])
243 else:
244 self.assertEqual(name, volume['name'])
Yaroslav Lobankoved3a35b2016-03-24 22:41:30 -0500245 waiters.wait_for_volume_status(self.volumes_client,
246 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100247 # The volume retrieved on creation has a non-up-to-date status.
248 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000249 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100250 return volume
251
Yair Fried1fc32a12014-08-04 09:11:30 +0300252 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500253 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500254 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100255 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900256 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100257 for sg in sgs:
258 if sg['name'] == 'default':
259 secgroup_id = sg['id']
260
261 # These rules are intended to permit inbound ssh and icmp
262 # traffic from all sources, so no group_id is provided.
263 # Setting a group_id would only permit traffic from ports
264 # belonging to the same security group.
265 rulesets = [
266 {
267 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000268 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100269 'from_port': 22,
270 'to_port': 22,
271 'cidr': '0.0.0.0/0',
272 },
273 {
274 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000275 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100276 'from_port': -1,
277 'to_port': -1,
278 'cidr': '0.0.0.0/0',
279 }
280 ]
281 rules = list()
282 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000283 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900284 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100285 rules.append(sg_rule)
286 return rules
287
Yair Fried1fc32a12014-08-04 09:11:30 +0300288 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100289 # Create security group
290 sg_name = data_utils.rand_name(self.__class__.__name__)
291 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500292 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900293 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100294 self.assertEqual(secgroup['name'], sg_name)
295 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500296 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100297 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500298 self.compute_security_groups_client.delete_security_group,
299 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100300
301 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300302 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100303
304 return secgroup
305
Sean Dague20e98612016-01-06 14:33:28 -0500306 def get_remote_client(self, ip_address, username=None, private_key=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100307 """Get a SSH client to a remote server
308
Sean Dague20e98612016-01-06 14:33:28 -0500309 @param ip_address the server floating or fixed IP address to use
310 for ssh validation
JordanP3fe2dc32014-11-17 13:06:01 +0100311 @param username name of the Linux account on the remote server
312 @param private_key the SSH private key to use
JordanP3fe2dc32014-11-17 13:06:01 +0100313 @return a RemoteClient object
314 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700315
Andrea Frittoli247058f2014-07-16 16:09:22 +0100316 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800317 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800318 # Set this with 'keypair' or others to log in with keypair or
319 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000320 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800321 password = None
322 if private_key is None:
323 private_key = self.keypair['private_key']
324 else:
lanoux283273b2015-12-04 03:01:54 -0800325 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800326 private_key = None
Sean Dague20e98612016-01-06 14:33:28 -0500327 linux_client = remote_client.RemoteClient(ip_address, username,
wantwatering896300c2015-03-27 15:17:42 +0800328 pkey=private_key,
329 password=password)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100330 try:
331 linux_client.validate_authentication()
Matt Riedemann5f0ac522015-05-21 09:16:24 -0700332 except Exception as e:
333 message = ('Initializing SSH connection to %(ip)s failed. '
Eric Brown0911af62016-02-08 17:15:40 -0800334 'Error: %(error)s' % {'ip': ip_address,
Sean Dague20e98612016-01-06 14:33:28 -0500335 'error': e})
Jordan Pittier9e227c52016-02-09 14:35:18 +0100336 caller = test_utils.find_test_caller()
Matt Riedemann5f0ac522015-05-21 09:16:24 -0700337 if caller:
338 message = '(%s) %s' % (caller, message)
339 LOG.exception(message)
Sean Dague2c98a162016-01-06 14:11:13 -0500340 self._log_console_output()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100341 raise
342
343 return linux_client
344
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000345 def _image_create(self, name, fmt, path,
346 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900347 if properties is None:
348 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100349 name = data_utils.rand_name('%s-' % name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100350 params = {
351 'name': name,
352 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000353 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100354 }
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400355 if CONF.image_feature_enabled.api_v1:
356 params['is_public'] = 'False'
357 params['properties'] = properties
Ken'ichi Ohmichi02bcdf32016-06-17 16:41:26 -0700358 params = {'headers': common_image.image_meta_to_headers(**params)}
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400359 else:
360 params['visibility'] = 'private'
361 # Additional properties are flattened out in the v2 API.
362 params.update(properties)
363 body = self.image_client.create_image(**params)
364 image = body['image'] if 'image' in body else body
Andrea Frittoli247058f2014-07-16 16:09:22 +0100365 self.addCleanup(self.image_client.delete_image, image['id'])
366 self.assertEqual("queued", image['status'])
zhang.leia4b1cef2016-03-01 10:50:01 +0800367 with open(path, 'rb') as image_file:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400368 if CONF.image_feature_enabled.api_v1:
369 self.image_client.update_image(image['id'], data=image_file)
370 else:
371 self.image_client.store_image_file(image['id'], image_file)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100372 return image['id']
373
374 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300375 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100376 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
377 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
378 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300379 img_container_format = CONF.scenario.img_container_format
380 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000381 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400382 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000383 "properties: %s, ami: %s, ari: %s, aki: %s" %
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300384 (img_path, img_container_format, img_disk_format,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000385 img_properties, ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100386 try:
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100387 image = self._image_create('scenario-img',
388 img_container_format,
389 img_path,
390 disk_format=img_disk_format,
391 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100392 except IOError:
393 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
394 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
395 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000396 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100397 image = self._image_create('scenario-ami', 'ami',
398 path=ami_img_path,
399 properties=properties)
400 LOG.debug("image:%s" % image)
401
402 return image
Andrea Frittoli247058f2014-07-16 16:09:22 +0100403
404 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400405 if not CONF.compute_feature_enabled.console_output:
406 LOG.debug('Console output not supported, cannot log')
407 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100408 if not servers:
David Kranzae99b9a2015-02-16 13:37:01 -0500409 servers = self.servers_client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100410 servers = servers['servers']
411 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100412 try:
413 console_output = self.servers_client.get_console_output(
414 server['id'])['output']
415 LOG.debug('Console output for %s\nbody=\n%s',
416 server['id'], console_output)
417 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100418 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100419 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100420
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000421 def _log_net_info(self, exc):
422 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300423 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000424 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000425
nithya-ganesan882595e2014-07-29 18:51:07 +0000426 def create_server_snapshot(self, server, name=None):
427 # Glance client
428 _image_client = self.image_client
429 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900430 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000431 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800432 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000433 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000434 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500435 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300436 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200437
438 self.addCleanup(_image_client.wait_for_resource_deletion,
439 image_id)
440 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
441 _image_client.delete_image, image_id)
442
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400443 if CONF.image_feature_enabled.api_v1:
444 # In glance v1 the additional properties are stored in the headers.
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700445 resp = _image_client.check_image(image_id)
446 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400447 image_props = snapshot_image.get('properties', {})
448 else:
449 # In glance v2 the additional properties are flattened.
450 snapshot_image = _image_client.show_image(image_id)
451 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300452
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400453 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300454 if bdm:
455 bdm = json.loads(bdm)
456 if bdm and 'snapshot_id' in bdm[0]:
457 snapshot_id = bdm[0]['snapshot_id']
458 self.addCleanup(
459 self.snapshots_client.wait_for_resource_deletion,
460 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100461 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
462 self.snapshots_client.delete_snapshot,
463 snapshot_id)
Yaroslav Lobankov667aaa22016-03-24 23:13:28 -0500464 waiters.wait_for_snapshot_status(self.snapshots_client,
465 snapshot_id, 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000466 image_name = snapshot_image['name']
467 self.assertEqual(name, image_name)
468 LOG.debug("Created snapshot image %s for server %s",
469 image_name, server['name'])
470 return snapshot_image
471
Jordan Pittier7cf64762015-10-14 15:01:12 +0200472 def nova_volume_attach(self, server, volume_to_attach):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000473 volume = self.servers_client.attach_volume(
Jordan Pittier7cf64762015-10-14 15:01:12 +0200474 server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900475 % CONF.compute.volume_device_name)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200476 self.assertEqual(volume_to_attach['id'], volume['id'])
Yaroslav Lobankoved3a35b2016-03-24 22:41:30 -0500477 waiters.wait_for_volume_status(self.volumes_client,
478 volume['id'], 'in-use')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900479
Jordan Pittier7cf64762015-10-14 15:01:12 +0200480 # Return the updated volume after the attachment
481 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900482
Jordan Pittier7cf64762015-10-14 15:01:12 +0200483 def nova_volume_detach(self, server, volume):
484 self.servers_client.detach_volume(server['id'], volume['id'])
Yaroslav Lobankoved3a35b2016-03-24 22:41:30 -0500485 waiters.wait_for_volume_status(self.volumes_client,
486 volume['id'], 'available')
Jordan Pittier7cf64762015-10-14 15:01:12 +0200487
488 volume = self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900489 self.assertEqual('available', volume['status'])
490
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700491 def rebuild_server(self, server_id, image=None,
492 preserve_ephemeral=False, wait=True,
493 rebuild_kwargs=None):
494 if image is None:
495 image = CONF.compute.image_ref
496
497 rebuild_kwargs = rebuild_kwargs or {}
498
499 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
500 server_id, image, preserve_ephemeral)
Ken'ichi Ohmichi5271b0f2015-08-10 07:53:27 +0000501 self.servers_client.rebuild_server(
502 server_id=server_id, image_ref=image,
503 preserve_ephemeral=preserve_ephemeral,
504 **rebuild_kwargs)
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700505 if wait:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000506 waiters.wait_for_server_status(self.servers_client,
507 server_id, 'ACTIVE')
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700508
Steven Hardyda2a8352014-10-02 12:52:20 +0100509 def ping_ip_address(self, ip_address, should_succeed=True,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000510 ping_timeout=None, mtu=None):
lanoux5fc14522015-09-21 08:17:35 +0000511 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000512 cmd = ['ping', '-c1', '-w1']
513
514 if mtu:
515 cmd += [
516 # don't fragment
517 '-M', 'do',
518 # ping receives just the size of ICMP payload
519 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
520 ]
521 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700522
523 def ping():
524 proc = subprocess.Popen(cmd,
525 stdout=subprocess.PIPE,
526 stderr=subprocess.PIPE)
527 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000528
Aaron Rosena7df13b2014-09-23 09:45:45 -0700529 return (proc.returncode == 0) == should_succeed
530
Jordan Pittier9e227c52016-02-09 14:35:18 +0100531 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000532 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
533 ' expected result is %(should_succeed)s' % {
534 'caller': caller, 'ip': ip_address, 'timeout': timeout,
535 'should_succeed':
536 'reachable' if should_succeed else 'unreachable'
537 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200538 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000539 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
540 'ping result is %(result)s' % {
541 'caller': caller, 'ip': ip_address, 'timeout': timeout,
542 'result': 'expected' if result else 'unexpected'
543 })
544 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700545
Yair Friedae0e73d2014-11-24 11:56:26 +0200546 def check_vm_connectivity(self, ip_address,
547 username=None,
548 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000549 should_connect=True,
550 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000551 """Check server connectivity
552
Yair Friedae0e73d2014-11-24 11:56:26 +0200553 :param ip_address: server to test against
554 :param username: server's ssh username
555 :param private_key: server's ssh private key to be used
556 :param should_connect: True/False indicates positive/negative test
557 positive - attempt ping and ssh
558 negative - attempt ping and fail if succeed
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000559 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200560
561 :raises: AssertError if the result of the connectivity check does
562 not match the value of the should_connect param
563 """
564 if should_connect:
565 msg = "Timed out waiting for %s to become reachable" % ip_address
566 else:
567 msg = "ip address %s is reachable" % ip_address
568 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000569 should_succeed=should_connect,
570 mtu=mtu),
Yair Friedae0e73d2014-11-24 11:56:26 +0200571 msg=msg)
572 if should_connect:
573 # no need to check ssh for negative connectivity
574 self.get_remote_client(ip_address, username, private_key)
575
576 def check_public_network_connectivity(self, ip_address, username,
577 private_key, should_connect=True,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000578 msg=None, servers=None, mtu=None):
Yair Friedae0e73d2014-11-24 11:56:26 +0200579 # The target login is assumed to have been configured for
580 # key-based authentication by cloud-init.
581 LOG.debug('checking network connections to IP %s with user: %s' %
582 (ip_address, username))
583 try:
584 self.check_vm_connectivity(ip_address,
585 username,
586 private_key,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000587 should_connect=should_connect,
588 mtu=mtu)
Matthew Treinish53483132014-12-09 18:50:06 -0500589 except Exception:
Yair Friedae0e73d2014-11-24 11:56:26 +0200590 ex_msg = 'Public network connectivity check failed'
591 if msg:
592 ex_msg += ": " + msg
593 LOG.exception(ex_msg)
594 self._log_console_output(servers)
Yair Friedae0e73d2014-11-24 11:56:26 +0200595 raise
596
597 def create_floating_ip(self, thing, pool_name=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000598 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200599
Marc Koderer3b57d802016-03-22 15:23:31 +0100600 if not pool_name:
601 pool_name = CONF.network.floating_network_name
John Warrene74890a2015-11-11 15:18:01 -0500602 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000603 create_floating_ip(pool=pool_name)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100604 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500605 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200606 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500607 self.compute_floating_ips_client.associate_floating_ip_to_server(
Yair Friedae0e73d2014-11-24 11:56:26 +0200608 floating_ip['ip'], thing['id'])
609 return floating_ip
610
Sean Dague20e98612016-01-06 14:33:28 -0500611 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700612 private_key=None):
Sean Dague20e98612016-01-06 14:33:28 -0500613 ssh_client = self.get_remote_client(ip_address,
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700614 private_key=private_key)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300615 if dev_name is not None:
616 ssh_client.make_fs(dev_name)
Matt Riedemann076685a2015-09-30 14:38:16 -0700617 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300618 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
619 ssh_client.exec_command(cmd_timestamp)
620 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
621 % mount_path)
622 if dev_name is not None:
623 ssh_client.umount(mount_path)
624 return timestamp
625
Sean Dague20e98612016-01-06 14:33:28 -0500626 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700627 private_key=None):
Sean Dague20e98612016-01-06 14:33:28 -0500628 ssh_client = self.get_remote_client(ip_address,
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700629 private_key=private_key)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300630 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700631 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300632 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
633 % mount_path)
634 if dev_name is not None:
635 ssh_client.umount(mount_path)
636 return timestamp
637
Sean Dague20e98612016-01-06 14:33:28 -0500638 def get_server_ip(self, server):
639 """Get the server fixed or floating IP.
640
641 Based on the configuration we're in, return a correct ip
642 address for validating that a guest is up.
643 """
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200644 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500645 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800646 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500647 # method is creating the floating IP there.
648 return self.create_floating_ip(server)['ip']
649 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400650 # Determine the network name to look for based on config or creds
651 # provider network resources.
652 if CONF.validation.network_for_ssh:
653 addresses = server['addresses'][
654 CONF.validation.network_for_ssh]
655 else:
656 creds_provider = self._get_credentials_provider()
657 net_creds = creds_provider.get_primary_creds()
658 network = getattr(net_creds, 'network', None)
659 addresses = (server['addresses'][network['name']]
660 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500661 for address in addresses:
Matt Riedemanna7782552016-08-08 16:26:01 -0400662 if (address['version'] == CONF.validation.ip_version_for_ssh
663 and address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500664 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800665 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200666 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400667 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200668
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100669
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100670class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300671 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000672
Yair Fried1fc32a12014-08-04 09:11:30 +0300673 This class provide helpers for network scenario tests, using the neutron
674 API. Helpers from ancestor which use the nova network API are overridden
675 with the neutron API.
676
677 This Class also enforces using Neutron instead of novanetwork.
678 Subclassed tests will be skipped if Neutron is not enabled
679
680 """
681
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000682 credentials = ['primary', 'admin']
683
Yair Fried1fc32a12014-08-04 09:11:30 +0300684 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000685 def skip_checks(cls):
686 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100687 if not CONF.service_available.neutron:
688 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300689
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700690 def _create_network(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +0000691 routers_client=None, tenant_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200692 namestart='network-smoke-',
693 port_security_enabled=True):
John Warren94d8faf2015-09-15 12:22:24 -0400694 if not networks_client:
695 networks_client = self.networks_client
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +0000696 if not routers_client:
697 routers_client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300698 if not tenant_id:
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700699 tenant_id = networks_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300700 name = data_utils.rand_name(namestart)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400701 network_kwargs = dict(name=name, tenant_id=tenant_id)
702 # Neutron disables port security by default so we have to check the
703 # config before trying to create the network with port_security_enabled
704 if CONF.network_feature_enabled.port_security:
705 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +0200706 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -0500707 network = result['network']
708
709 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100710 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -0500711 self.networks_client.delete_network,
712 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300713 return network
714
715 def _list_networks(self, *args, **kwargs):
716 """List networks using admin creds """
John Warren94d8faf2015-09-15 12:22:24 -0400717 networks_list = self.admin_manager.networks_client.list_networks(
ghanshyam2f7cc022015-06-26 18:18:11 +0900718 *args, **kwargs)
719 return networks_list['networks']
Yair Fried1fc32a12014-08-04 09:11:30 +0300720
721 def _list_subnets(self, *args, **kwargs):
722 """List subnets using admin creds """
John Warren3961acd2015-10-02 14:38:53 -0400723 subnets_list = self.admin_manager.subnets_client.list_subnets(
ghanshyam2f7cc022015-06-26 18:18:11 +0900724 *args, **kwargs)
725 return subnets_list['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300726
727 def _list_routers(self, *args, **kwargs):
728 """List routers using admin creds """
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +0000729 routers_list = self.admin_manager.routers_client.list_routers(
ghanshyam2f7cc022015-06-26 18:18:11 +0900730 *args, **kwargs)
731 return routers_list['routers']
Yair Fried1fc32a12014-08-04 09:11:30 +0300732
733 def _list_ports(self, *args, **kwargs):
734 """List ports using admin creds """
John Warren49c0fe52015-10-22 12:35:54 -0400735 ports_list = self.admin_manager.ports_client.list_ports(
ghanshyam2f7cc022015-06-26 18:18:11 +0900736 *args, **kwargs)
737 return ports_list['ports']
Yair Fried1fc32a12014-08-04 09:11:30 +0300738
Yair Fried564d89d2015-08-06 17:02:12 +0300739 def _list_agents(self, *args, **kwargs):
740 """List agents using admin creds """
Ken'ichi Ohmichi71860062015-12-15 08:20:13 +0000741 agents_list = self.admin_manager.network_agents_client.list_agents(
Yair Fried564d89d2015-08-06 17:02:12 +0300742 *args, **kwargs)
743 return agents_list['agents']
744
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700745 def _create_subnet(self, network, subnets_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +0000746 routers_client=None, namestart='subnet-smoke',
747 **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000748 """Create a subnet for the given network
749
750 within the cidr block configured for tenant networks.
Yair Fried1fc32a12014-08-04 09:11:30 +0300751 """
John Warren3961acd2015-10-02 14:38:53 -0400752 if not subnets_client:
753 subnets_client = self.subnets_client
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +0000754 if not routers_client:
755 routers_client = self.routers_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300756
757 def cidr_in_use(cidr, tenant_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000758 """Check cidr existence
759
lei zhangdd552b22015-11-25 20:41:48 +0800760 :returns: True if subnet with cidr already exist in tenant
761 False else
Yair Fried1fc32a12014-08-04 09:11:30 +0300762 """
763 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
764 return len(cidr_in_use) != 0
765
Kirill Shileev14113572014-11-21 16:58:02 +0300766 ip_version = kwargs.pop('ip_version', 4)
767
768 if ip_version == 6:
769 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -0400770 CONF.network.project_network_v6_cidr)
771 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300772 else:
Sean Dagueed6e5862016-04-04 10:49:13 -0400773 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
774 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300775
Yair Fried1fc32a12014-08-04 09:11:30 +0300776 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300777 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300778 # Repeatedly attempt subnet creation with sequential cidr
779 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300780 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300781 str_cidr = str(subnet_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -0500782 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300783 continue
784
785 subnet = dict(
786 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -0500787 network_id=network['id'],
788 tenant_id=network['tenant_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +0300789 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300790 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300791 **kwargs
792 )
793 try:
John Warren3961acd2015-10-02 14:38:53 -0400794 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300795 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900796 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300797 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
798 if not is_overlapping_cidr:
799 raise
800 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -0500801
802 subnet = result['subnet']
803 self.assertEqual(subnet['cidr'], str_cidr)
804
805 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
806 subnets_client.delete_subnet, subnet['id'])
807
Yair Fried1fc32a12014-08-04 09:11:30 +0300808 return subnet
809
Kirill Shileev14113572014-11-21 16:58:02 +0300810 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Kevin Benton1d0c1dc2016-02-04 14:30:08 -0800811 ports = self._list_ports(device_id=server['id'], fixed_ip=ip_addr)
Kobi Samoray166500a2016-10-09 14:42:48 +0300812 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -0500813 # If the network is dual-stack (IPv4 + IPv6), this port is associated
814 # with 2 subnets
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300815 p_status = ['ACTIVE']
816 # NOTE(vsaienko) With Ironic, instances live on separate hardware
817 # servers. Neutron does not bind ports for Ironic instances, as a
818 # result the port remains in the DOWN state.
Vasyl Saienkoc8aa34b2016-08-01 14:18:37 +0300819 # TODO(vsaienko) remove once bug: #1599836 is resolved.
Thiago Paiva66cded22016-08-15 14:55:58 -0300820 if getattr(CONF.service_available, 'ironic', False):
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300821 p_status.append('DOWN')
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200822 port_map = [(p["id"], fxip["ip_address"])
823 for p in ports
824 for fxip in p["fixed_ips"]
Yatin Kumbhareee4924c2016-06-09 15:12:06 +0530825 if netutils.is_valid_ipv4(fxip["ip_address"])
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300826 and p['status'] in p_status]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -0800827 inactive = [p for p in ports if p['status'] != 'ACTIVE']
828 if inactive:
829 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200830
John L. Villalovosb83286f2015-11-04 14:46:57 -0800831 self.assertNotEqual(0, len(port_map),
832 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200833 self.assertEqual(len(port_map), 1,
834 "Found multiple IPv4 addresses: %s. "
835 "Unable to determine which port to target."
836 % port_map)
837 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300838
David Shrewsbury9bac3662014-08-07 15:07:01 -0400839 def _get_network_by_name(self, network_name):
840 net = self._list_networks(name=network_name)
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700841 self.assertNotEqual(len(net), 0,
842 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -0500843 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -0400844
Yair Friedae0e73d2014-11-24 11:56:26 +0200845 def create_floating_ip(self, thing, external_network_id=None,
846 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000847 """Create a floating IP and associates to a resource/port on Neutron"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200848 if not external_network_id:
849 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300850 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -0500851 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300852 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300853 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
854 else:
855 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500856 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300857 floating_network_id=external_network_id,
858 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300859 tenant_id=thing['tenant_id'],
860 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300861 )
Steve Heyman33735f22016-05-24 09:28:08 -0500862 floating_ip = result['floatingip']
Jordan Pittier9e227c52016-02-09 14:35:18 +0100863 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -0500864 self.floating_ips_client.delete_floatingip,
865 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300866 return floating_ip
867
868 def _associate_floating_ip(self, floating_ip, server):
Kirill Shileev14113572014-11-21 16:58:02 +0300869 port_id, _ = self._get_server_port_id_and_ip4(server)
Steve Heyman33735f22016-05-24 09:28:08 -0500870 kwargs = dict(port_id=port_id)
871 floating_ip = self.floating_ips_client.update_floatingip(
872 floating_ip['id'], **kwargs)['floatingip']
873 self.assertEqual(port_id, floating_ip['port_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300874 return floating_ip
875
876 def _disassociate_floating_ip(self, floating_ip):
Steve Heyman33735f22016-05-24 09:28:08 -0500877 """:param floating_ip: floating_ips_client.create_floatingip"""
878 kwargs = dict(port_id=None)
879 floating_ip = self.floating_ips_client.update_floatingip(
880 floating_ip['id'], **kwargs)['floatingip']
881 self.assertIsNone(floating_ip['port_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300882 return floating_ip
883
Yair Fried45f92952014-06-26 05:19:19 +0300884 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000885 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300886
Steve Heyman33735f22016-05-24 09:28:08 -0500887 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +0300888 :param status: target status
889 :raises: AssertionError if status doesn't match
890 """
Steve Heyman33735f22016-05-24 09:28:08 -0500891 floatingip_id = floating_ip['id']
892
Carl Baldwina754e2d2014-10-23 22:47:41 +0000893 def refresh():
Steve Heyman33735f22016-05-24 09:28:08 -0500894 result = (self.floating_ips_client.
895 show_floatingip(floatingip_id)['floatingip'])
896 return status == result['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +0000897
Jordan Pittier35a63752016-08-30 13:09:12 +0200898 test_utils.call_until_true(refresh,
899 CONF.network.build_timeout,
900 CONF.network.build_interval)
Steve Heyman33735f22016-05-24 09:28:08 -0500901 floating_ip = self.floating_ips_client.show_floatingip(
902 floatingip_id)['floatingip']
903 self.assertEqual(status, floating_ip['status'],
Yair Fried45f92952014-06-26 05:19:19 +0300904 message="FloatingIP: {fp} is at status: {cst}. "
905 "failed to reach status: {st}"
Steve Heyman33735f22016-05-24 09:28:08 -0500906 .format(fp=floating_ip, cst=floating_ip['status'],
Yair Fried45f92952014-06-26 05:19:19 +0300907 st=status))
908 LOG.info("FloatingIP: {fp} is at status: {st}"
909 .format(fp=floating_ip, st=status))
910
Yair Fried1fc32a12014-08-04 09:11:30 +0300911 def _check_tenant_network_connectivity(self, server,
912 username,
913 private_key,
914 should_connect=True,
915 servers_for_debug=None):
Sean Dagueed6e5862016-04-04 10:49:13 -0400916 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +0300917 msg = 'Tenant networks not configured to be reachable.'
918 LOG.info(msg)
919 return
920 # The target login is assumed to have been configured for
921 # key-based authentication by cloud-init.
922 try:
Matthew Treinish71426682015-04-23 11:19:38 -0400923 for net_name, ip_addresses in six.iteritems(server['addresses']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300924 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900925 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200926 username,
927 private_key,
928 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300929 except Exception as e:
930 LOG.exception('Tenant network connectivity check failed')
931 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000932 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300933 raise
934
Yair Friedbc46f592015-11-18 16:29:34 +0200935 def _check_remote_connectivity(self, source, dest, should_succeed=True,
936 nic=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000937 """check ping server via source ssh connection
Yair Fried1fc32a12014-08-04 09:11:30 +0300938
939 :param source: RemoteClient: an ssh connection from which to ping
940 :param dest: and IP to ping against
941 :param should_succeed: boolean should ping succeed or not
Yair Friedbc46f592015-11-18 16:29:34 +0200942 :param nic: specific network interface to ping from
Yair Fried1fc32a12014-08-04 09:11:30 +0300943 :returns: boolean -- should_succeed == ping
944 :returns: ping is false if ping failed
945 """
946 def ping_remote():
947 try:
Yair Friedbc46f592015-11-18 16:29:34 +0200948 source.ping_host(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +0300949 except lib_exc.SSHExecCommandFailed:
zhangguoqing6c096642016-01-04 06:17:21 +0000950 LOG.warning('Failed to ping IP: %s via a ssh connection '
951 'from: %s.' % (dest, source.ssh_client.host))
Yair Fried1fc32a12014-08-04 09:11:30 +0300952 return not should_succeed
953 return should_succeed
954
Jordan Pittier35a63752016-08-30 13:09:12 +0200955 return test_utils.call_until_true(ping_remote,
956 CONF.validation.ping_timeout,
957 1)
Yair Fried1fc32a12014-08-04 09:11:30 +0300958
John Warren456d9ae2016-01-12 15:36:33 -0500959 def _create_security_group(self, security_group_rules_client=None,
960 tenant_id=None,
John Warrenf9606e92015-12-10 12:12:42 -0500961 namestart='secgroup-smoke',
962 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -0500963 if security_group_rules_client is None:
964 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -0500965 if security_groups_client is None:
966 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300967 if tenant_id is None:
John Warrenf9606e92015-12-10 12:12:42 -0500968 tenant_id = security_groups_client.tenant_id
969 secgroup = self._create_empty_security_group(
970 namestart=namestart, client=security_groups_client,
971 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300972
973 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -0500974 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -0500975 security_group_rules_client=security_group_rules_client,
976 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -0500977 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +0300978 for rule in rules:
Steve Heyman33735f22016-05-24 09:28:08 -0500979 self.assertEqual(tenant_id, rule['tenant_id'])
980 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300981 return secgroup
982
Yair Frieddb6c9e92014-08-06 08:53:13 +0300983 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300984 namestart='secgroup-smoke'):
985 """Create a security group without rules.
986
987 Default rules will be created:
988 - IPv4 egress to any
989 - IPv6 egress to any
990
991 :param tenant_id: secgroup will be created in this tenant
Steve Heyman33735f22016-05-24 09:28:08 -0500992 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300993 """
994 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -0500995 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300996 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000997 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300998 sg_name = data_utils.rand_name(namestart)
999 sg_desc = sg_name + " description"
1000 sg_dict = dict(name=sg_name,
1001 description=sg_desc)
1002 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -05001003 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001004
1005 secgroup = result['security_group']
1006 self.assertEqual(secgroup['name'], sg_name)
1007 self.assertEqual(tenant_id, secgroup['tenant_id'])
1008 self.assertEqual(secgroup['description'], sg_desc)
1009
Jordan Pittier9e227c52016-02-09 14:35:18 +01001010 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001011 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001012 return secgroup
1013
Yair Frieddb6c9e92014-08-06 08:53:13 +03001014 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001015 """Get default secgroup for given tenant_id.
1016
Ken'ichi Ohmichid67c8da2016-09-13 16:18:11 -07001017 :returns: default secgroup for given tenant
Yair Fried1fc32a12014-08-04 09:11:30 +03001018 """
1019 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001020 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001021 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001022 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001023 sgs = [
Jordan Pittier8ad86172016-04-25 16:20:53 +02001024 sg for sg in list(client.list_security_groups().values())[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001025 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
1026 ]
1027 msg = "No default security group for tenant %s." % (tenant_id)
Béla Vancsics64862f72016-11-08 09:12:31 +01001028 self.assertGreater(len(sgs), 0, msg)
Steve Heyman33735f22016-05-24 09:28:08 -05001029 return sgs[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001030
John Warren456d9ae2016-01-12 15:36:33 -05001031 def _create_security_group_rule(self, secgroup=None,
1032 sec_group_rules_client=None,
John Warrenf9606e92015-12-10 12:12:42 -05001033 tenant_id=None,
1034 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001035 """Create a rule from a dictionary of rule parameters.
1036
1037 Create a rule in a secgroup. if secgroup not defined will search for
1038 default secgroup in tenant_id.
1039
Steve Heyman33735f22016-05-24 09:28:08 -05001040 :param secgroup: the security group.
Yair Fried1fc32a12014-08-04 09:11:30 +03001041 :param tenant_id: if secgroup not passed -- the tenant in which to
1042 search for default secgroup
1043 :param kwargs: a dictionary containing rule parameters:
1044 for example, to allow incoming ssh:
1045 rule = {
1046 direction: 'ingress'
1047 protocol:'tcp',
1048 port_range_min: 22,
1049 port_range_max: 22
1050 }
1051 """
John Warren456d9ae2016-01-12 15:36:33 -05001052 if sec_group_rules_client is None:
1053 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001054 if security_groups_client is None:
1055 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001056 if not tenant_id:
John Warrenf9606e92015-12-10 12:12:42 -05001057 tenant_id = security_groups_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001058 if secgroup is None:
John Warrenf9606e92015-12-10 12:12:42 -05001059 secgroup = self._default_security_group(
1060 client=security_groups_client, tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001061
Steve Heyman33735f22016-05-24 09:28:08 -05001062 ruleset = dict(security_group_id=secgroup['id'],
1063 tenant_id=secgroup['tenant_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001064 ruleset.update(kwargs)
1065
John Warren456d9ae2016-01-12 15:36:33 -05001066 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001067 sg_rule = sg_rule['security_group_rule']
1068
1069 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1070 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001071
1072 return sg_rule
1073
John Warren456d9ae2016-01-12 15:36:33 -05001074 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1075 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001076 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001077 """Create loginable security group rule
1078
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001079 This function will create:
1080 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1081 access for ipv4.
1082 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1083 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001084 """
1085
John Warren456d9ae2016-01-12 15:36:33 -05001086 if security_group_rules_client is None:
1087 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001088 if security_groups_client is None:
1089 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001090 rules = []
1091 rulesets = [
1092 dict(
1093 # ssh
1094 protocol='tcp',
1095 port_range_min=22,
1096 port_range_max=22,
1097 ),
1098 dict(
1099 # ping
1100 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001101 ),
1102 dict(
1103 # ipv6-icmp for ping6
1104 protocol='icmp',
1105 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001106 )
1107 ]
John Warren456d9ae2016-01-12 15:36:33 -05001108 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001109 for ruleset in rulesets:
1110 for r_direction in ['ingress', 'egress']:
1111 ruleset['direction'] = r_direction
1112 try:
1113 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001114 sec_group_rules_client=sec_group_rules_client,
1115 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001116 security_groups_client=security_groups_client,
1117 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001118 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001119 # if rule already exist - skip rule and continue
1120 msg = 'Security group rule already exists'
1121 if msg not in ex._error_string:
1122 raise ex
1123 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001124 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001125 rules.append(sg_rule)
1126
1127 return rules
1128
Yair Frieddb6c9e92014-08-06 08:53:13 +03001129 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001130 """Retrieve a router for the given tenant id.
1131
1132 If a public router has been configured, it will be returned.
1133
1134 If a public router has not been configured, but a public
1135 network has, a tenant router will be created and returned that
1136 routes traffic to the public network.
1137 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001138 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001139 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001140 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001141 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001142 router_id = CONF.network.public_router_id
1143 network_id = CONF.network.public_network_id
1144 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001145 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001146 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001147 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001148 router = self._create_router(client, tenant_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001149 kwargs = {'external_gateway_info': dict(network_id=network_id)}
1150 router = client.update_router(router['id'], **kwargs)['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001151 return router
1152 else:
1153 raise Exception("Neither of 'public_router_id' or "
1154 "'public_network_id' has been defined.")
1155
Yair Frieddb6c9e92014-08-06 08:53:13 +03001156 def _create_router(self, client=None, tenant_id=None,
1157 namestart='router-smoke'):
1158 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001159 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001160 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001161 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001162 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -05001163 result = client.create_router(name=name,
1164 admin_state_up=True,
1165 tenant_id=tenant_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001166 router = result['router']
1167 self.assertEqual(router['name'], name)
1168 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1169 client.delete_router,
1170 router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001171 return router
1172
Alok Maurya6384bbb2014-07-13 06:44:29 -07001173 def _update_router_admin_state(self, router, admin_state_up):
Steve Heyman33735f22016-05-24 09:28:08 -05001174 kwargs = dict(admin_state_up=admin_state_up)
1175 router = self.routers_client.update_router(
1176 router['id'], **kwargs)['router']
1177 self.assertEqual(admin_state_up, router['admin_state_up'])
Alok Maurya6384bbb2014-07-13 06:44:29 -07001178
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001179 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001180 routers_client=None, subnets_client=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001181 tenant_id=None, dns_nameservers=None,
1182 port_security_enabled=True):
Yair Fried1fc32a12014-08-04 09:11:30 +03001183 """Create a network with a subnet connected to a router.
1184
David Shrewsbury9bac3662014-08-07 15:07:01 -04001185 The baremetal driver is a special case since all nodes are
1186 on the same shared network.
1187
Yair Fried413bf2d2014-11-19 17:07:11 +02001188 :param tenant_id: id of tenant to create resources in.
1189 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001190 :returns: network, subnet, router
1191 """
Thiago Paiva66cded22016-08-15 14:55:58 -03001192 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001193 # NOTE(Shrews): This exception is for environments where tenant
1194 # credential isolation is available, but network separation is
1195 # not (the current baremetal case). Likely can be removed when
1196 # test account mgmt is reworked:
1197 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001198 if not CONF.compute.fixed_network_name:
1199 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001200 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001201 network = self._get_network_by_name(
1202 CONF.compute.fixed_network_name)
1203 router = None
1204 subnet = None
1205 else:
John Warren94d8faf2015-09-15 12:22:24 -04001206 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001207 networks_client=networks_client,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001208 tenant_id=tenant_id,
1209 port_security_enabled=port_security_enabled)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001210 router = self._get_router(client=routers_client,
1211 tenant_id=tenant_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001212 subnet_kwargs = dict(network=network,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001213 subnets_client=subnets_client,
1214 routers_client=routers_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001215 # use explicit check because empty list is a valid option
1216 if dns_nameservers is not None:
1217 subnet_kwargs['dns_nameservers'] = dns_nameservers
1218 subnet = self._create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001219 if not routers_client:
1220 routers_client = self.routers_client
1221 router_id = router['id']
1222 routers_client.add_router_interface(router_id,
1223 subnet_id=subnet['id'])
1224
1225 # save a cleanup job to remove this association between
1226 # router and subnet
1227 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1228 routers_client.remove_router_interface, router_id,
1229 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001230 return network, subnet, router
1231
1232
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001233class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001234 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001235
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001236 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001237
1238 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001239 def setup_clients(cls):
1240 super(EncryptionScenarioTest, cls).setup_clients()
Jordan Pittierc3f76be2016-10-11 17:06:21 +02001241 if CONF.volume_feature_enabled.api_v2:
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +03001242 cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001243 cls.admin_encryption_types_client =\
1244 cls.os_adm.encryption_types_v2_client
Jordan Pittierc3f76be2016-10-11 17:06:21 +02001245 else:
1246 cls.admin_volume_types_client = cls.os_adm.volume_types_client
1247 cls.admin_encryption_types_client =\
1248 cls.os_adm.encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001249
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001250 def create_volume_type(self, client=None, name=None):
1251 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001252 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001253 if not name:
1254 name = 'generic'
Ken'ichi Ohmichi6ded8df2015-03-23 02:00:19 +00001255 randomized_name = data_utils.rand_name('scenario-type-' + name)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001256 LOG.debug("Creating a volume type: %s", randomized_name)
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001257 body = client.create_volume_type(
lei zhang83c4bbd2015-11-27 00:35:21 +08001258 name=randomized_name)['volume_type']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001259 self.assertIn('id', body)
1260 self.addCleanup(client.delete_volume_type, body['id'])
1261 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001262
1263 def create_encryption_type(self, client=None, type_id=None, provider=None,
1264 key_size=None, cipher=None,
1265 control_location=None):
1266 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001267 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001268 if not type_id:
1269 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001270 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001271 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001272 client.create_encryption_type(
1273 type_id, provider=provider, key_size=key_size, cipher=cipher,
John Warrend053ded2015-08-13 15:22:48 +00001274 control_location=control_location)['encryption']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001275
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001276
Masayuki Igawa0870db52015-09-18 21:08:36 +09001277class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001278 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001279
1280 Subclasses implement the tests that use the methods provided by this
1281 class.
1282 """
1283
1284 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001285 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001286 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001287 if not CONF.service_available.swift:
1288 skip_msg = ("%s skipped as swift is not available" %
1289 cls.__name__)
1290 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001291
1292 @classmethod
1293 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001294 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001295 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001296 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001297 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001298
1299 @classmethod
1300 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001301 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001302 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001303 cls.account_client = cls.os_operator.account_client
1304 cls.container_client = cls.os_operator.container_client
1305 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001306
Chris Dentde456a12014-09-10 12:41:15 +01001307 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001308 """get swift status for our user account."""
1309 self.account_client.list_account_containers()
1310 LOG.debug('Swift status information obtained successfully')
1311
Chris Dentde456a12014-09-10 12:41:15 +01001312 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001313 name = container_name or data_utils.rand_name(
1314 'swift-scenario-container')
1315 self.container_client.create_container(name)
1316 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001317 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001318 LOG.debug('Container %s created' % (name))
Jordan Pittier9e227c52016-02-09 14:35:18 +01001319 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001320 self.container_client.delete_container,
1321 name)
Chris Dent0d494112014-08-26 13:48:30 +01001322 return name
1323
Chris Dentde456a12014-09-10 12:41:15 +01001324 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001325 self.container_client.delete_container(container_name)
1326 LOG.debug('Container %s deleted' % (container_name))
1327
Chris Dentde456a12014-09-10 12:41:15 +01001328 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001329 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001330 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001331 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001332 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001333 self.object_client.delete_object,
1334 container_name,
1335 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001336 return obj_name, obj_data
1337
Chris Dentde456a12014-09-10 12:41:15 +01001338 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001339 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001340 self.list_and_check_container_objects(container_name,
1341 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001342
Chris Dentde456a12014-09-10 12:41:15 +01001343 def list_and_check_container_objects(self, container_name,
1344 present_obj=None,
1345 not_present_obj=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001346 # List objects for a given container and assert which are present and
1347 # which are not.
Ghanshyam2a180b82014-06-16 13:54:22 +09001348 if present_obj is None:
1349 present_obj = []
1350 if not_present_obj is None:
1351 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001352 _, object_list = self.container_client.list_container_contents(
1353 container_name)
1354 if present_obj:
1355 for obj in present_obj:
1356 self.assertIn(obj, object_list)
1357 if not_present_obj:
1358 for obj in not_present_obj:
1359 self.assertNotIn(obj, object_list)
1360
Chris Dentde456a12014-09-10 12:41:15 +01001361 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001362 metadata_param = {'metadata_prefix': 'x-container-',
1363 'metadata': {'read': acl}}
1364 self.container_client.update_container_metadata(container_name,
1365 **metadata_param)
1366 resp, _ = self.container_client.list_container_metadata(container_name)
1367 self.assertEqual(resp['x-container-read'], acl)
1368
Chris Dentde456a12014-09-10 12:41:15 +01001369 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001370 _, obj = self.object_client.get_object(container_name, obj_name)
1371 self.assertEqual(obj, expected_data)