blob: d09f20c7d8b6c2a38d878766bb0f1740510c88cf [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
Sean Dague6dbc6da2013-05-08 17:49:46 -040023
lanoux5fc14522015-09-21 08:17:35 +000024from tempest.common import compute
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -070025from tempest.common import image as common_image
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090026from tempest.common.utils.linux import remote_client
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +000027from tempest.common.utils import net_utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000028from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000029from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020030from tempest import exceptions
Ken'ichi Ohmichibe4fb502017-03-10 10:04:48 -080031from tempest.lib.common.utils import data_utils
Jordan Pittier9e227c52016-02-09 14:35:18 +010032from tempest.lib.common.utils import test_utils
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050033from tempest.lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040034import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040035
Matthew Treinish6c072292014-01-29 19:15:52 +000036CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040037
Attila Fazekasfb7552a2013-08-27 13:02:26 +020038LOG = log.getLogger(__name__)
39
Sean Dague6dbc6da2013-05-08 17:49:46 -040040
Andrea Frittoli2e733b52014-07-16 14:12:11 +010041class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010042 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010043
Andrea Frittolib21de6c2015-02-06 20:12:38 +000044 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000045
46 @classmethod
47 def setup_clients(cls):
48 super(ScenarioTest, cls).setup_clients()
Andrea Frittoli247058f2014-07-16 16:09:22 +010049 # Clients (in alphabetical order)
jeremy.zhang0343be52017-05-25 21:29:57 +080050 cls.flavors_client = cls.os_primary.flavors_client
John Warrene74890a2015-11-11 15:18:01 -050051 cls.compute_floating_ips_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +080052 cls.os_primary.compute_floating_ips_client)
Jordan Pittier1d2e40f2016-01-05 18:49:14 +010053 if CONF.service_available.glance:
Matt Riedemann2aa19d42016-06-06 17:45:41 -040054 # Check if glance v1 is available to determine which client to use.
55 if CONF.image_feature_enabled.api_v1:
jeremy.zhang0343be52017-05-25 21:29:57 +080056 cls.image_client = cls.os_primary.image_client
Matt Riedemann2aa19d42016-06-06 17:45:41 -040057 elif CONF.image_feature_enabled.api_v2:
jeremy.zhang0343be52017-05-25 21:29:57 +080058 cls.image_client = cls.os_primary.image_client_v2
Matt Riedemann2aa19d42016-06-06 17:45:41 -040059 else:
Matthew Treinish4217a702016-10-07 17:27:11 -040060 raise lib_exc.InvalidConfiguration(
Matt Riedemann2aa19d42016-06-06 17:45:41 -040061 'Either api_v1 or api_v2 must be True in '
62 '[image-feature-enabled].')
nithya-ganesan882595e2014-07-29 18:51:07 +000063 # Compute image client
jeremy.zhang0343be52017-05-25 21:29:57 +080064 cls.compute_images_client = cls.os_primary.compute_images_client
65 cls.keypairs_client = cls.os_primary.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010066 # Nova security groups client
John Warrenf2345512015-12-10 13:39:30 -050067 cls.compute_security_groups_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +080068 cls.os_primary.compute_security_groups_client)
John Warren5cdbf422016-01-05 12:42:43 -050069 cls.compute_security_group_rules_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +080070 cls.os_primary.compute_security_group_rules_client)
71 cls.servers_client = cls.os_primary.servers_client
72 cls.interface_client = cls.os_primary.interfaces_client
Yair Fried1fc32a12014-08-04 09:11:30 +030073 # Neutron network client
jeremy.zhang0343be52017-05-25 21:29:57 +080074 cls.networks_client = cls.os_primary.networks_client
75 cls.ports_client = cls.os_primary.ports_client
76 cls.routers_client = cls.os_primary.routers_client
77 cls.subnets_client = cls.os_primary.subnets_client
78 cls.floating_ips_client = cls.os_primary.floating_ips_client
79 cls.security_groups_client = cls.os_primary.security_groups_client
John Warren456d9ae2016-01-12 15:36:33 -050080 cls.security_group_rules_client = (
jeremy.zhang0343be52017-05-25 21:29:57 +080081 cls.os_primary.security_group_rules_client)
Andrea Frittolia6b30152017-08-04 10:46:10 +010082 # Use the latest available volume clients
83 if CONF.service_available.cinder:
84 cls.volumes_client = cls.os_primary.volumes_client_latest
85 cls.snapshots_client = cls.os_primary.snapshots_client_latest
lkuchlane20e6a82018-05-08 11:28:46 +030086 cls.backups_client = cls.os_primary.backups_client_latest
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +030087
Jordan Pittierf672b7d2016-06-20 18:50:40 +020088 # ## Test functions library
89 #
90 # The create_[resource] functions only return body and discard the
91 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +010092
zhufl1e446b52017-10-16 16:54:57 +080093 def create_port(self, network_id, client=None, **kwargs):
Lenny Verkhovsky136376f2016-06-29 14:33:34 +030094 if not client:
95 client = self.ports_client
zhufl1e446b52017-10-16 16:54:57 +080096 name = data_utils.rand_name(self.__class__.__name__)
Edan David408a97b2018-01-15 03:52:15 -050097 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
98 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
99 if CONF.network.port_profile and 'binding:profile' not in kwargs:
100 kwargs['binding:profile'] = CONF.network.port_profile
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300101 result = client.create_port(
102 name=name,
103 network_id=network_id,
104 **kwargs)
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300105 port = result['port']
106 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
107 client.delete_port, port['id'])
108 return port
109
Yair Frieddb6c9e92014-08-06 08:53:13 +0300110 def create_keypair(self, client=None):
111 if not client:
112 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100113 name = data_utils.rand_name(self.__class__.__name__)
114 # We don't need to create a keypair by pubkey in scenario
Ken'ichi Ohmichie364bce2015-07-17 10:27:59 +0000115 body = client.create_keypair(name=name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300116 self.addCleanup(client.delete_keypair, name)
ghanshyamdee01f22015-08-17 11:41:47 +0900117 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100118
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530119 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800120 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200121 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000122 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100123
lanoux5fc14522015-09-21 08:17:35 +0000124 This wrapper utility calls the common create test server and
125 returns a test server. The purpose of this wrapper is to minimize
126 the impact on the code of the tests already using this
127 function.
Noam Angel6e309952019-01-27 05:52:40 +0000128
129 :param **kwargs:
130 See extra parameters below
131
132 :Keyword Arguments:
133 * *vnic_type* (``string``) --
134 used when launching instances with pre-configured ports.
135 Examples:
136 normal: a traditional virtual port that is either attached
137 to a linux bridge or an openvswitch bridge on a
138 compute node.
139 direct: an SR-IOV port that is directly attached to a VM
140 macvtap: an SR-IOV port that is attached to a VM via a macvtap
141 device.
142 Defaults to ``CONF.network.port_vnic_type``.
143 * *port_profile* (``dict``) --
144 This attribute is a dictionary that can be used (with admin
145 credentials) to supply information influencing the binding of
146 the port.
147 example: port_profile = "capabilities:[switchdev]"
148 Defaults to ``CONF.network.port_profile``.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100149 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100150
lanoux5fc14522015-09-21 08:17:35 +0000151 # NOTE(jlanoux): As a first step, ssh checks in the scenario
152 # tests need to be run regardless of the run_validation and
153 # validatable parameters and thus until the ssh validation job
154 # becomes voting in CI. The test resources management and IP
155 # association are taken care of in the scenario tests.
156 # Therefore, the validatable parameter is set to false in all
157 # those tests. In this way create_server just return a standard
158 # server and the scenario tests always perform ssh checks.
159
160 # Needed for the cross_tenant_traffic test:
161 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800162 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000163
zhufl24208c22016-10-25 15:23:48 +0800164 if name is None:
165 name = data_utils.rand_name(self.__class__.__name__ + "-server")
166
Noam Angel6e309952019-01-27 05:52:40 +0000167 vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
168 profile = kwargs.pop('port_profile', CONF.network.port_profile)
lanoux5fc14522015-09-21 08:17:35 +0000169
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000170 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000171 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000172 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000173 ports = []
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000174 create_port_body = {}
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300175
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000176 if vnic_type:
177 create_port_body['binding:vnic_type'] = vnic_type
178
179 if profile:
180 create_port_body['binding:profile'] = profile
181
lanoux5fc14522015-09-21 08:17:35 +0000182 if kwargs:
183 # Convert security group names to security group ids
184 # to pass to create_port
185 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300186 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500187 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000188 ).get('security_groups')
189 sec_dict = dict([(s['name'], s['id'])
afazekas40fcb9b2019-03-08 11:25:11 +0100190 for s in security_groups])
lanoux5fc14522015-09-21 08:17:35 +0000191
192 sec_groups_names = [s['name'] for s in kwargs.pop(
193 'security_groups')]
194 security_groups_ids = [sec_dict[s]
195 for s in sec_groups_names]
196
197 if security_groups_ids:
198 create_port_body[
199 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300200 networks = kwargs.pop('networks', [])
201 else:
202 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000203
204 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300205 # for the project's private networks and create a port.
206 # The same behaviour as we would expect when passing
207 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000208 if not networks:
209 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300210 **{'router:external': False, 'fields': 'id'})['networks']
211
212 # It's net['uuid'] if networks come from kwargs
213 # and net['id'] if they come from
214 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000215 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000216 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300217 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800218 port = self.create_port(network_id=net_id,
219 client=clients.ports_client,
220 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300221 ports.append({'port': port['id']})
222 else:
223 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000224 if ports:
225 kwargs['networks'] = ports
226 self.ports = ports
227
228 tenant_network = self.get_tenant_network()
229
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200230 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000231 clients,
232 tenant_network=tenant_network,
233 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530234 name=name, flavor=flavor,
235 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000236
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200237 self.addCleanup(waiters.wait_for_server_termination,
238 clients.servers_client, body['id'])
239 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
240 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000241 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100242 return server
243
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100244 def create_volume(self, size=None, name=None, snapshot_id=None,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100245 imageRef=None, volume_type=None):
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700246 if size is None:
247 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500248 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800249 if CONF.image_feature_enabled.api_v1:
250 resp = self.image_client.check_image(imageRef)
251 image = common_image.get_image_meta_from_headers(resp)
252 else:
253 image = self.image_client.show_image(imageRef)
254 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500255 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100256 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800257 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900258 kwargs = {'display_name': name,
259 'snapshot_id': snapshot_id,
260 'imageRef': imageRef,
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700261 'volume_type': volume_type,
262 'size': size}
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900263 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700264
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100265 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
266 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100267 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100268 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300269 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200270 waiters.wait_for_volume_resource_status(self.volumes_client,
271 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100272 # The volume retrieved on creation has a non-up-to-date status.
273 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000274 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100275 return volume
276
lkuchlane20e6a82018-05-08 11:28:46 +0300277 def create_backup(self, volume_id, name=None, description=None,
278 force=False, snapshot_id=None, incremental=False,
279 container=None):
280
281 name = name or data_utils.rand_name(
282 self.__class__.__name__ + "-backup")
283 kwargs = {'name': name,
284 'description': description,
285 'force': force,
286 'snapshot_id': snapshot_id,
287 'incremental': incremental,
288 'container': container}
289 backup = self.backups_client.create_backup(volume_id=volume_id,
290 **kwargs)['backup']
291 self.addCleanup(self.backups_client.delete_backup, backup['id'])
292 waiters.wait_for_volume_resource_status(self.backups_client,
293 backup['id'], 'available')
294 return backup
295
296 def restore_backup(self, backup_id):
297 restore = self.backups_client.restore_backup(backup_id)['restore']
298 self.addCleanup(self.volumes_client.delete_volume,
299 restore['volume_id'])
300 waiters.wait_for_volume_resource_status(self.backups_client,
301 backup_id, 'available')
302 waiters.wait_for_volume_resource_status(self.volumes_client,
303 restore['volume_id'],
304 'available')
305 self.assertEqual(backup_id, restore['backup_id'])
306 return restore
307
lkuchlan73ed1f32017-07-06 16:22:12 +0300308 def create_volume_snapshot(self, volume_id, name=None, description=None,
309 metadata=None, force=False):
310 name = name or data_utils.rand_name(
311 self.__class__.__name__ + '-snapshot')
312 snapshot = self.snapshots_client.create_snapshot(
313 volume_id=volume_id,
314 force=force,
315 display_name=name,
316 description=description,
317 metadata=metadata)['snapshot']
318 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
319 snapshot['id'])
320 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
321 waiters.wait_for_volume_resource_status(self.snapshots_client,
322 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200323 snapshot = self.snapshots_client.show_snapshot(
324 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300325 return snapshot
326
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100327 def _cleanup_volume_type(self, volume_type):
328 """Clean up a given volume type.
329
330 Ensuring all volumes associated to a type are first removed before
331 attempting to remove the type itself. This includes any image volume
332 cache volumes stored in a separate tenant to the original volumes
333 created from the type.
334 """
335 admin_volume_type_client = self.os_admin.volume_types_client_latest
336 admin_volumes_client = self.os_admin.volumes_client_latest
337 volumes = admin_volumes_client.list_volumes(
338 detail=True, params={'all_tenants': 1})['volumes']
339 type_name = volume_type['name']
340 for volume in [v for v in volumes if v['volume_type'] == type_name]:
341 test_utils.call_and_ignore_notfound_exc(
342 admin_volumes_client.delete_volume, volume['id'])
343 admin_volumes_client.wait_for_resource_deletion(volume['id'])
344 admin_volume_type_client.delete_volume_type(volume_type['id'])
345
scottda61f68ac2016-06-07 12:07:55 -0600346 def create_volume_type(self, client=None, name=None, backend_name=None):
347 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000348 client = self.os_admin.volume_types_client_latest
lkuchlane3e7b272018-05-23 13:16:06 +0300349 randomized_name = name or data_utils.rand_name(
350 'volume-type-' + self.__class__.__name__)
scottda61f68ac2016-06-07 12:07:55 -0600351
352 LOG.debug("Creating a volume type: %s on backend %s",
353 randomized_name, backend_name)
354 extra_specs = {}
355 if backend_name:
356 extra_specs = {"volume_backend_name": backend_name}
357
lkuchlanbbabe542017-09-26 10:47:23 +0300358 volume_type = client.create_volume_type(
359 name=randomized_name, extra_specs=extra_specs)['volume_type']
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100360 self.addCleanup(self._cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600361 return volume_type
362
Yair Fried1fc32a12014-08-04 09:11:30 +0300363 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500364 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500365 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100366 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900367 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100368 for sg in sgs:
369 if sg['name'] == 'default':
370 secgroup_id = sg['id']
371
372 # These rules are intended to permit inbound ssh and icmp
373 # traffic from all sources, so no group_id is provided.
374 # Setting a group_id would only permit traffic from ports
375 # belonging to the same security group.
376 rulesets = [
377 {
378 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000379 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100380 'from_port': 22,
381 'to_port': 22,
382 'cidr': '0.0.0.0/0',
383 },
384 {
385 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000386 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100387 'from_port': -1,
388 'to_port': -1,
389 'cidr': '0.0.0.0/0',
390 }
391 ]
392 rules = list()
393 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000394 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900395 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100396 rules.append(sg_rule)
397 return rules
398
Yair Fried1fc32a12014-08-04 09:11:30 +0300399 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100400 # Create security group
401 sg_name = data_utils.rand_name(self.__class__.__name__)
402 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500403 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900404 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100405 self.assertEqual(secgroup['name'], sg_name)
406 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500407 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100408 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500409 self.compute_security_groups_client.delete_security_group,
410 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100411
412 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300413 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100414
415 return secgroup
416
zhuflf52c7592017-05-25 13:55:24 +0800417 def get_remote_client(self, ip_address, username=None, private_key=None,
418 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100419 """Get a SSH client to a remote server
420
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600421 :param ip_address: the server floating or fixed IP address to use
422 for ssh validation
423 :param username: name of the Linux account on the remote server
424 :param private_key: the SSH private key to use
425 :param server: server dict, used for debugging purposes
426 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100427 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700428
Andrea Frittoli247058f2014-07-16 16:09:22 +0100429 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800430 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800431 # Set this with 'keypair' or others to log in with keypair or
432 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000433 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800434 password = None
435 if private_key is None:
436 private_key = self.keypair['private_key']
437 else:
lanoux283273b2015-12-04 03:01:54 -0800438 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800439 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800440 linux_client = remote_client.RemoteClient(
441 ip_address, username, pkey=private_key, password=password,
442 server=server, servers_client=self.servers_client)
443 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100444 return linux_client
445
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000446 def _image_create(self, name, fmt, path,
447 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900448 if properties is None:
449 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100450 name = data_utils.rand_name('%s-' % name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100451 params = {
452 'name': name,
453 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000454 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100455 }
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400456 if CONF.image_feature_enabled.api_v1:
457 params['is_public'] = 'False'
458 params['properties'] = properties
Ken'ichi Ohmichi02bcdf32016-06-17 16:41:26 -0700459 params = {'headers': common_image.image_meta_to_headers(**params)}
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400460 else:
461 params['visibility'] = 'private'
462 # Additional properties are flattened out in the v2 API.
463 params.update(properties)
464 body = self.image_client.create_image(**params)
465 image = body['image'] if 'image' in body else body
Andrea Frittoli247058f2014-07-16 16:09:22 +0100466 self.addCleanup(self.image_client.delete_image, image['id'])
467 self.assertEqual("queued", image['status'])
zhang.leia4b1cef2016-03-01 10:50:01 +0800468 with open(path, 'rb') as image_file:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400469 if CONF.image_feature_enabled.api_v1:
470 self.image_client.update_image(image['id'], data=image_file)
471 else:
472 self.image_client.store_image_file(image['id'], image_file)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100473 return image['id']
474
475 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300476 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100477 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
478 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
479 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300480 img_container_format = CONF.scenario.img_container_format
481 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000482 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400483 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Jordan Pittier525ec712016-12-07 17:51:26 +0100484 "properties: %s, ami: %s, ari: %s, aki: %s",
485 img_path, img_container_format, img_disk_format,
486 img_properties, ami_img_path, ari_img_path, aki_img_path)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100487 try:
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100488 image = self._image_create('scenario-img',
489 img_container_format,
490 img_path,
491 disk_format=img_disk_format,
492 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100493 except IOError:
Ryan Hsue5107be2015-12-03 13:55:03 -0800494 LOG.warning(
495 "A(n) %s image was not found. Retrying with uec image.",
496 img_disk_format)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100497 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
498 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000499 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100500 image = self._image_create('scenario-ami', 'ami',
501 path=ami_img_path,
502 properties=properties)
Jordan Pittier525ec712016-12-07 17:51:26 +0100503 LOG.debug("image:%s", image)
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100504
505 return image
Andrea Frittoli247058f2014-07-16 16:09:22 +0100506
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700507 def _log_console_output(self, servers=None, client=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400508 if not CONF.compute_feature_enabled.console_output:
509 LOG.debug('Console output not supported, cannot log')
510 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700511 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100512 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700513 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100514 servers = servers['servers']
515 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100516 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700517 console_output = client.get_console_output(
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100518 server['id'])['output']
519 LOG.debug('Console output for %s\nbody=\n%s',
520 server['id'], console_output)
521 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100522 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100523 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100524
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000525 def _log_net_info(self, exc):
526 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300527 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000528 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000529
nithya-ganesan882595e2014-07-29 18:51:07 +0000530 def create_server_snapshot(self, server, name=None):
531 # Glance client
532 _image_client = self.image_client
533 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900534 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000535 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800536 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000537 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000538 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500539 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300540 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200541
542 self.addCleanup(_image_client.wait_for_resource_deletion,
543 image_id)
544 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
545 _image_client.delete_image, image_id)
546
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400547 if CONF.image_feature_enabled.api_v1:
548 # In glance v1 the additional properties are stored in the headers.
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700549 resp = _image_client.check_image(image_id)
550 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400551 image_props = snapshot_image.get('properties', {})
552 else:
553 # In glance v2 the additional properties are flattened.
554 snapshot_image = _image_client.show_image(image_id)
555 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300556
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400557 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300558 if bdm:
559 bdm = json.loads(bdm)
560 if bdm and 'snapshot_id' in bdm[0]:
561 snapshot_id = bdm[0]['snapshot_id']
562 self.addCleanup(
563 self.snapshots_client.wait_for_resource_deletion,
564 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100565 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
566 self.snapshots_client.delete_snapshot,
567 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200568 waiters.wait_for_volume_resource_status(self.snapshots_client,
569 snapshot_id,
570 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000571 image_name = snapshot_image['name']
572 self.assertEqual(name, image_name)
573 LOG.debug("Created snapshot image %s for server %s",
574 image_name, server['name'])
575 return snapshot_image
576
Jordan Pittier7cf64762015-10-14 15:01:12 +0200577 def nova_volume_attach(self, server, volume_to_attach):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000578 volume = self.servers_client.attach_volume(
Jordan Pittier7cf64762015-10-14 15:01:12 +0200579 server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900580 % CONF.compute.volume_device_name)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200581 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200582 waiters.wait_for_volume_resource_status(self.volumes_client,
583 volume['id'], 'in-use')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900584
Jordan Pittier7cf64762015-10-14 15:01:12 +0200585 # Return the updated volume after the attachment
586 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900587
Jordan Pittier7cf64762015-10-14 15:01:12 +0200588 def nova_volume_detach(self, server, volume):
589 self.servers_client.detach_volume(server['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200590 waiters.wait_for_volume_resource_status(self.volumes_client,
591 volume['id'], 'available')
Jordan Pittier7cf64762015-10-14 15:01:12 +0200592
Steven Hardyda2a8352014-10-02 12:52:20 +0100593 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800594 ping_timeout=None, mtu=None, server=None):
lanoux5fc14522015-09-21 08:17:35 +0000595 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000596 cmd = ['ping', '-c1', '-w1']
597
598 if mtu:
599 cmd += [
600 # don't fragment
601 '-M', 'do',
602 # ping receives just the size of ICMP payload
603 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
604 ]
605 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700606
607 def ping():
608 proc = subprocess.Popen(cmd,
609 stdout=subprocess.PIPE,
610 stderr=subprocess.PIPE)
611 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000612
Aaron Rosena7df13b2014-09-23 09:45:45 -0700613 return (proc.returncode == 0) == should_succeed
614
Jordan Pittier9e227c52016-02-09 14:35:18 +0100615 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000616 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800617 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000618 'caller': caller, 'ip': ip_address, 'timeout': timeout,
619 'should_succeed':
620 'reachable' if should_succeed else 'unreachable'
621 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200622 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000623 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800624 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000625 'caller': caller, 'ip': ip_address, 'timeout': timeout,
626 'result': 'expected' if result else 'unexpected'
627 })
zhufl0ec74c42017-11-15 14:02:28 +0800628 if server:
629 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000630 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700631
Yair Friedae0e73d2014-11-24 11:56:26 +0200632 def check_vm_connectivity(self, ip_address,
633 username=None,
634 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000635 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800636 extra_msg="",
637 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000638 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000639 """Check server connectivity
640
Yair Friedae0e73d2014-11-24 11:56:26 +0200641 :param ip_address: server to test against
642 :param username: server's ssh username
643 :param private_key: server's ssh private key to be used
644 :param should_connect: True/False indicates positive/negative test
645 positive - attempt ping and ssh
646 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800647 :param extra_msg: Message to help with debugging if ``ping_ip_address``
648 fails
649 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000650 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200651
652 :raises: AssertError if the result of the connectivity check does
653 not match the value of the should_connect param
654 """
zhufl0ec74c42017-11-15 14:02:28 +0800655 LOG.debug('checking network connections to IP %s with user: %s',
656 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200657 if should_connect:
658 msg = "Timed out waiting for %s to become reachable" % ip_address
659 else:
660 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800661 if extra_msg:
662 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200663 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000664 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800665 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200666 msg=msg)
667 if should_connect:
668 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800669 try:
670 self.get_remote_client(ip_address, username, private_key,
671 server=server)
672 except Exception:
673 if not extra_msg:
674 extra_msg = 'Failed to ssh to %s' % ip_address
675 LOG.exception(extra_msg)
676 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200677
678 def create_floating_ip(self, thing, pool_name=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000679 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200680
Marc Koderer3b57d802016-03-22 15:23:31 +0100681 if not pool_name:
682 pool_name = CONF.network.floating_network_name
John Warrene74890a2015-11-11 15:18:01 -0500683 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000684 create_floating_ip(pool=pool_name)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100685 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500686 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200687 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500688 self.compute_floating_ips_client.associate_floating_ip_to_server(
Yair Friedae0e73d2014-11-24 11:56:26 +0200689 floating_ip['ip'], thing['id'])
690 return floating_ip
691
Sean Dague20e98612016-01-06 14:33:28 -0500692 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200693 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500694 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200695 private_key=private_key,
696 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300697 if dev_name is not None:
698 ssh_client.make_fs(dev_name)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800699 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
700 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300701 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
702 ssh_client.exec_command(cmd_timestamp)
703 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
704 % mount_path)
705 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800706 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300707 return timestamp
708
Sean Dague20e98612016-01-06 14:33:28 -0500709 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200710 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500711 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200712 private_key=private_key,
713 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300714 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700715 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300716 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
717 % mount_path)
718 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800719 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300720 return timestamp
721
Sean Dague20e98612016-01-06 14:33:28 -0500722 def get_server_ip(self, server):
723 """Get the server fixed or floating IP.
724
725 Based on the configuration we're in, return a correct ip
726 address for validating that a guest is up.
727 """
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200728 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500729 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800730 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500731 # method is creating the floating IP there.
732 return self.create_floating_ip(server)['ip']
733 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400734 # Determine the network name to look for based on config or creds
735 # provider network resources.
736 if CONF.validation.network_for_ssh:
737 addresses = server['addresses'][
738 CONF.validation.network_for_ssh]
739 else:
zhufl7b4a7202017-09-28 10:29:27 +0800740 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400741 addresses = (server['addresses'][network['name']]
742 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500743 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200744 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
745 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500746 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800747 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200748 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400749 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200750
zhufl7bc916d2018-08-22 14:47:39 +0800751 @classmethod
752 def get_host_for_server(cls, server_id):
753 server_details = cls.os_admin.servers_client.show_server(server_id)
754 return server_details['server']['OS-EXT-SRV-ATTR:host']
755
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100756
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100757class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300758 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000759
Yair Fried1fc32a12014-08-04 09:11:30 +0300760 This class provide helpers for network scenario tests, using the neutron
761 API. Helpers from ancestor which use the nova network API are overridden
762 with the neutron API.
763
764 This Class also enforces using Neutron instead of novanetwork.
765 Subclassed tests will be skipped if Neutron is not enabled
766
767 """
768
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000769 credentials = ['primary', 'admin']
770
Yair Fried1fc32a12014-08-04 09:11:30 +0300771 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000772 def skip_checks(cls):
773 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100774 if not CONF.service_available.neutron:
775 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300776
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700777 def _create_network(self, networks_client=None,
zhoubin5058bead72017-02-04 18:01:15 +0800778 tenant_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200779 namestart='network-smoke-',
780 port_security_enabled=True):
John Warren94d8faf2015-09-15 12:22:24 -0400781 if not networks_client:
782 networks_client = self.networks_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300783 if not tenant_id:
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700784 tenant_id = networks_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300785 name = data_utils.rand_name(namestart)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400786 network_kwargs = dict(name=name, tenant_id=tenant_id)
787 # Neutron disables port security by default so we have to check the
788 # config before trying to create the network with port_security_enabled
789 if CONF.network_feature_enabled.port_security:
790 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +0200791 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -0500792 network = result['network']
793
794 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100795 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800796 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -0500797 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300798 return network
799
zhufl5b0a52f2017-10-24 15:48:20 +0800800 def create_subnet(self, network, subnets_client=None,
801 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000802 """Create a subnet for the given network
803
804 within the cidr block configured for tenant networks.
Yair Fried1fc32a12014-08-04 09:11:30 +0300805 """
John Warren3961acd2015-10-02 14:38:53 -0400806 if not subnets_client:
807 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300808
809 def cidr_in_use(cidr, tenant_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000810 """Check cidr existence
811
lei zhangdd552b22015-11-25 20:41:48 +0800812 :returns: True if subnet with cidr already exist in tenant
813 False else
Yair Fried1fc32a12014-08-04 09:11:30 +0300814 """
jeremy.zhang5870ff12017-05-25 11:24:23 +0800815 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100816 tenant_id=tenant_id, cidr=cidr)['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300817 return len(cidr_in_use) != 0
818
Kirill Shileev14113572014-11-21 16:58:02 +0300819 ip_version = kwargs.pop('ip_version', 4)
820
821 if ip_version == 6:
822 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -0400823 CONF.network.project_network_v6_cidr)
824 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300825 else:
Sean Dagueed6e5862016-04-04 10:49:13 -0400826 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
827 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300828
Yair Fried1fc32a12014-08-04 09:11:30 +0300829 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300830 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300831 # Repeatedly attempt subnet creation with sequential cidr
832 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300833 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300834 str_cidr = str(subnet_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -0500835 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300836 continue
837
838 subnet = dict(
839 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -0500840 network_id=network['id'],
841 tenant_id=network['tenant_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +0300842 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300843 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300844 **kwargs
845 )
846 try:
John Warren3961acd2015-10-02 14:38:53 -0400847 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300848 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900849 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300850 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
851 if not is_overlapping_cidr:
852 raise
853 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -0500854
855 subnet = result['subnet']
856 self.assertEqual(subnet['cidr'], str_cidr)
857
858 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
859 subnets_client.delete_subnet, subnet['id'])
860
Yair Fried1fc32a12014-08-04 09:11:30 +0300861 return subnet
862
Kirill Shileev14113572014-11-21 16:58:02 +0300863 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Hongbin Lu95a31692018-06-13 23:17:54 +0000864 if ip_addr:
865 ports = self.os_admin.ports_client.list_ports(
866 device_id=server['id'],
867 fixed_ips='ip_address=%s' % ip_addr)['ports']
868 else:
869 ports = self.os_admin.ports_client.list_ports(
870 device_id=server['id'])['ports']
Kobi Samoray166500a2016-10-09 14:42:48 +0300871 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -0500872 # If the network is dual-stack (IPv4 + IPv6), this port is associated
873 # with 2 subnets
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300874 p_status = ['ACTIVE']
875 # NOTE(vsaienko) With Ironic, instances live on separate hardware
876 # servers. Neutron does not bind ports for Ironic instances, as a
877 # result the port remains in the DOWN state.
Vasyl Saienkoc8aa34b2016-08-01 14:18:37 +0300878 # TODO(vsaienko) remove once bug: #1599836 is resolved.
Thiago Paiva66cded22016-08-15 14:55:58 -0300879 if getattr(CONF.service_available, 'ironic', False):
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300880 p_status.append('DOWN')
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200881 port_map = [(p["id"], fxip["ip_address"])
882 for p in ports
883 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200884 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
885 p['status'] in p_status)]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -0800886 inactive = [p for p in ports if p['status'] != 'ACTIVE']
887 if inactive:
888 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200889
Masayuki Igawaf9009b42017-04-10 14:49:29 +0900890 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -0800891 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200892 self.assertEqual(len(port_map), 1,
893 "Found multiple IPv4 addresses: %s. "
894 "Unable to determine which port to target."
895 % port_map)
896 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300897
David Shrewsbury9bac3662014-08-07 15:07:01 -0400898 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +0800899 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100900 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +0200901 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700902 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -0500903 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -0400904
Yair Friedae0e73d2014-11-24 11:56:26 +0200905 def create_floating_ip(self, thing, external_network_id=None,
906 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000907 """Create a floating IP and associates to a resource/port on Neutron"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200908 if not external_network_id:
909 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300910 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -0500911 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300912 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300913 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
914 else:
915 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500916 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300917 floating_network_id=external_network_id,
918 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300919 tenant_id=thing['tenant_id'],
920 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300921 )
Steve Heyman33735f22016-05-24 09:28:08 -0500922 floating_ip = result['floatingip']
Jordan Pittier9e227c52016-02-09 14:35:18 +0100923 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800924 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -0500925 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300926 return floating_ip
927
Yair Fried45f92952014-06-26 05:19:19 +0300928 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000929 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300930
Steve Heyman33735f22016-05-24 09:28:08 -0500931 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +0300932 :param status: target status
933 :raises: AssertionError if status doesn't match
934 """
Steve Heyman33735f22016-05-24 09:28:08 -0500935 floatingip_id = floating_ip['id']
936
Carl Baldwina754e2d2014-10-23 22:47:41 +0000937 def refresh():
Steve Heyman33735f22016-05-24 09:28:08 -0500938 result = (self.floating_ips_client.
939 show_floatingip(floatingip_id)['floatingip'])
940 return status == result['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +0000941
zhufl4dda94e2017-03-14 16:14:46 +0800942 if not test_utils.call_until_true(refresh,
943 CONF.network.build_timeout,
944 CONF.network.build_interval):
945 floating_ip = self.floating_ips_client.show_floatingip(
946 floatingip_id)['floatingip']
947 self.assertEqual(status, floating_ip['status'],
948 message="FloatingIP: {fp} is at status: {cst}. "
949 "failed to reach status: {st}"
950 .format(fp=floating_ip, cst=floating_ip['status'],
951 st=status))
Yair Fried45f92952014-06-26 05:19:19 +0300952 LOG.info("FloatingIP: {fp} is at status: {st}"
953 .format(fp=floating_ip, st=status))
954
zhufl420a0192017-09-28 11:04:50 +0800955 def check_tenant_network_connectivity(self, server,
956 username,
957 private_key,
958 should_connect=True,
959 servers_for_debug=None):
Sean Dagueed6e5862016-04-04 10:49:13 -0400960 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +0300961 msg = 'Tenant networks not configured to be reachable.'
962 LOG.info(msg)
963 return
964 # The target login is assumed to have been configured for
965 # key-based authentication by cloud-init.
966 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +0100967 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +0300968 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900969 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200970 username,
971 private_key,
972 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300973 except Exception as e:
974 LOG.exception('Tenant network connectivity check failed')
975 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000976 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300977 raise
978
zhufle9877c62017-10-13 09:38:19 +0800979 def check_remote_connectivity(self, source, dest, should_succeed=True,
980 nic=None):
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900981 """assert ping server via source ssh connection
982
Yair Fried1fc32a12014-08-04 09:11:30 +0300983 :param source: RemoteClient: an ssh connection from which to ping
zhufle9877c62017-10-13 09:38:19 +0800984 :param dest: an IP to ping against
985 :param should_succeed: boolean: should ping succeed or not
Yair Friedbc46f592015-11-18 16:29:34 +0200986 :param nic: specific network interface to ping from
Yair Fried1fc32a12014-08-04 09:11:30 +0300987 """
988 def ping_remote():
989 try:
Yair Friedbc46f592015-11-18 16:29:34 +0200990 source.ping_host(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +0300991 except lib_exc.SSHExecCommandFailed:
zhangguoqing6c096642016-01-04 06:17:21 +0000992 LOG.warning('Failed to ping IP: %s via a ssh connection '
Jordan Pittier525ec712016-12-07 17:51:26 +0100993 'from: %s.', dest, source.ssh_client.host)
Yair Fried1fc32a12014-08-04 09:11:30 +0300994 return not should_succeed
995 return should_succeed
996
zhufle9877c62017-10-13 09:38:19 +0800997 result = test_utils.call_until_true(ping_remote,
998 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -0800999 if result:
1000 return
1001
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001002 source_host = source.ssh_client.host
1003 if should_succeed:
1004 msg = "Timed out waiting for %s to become reachable from %s" \
1005 % (dest, source_host)
1006 else:
1007 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001008 self._log_console_output()
1009 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001010
John Warren456d9ae2016-01-12 15:36:33 -05001011 def _create_security_group(self, security_group_rules_client=None,
1012 tenant_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001013 namestart='secgroup-smoke',
1014 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001015 if security_group_rules_client is None:
1016 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001017 if security_groups_client is None:
1018 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001019 if tenant_id is None:
John Warrenf9606e92015-12-10 12:12:42 -05001020 tenant_id = security_groups_client.tenant_id
1021 secgroup = self._create_empty_security_group(
1022 namestart=namestart, client=security_groups_client,
1023 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001024
1025 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -05001026 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001027 security_group_rules_client=security_group_rules_client,
1028 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001029 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001030 for rule in rules:
Steve Heyman33735f22016-05-24 09:28:08 -05001031 self.assertEqual(tenant_id, rule['tenant_id'])
1032 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001033 return secgroup
1034
Yair Frieddb6c9e92014-08-06 08:53:13 +03001035 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001036 namestart='secgroup-smoke'):
1037 """Create a security group without rules.
1038
1039 Default rules will be created:
1040 - IPv4 egress to any
1041 - IPv6 egress to any
1042
1043 :param tenant_id: secgroup will be created in this tenant
Steve Heyman33735f22016-05-24 09:28:08 -05001044 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001045 """
1046 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001047 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001048 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001049 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001050 sg_name = data_utils.rand_name(namestart)
1051 sg_desc = sg_name + " description"
1052 sg_dict = dict(name=sg_name,
1053 description=sg_desc)
1054 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -05001055 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001056
1057 secgroup = result['security_group']
1058 self.assertEqual(secgroup['name'], sg_name)
1059 self.assertEqual(tenant_id, secgroup['tenant_id'])
1060 self.assertEqual(secgroup['description'], sg_desc)
1061
Jordan Pittier9e227c52016-02-09 14:35:18 +01001062 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001063 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001064 return secgroup
1065
John Warren456d9ae2016-01-12 15:36:33 -05001066 def _create_security_group_rule(self, secgroup=None,
1067 sec_group_rules_client=None,
John Warrenf9606e92015-12-10 12:12:42 -05001068 tenant_id=None,
1069 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001070 """Create a rule from a dictionary of rule parameters.
1071
1072 Create a rule in a secgroup. if secgroup not defined will search for
1073 default secgroup in tenant_id.
1074
Steve Heyman33735f22016-05-24 09:28:08 -05001075 :param secgroup: the security group.
Yair Fried1fc32a12014-08-04 09:11:30 +03001076 :param tenant_id: if secgroup not passed -- the tenant in which to
1077 search for default secgroup
1078 :param kwargs: a dictionary containing rule parameters:
1079 for example, to allow incoming ssh:
1080 rule = {
1081 direction: 'ingress'
1082 protocol:'tcp',
1083 port_range_min: 22,
1084 port_range_max: 22
1085 }
1086 """
John Warren456d9ae2016-01-12 15:36:33 -05001087 if sec_group_rules_client is None:
1088 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001089 if security_groups_client is None:
1090 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001091 if not tenant_id:
John Warrenf9606e92015-12-10 12:12:42 -05001092 tenant_id = security_groups_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001093 if secgroup is None:
zhuflb0b272e2017-09-22 16:01:46 +08001094 # Get default secgroup for tenant_id
1095 default_secgroups = security_groups_client.list_security_groups(
1096 name='default', tenant_id=tenant_id)['security_groups']
1097 msg = "No default security group for tenant %s." % (tenant_id)
1098 self.assertNotEmpty(default_secgroups, msg)
1099 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001100
Steve Heyman33735f22016-05-24 09:28:08 -05001101 ruleset = dict(security_group_id=secgroup['id'],
1102 tenant_id=secgroup['tenant_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001103 ruleset.update(kwargs)
1104
John Warren456d9ae2016-01-12 15:36:33 -05001105 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001106 sg_rule = sg_rule['security_group_rule']
1107
1108 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1109 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001110
1111 return sg_rule
1112
John Warren456d9ae2016-01-12 15:36:33 -05001113 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1114 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001115 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001116 """Create loginable security group rule
1117
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001118 This function will create:
1119 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1120 access for ipv4.
1121 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1122 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001123 """
1124
John Warren456d9ae2016-01-12 15:36:33 -05001125 if security_group_rules_client is None:
1126 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001127 if security_groups_client is None:
1128 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001129 rules = []
1130 rulesets = [
1131 dict(
1132 # ssh
1133 protocol='tcp',
1134 port_range_min=22,
1135 port_range_max=22,
1136 ),
1137 dict(
1138 # ping
1139 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001140 ),
1141 dict(
1142 # ipv6-icmp for ping6
1143 protocol='icmp',
1144 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001145 )
1146 ]
John Warren456d9ae2016-01-12 15:36:33 -05001147 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001148 for ruleset in rulesets:
1149 for r_direction in ['ingress', 'egress']:
1150 ruleset['direction'] = r_direction
1151 try:
1152 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001153 sec_group_rules_client=sec_group_rules_client,
1154 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001155 security_groups_client=security_groups_client,
1156 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001157 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001158 # if rule already exist - skip rule and continue
1159 msg = 'Security group rule already exists'
1160 if msg not in ex._error_string:
1161 raise ex
1162 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001163 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001164 rules.append(sg_rule)
1165
1166 return rules
1167
Yair Frieddb6c9e92014-08-06 08:53:13 +03001168 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001169 """Retrieve a router for the given tenant id.
1170
1171 If a public router has been configured, it will be returned.
1172
1173 If a public router has not been configured, but a public
1174 network has, a tenant router will be created and returned that
1175 routes traffic to the public network.
1176 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001177 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001178 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001179 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001180 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001181 router_id = CONF.network.public_router_id
1182 network_id = CONF.network.public_network_id
1183 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001184 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001185 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001186 elif network_id:
zhufl3484f992017-10-10 16:18:29 +08001187 router = client.create_router(
1188 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
1189 admin_state_up=True,
1190 tenant_id=tenant_id,
1191 external_gateway_info=dict(network_id=network_id))['router']
1192 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1193 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001194 return router
1195 else:
1196 raise Exception("Neither of 'public_router_id' or "
1197 "'public_network_id' has been defined.")
1198
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001199 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001200 routers_client=None, subnets_client=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001201 tenant_id=None, dns_nameservers=None,
1202 port_security_enabled=True):
Yair Fried1fc32a12014-08-04 09:11:30 +03001203 """Create a network with a subnet connected to a router.
1204
David Shrewsbury9bac3662014-08-07 15:07:01 -04001205 The baremetal driver is a special case since all nodes are
1206 on the same shared network.
1207
Yair Fried413bf2d2014-11-19 17:07:11 +02001208 :param tenant_id: id of tenant to create resources in.
1209 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001210 :returns: network, subnet, router
1211 """
Thiago Paiva66cded22016-08-15 14:55:58 -03001212 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001213 # NOTE(Shrews): This exception is for environments where tenant
1214 # credential isolation is available, but network separation is
1215 # not (the current baremetal case). Likely can be removed when
1216 # test account mgmt is reworked:
1217 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001218 if not CONF.compute.fixed_network_name:
1219 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001220 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001221 network = self._get_network_by_name(
1222 CONF.compute.fixed_network_name)
1223 router = None
1224 subnet = None
1225 else:
John Warren94d8faf2015-09-15 12:22:24 -04001226 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001227 networks_client=networks_client,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001228 tenant_id=tenant_id,
1229 port_security_enabled=port_security_enabled)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001230 router = self._get_router(client=routers_client,
1231 tenant_id=tenant_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001232 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001233 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001234 # use explicit check because empty list is a valid option
1235 if dns_nameservers is not None:
1236 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001237 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001238 if not routers_client:
1239 routers_client = self.routers_client
1240 router_id = router['id']
1241 routers_client.add_router_interface(router_id,
1242 subnet_id=subnet['id'])
1243
1244 # save a cleanup job to remove this association between
1245 # router and subnet
1246 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1247 routers_client.remove_router_interface, router_id,
1248 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001249 return network, subnet, router
1250
1251
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001252class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001253 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001254
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001255 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001256
1257 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001258 def setup_clients(cls):
1259 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001260 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001261 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001262 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001263
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001264 def create_encryption_type(self, client=None, type_id=None, provider=None,
1265 key_size=None, cipher=None,
1266 control_location=None):
1267 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001268 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001269 if not type_id:
1270 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001271 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001272 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001273 client.create_encryption_type(
1274 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001275 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001276
lkuchlan3023e752017-06-08 12:53:13 +03001277 def create_encrypted_volume(self, encryption_provider, volume_type,
1278 key_size=256, cipher='aes-xts-plain64',
1279 control_location='front-end'):
1280 volume_type = self.create_volume_type(name=volume_type)
1281 self.create_encryption_type(type_id=volume_type['id'],
1282 provider=encryption_provider,
1283 key_size=key_size,
1284 cipher=cipher,
1285 control_location=control_location)
1286 return self.create_volume(volume_type=volume_type['name'])
1287
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001288
Masayuki Igawa0870db52015-09-18 21:08:36 +09001289class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001290 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001291
1292 Subclasses implement the tests that use the methods provided by this
1293 class.
1294 """
1295
1296 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001297 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001298 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001299 if not CONF.service_available.swift:
1300 skip_msg = ("%s skipped as swift is not available" %
1301 cls.__name__)
1302 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001303
1304 @classmethod
1305 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001306 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001307 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001308 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001309 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001310
1311 @classmethod
1312 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001313 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001314 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001315 cls.account_client = cls.os_operator.account_client
1316 cls.container_client = cls.os_operator.container_client
1317 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001318
Chris Dentde456a12014-09-10 12:41:15 +01001319 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001320 """get swift status for our user account."""
1321 self.account_client.list_account_containers()
1322 LOG.debug('Swift status information obtained successfully')
1323
Chris Dentde456a12014-09-10 12:41:15 +01001324 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001325 name = container_name or data_utils.rand_name(
1326 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001327 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001328 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001329 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001330 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001331 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001332 self.container_client.delete_container,
1333 name)
Chris Dent0d494112014-08-26 13:48:30 +01001334 return name
1335
Chris Dentde456a12014-09-10 12:41:15 +01001336 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001337 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001338 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001339
Chris Dentde456a12014-09-10 12:41:15 +01001340 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001341 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001342 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001343 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001344 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001345 self.object_client.delete_object,
1346 container_name,
1347 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001348 return obj_name, obj_data
1349
Chris Dentde456a12014-09-10 12:41:15 +01001350 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001351 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001352 self.list_and_check_container_objects(container_name,
1353 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001354
Chris Dentde456a12014-09-10 12:41:15 +01001355 def list_and_check_container_objects(self, container_name,
1356 present_obj=None,
1357 not_present_obj=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001358 # List objects for a given container and assert which are present and
1359 # which are not.
Ghanshyam2a180b82014-06-16 13:54:22 +09001360 if present_obj is None:
1361 present_obj = []
1362 if not_present_obj is None:
1363 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001364 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001365 container_name)
1366 if present_obj:
1367 for obj in present_obj:
1368 self.assertIn(obj, object_list)
1369 if not_present_obj:
1370 for obj in not_present_obj:
1371 self.assertNotIn(obj, object_list)
1372
Chris Dentde456a12014-09-10 12:41:15 +01001373 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001374 _, obj = self.object_client.get_object(container_name, obj_name)
1375 self.assertEqual(obj, expected_data)