blob: 86d37f1796410b238f3cfaf66ed6cf70f143ac1f [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
Matt Riedemann514495b2019-05-04 17:34:12 +0000349 if not name:
350 class_name = self.__class__.__name__
351 name = data_utils.rand_name(class_name + '-volume-type')
352 randomized_name = data_utils.rand_name('scenario-type-' + name)
scottda61f68ac2016-06-07 12:07:55 -0600353
354 LOG.debug("Creating a volume type: %s on backend %s",
355 randomized_name, backend_name)
356 extra_specs = {}
357 if backend_name:
358 extra_specs = {"volume_backend_name": backend_name}
359
lkuchlanbbabe542017-09-26 10:47:23 +0300360 volume_type = client.create_volume_type(
361 name=randomized_name, extra_specs=extra_specs)['volume_type']
Lee Yarwoodbe64e1a2019-04-09 14:02:12 +0100362 self.addCleanup(self._cleanup_volume_type, volume_type)
scottda61f68ac2016-06-07 12:07:55 -0600363 return volume_type
364
Yair Fried1fc32a12014-08-04 09:11:30 +0300365 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500366 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500367 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100368 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900369 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100370 for sg in sgs:
371 if sg['name'] == 'default':
372 secgroup_id = sg['id']
373
374 # These rules are intended to permit inbound ssh and icmp
375 # traffic from all sources, so no group_id is provided.
376 # Setting a group_id would only permit traffic from ports
377 # belonging to the same security group.
378 rulesets = [
379 {
380 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000381 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100382 'from_port': 22,
383 'to_port': 22,
384 'cidr': '0.0.0.0/0',
385 },
386 {
387 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000388 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100389 'from_port': -1,
390 'to_port': -1,
391 'cidr': '0.0.0.0/0',
392 }
393 ]
394 rules = list()
395 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000396 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900397 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100398 rules.append(sg_rule)
399 return rules
400
Yair Fried1fc32a12014-08-04 09:11:30 +0300401 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100402 # Create security group
403 sg_name = data_utils.rand_name(self.__class__.__name__)
404 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500405 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900406 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100407 self.assertEqual(secgroup['name'], sg_name)
408 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500409 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100410 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500411 self.compute_security_groups_client.delete_security_group,
412 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100413
414 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300415 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100416
417 return secgroup
418
zhuflf52c7592017-05-25 13:55:24 +0800419 def get_remote_client(self, ip_address, username=None, private_key=None,
420 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100421 """Get a SSH client to a remote server
422
Sergey Vilgelmeac094a2018-11-21 18:27:51 -0600423 :param ip_address: the server floating or fixed IP address to use
424 for ssh validation
425 :param username: name of the Linux account on the remote server
426 :param private_key: the SSH private key to use
427 :param server: server dict, used for debugging purposes
428 :return: a RemoteClient object
JordanP3fe2dc32014-11-17 13:06:01 +0100429 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700430
Andrea Frittoli247058f2014-07-16 16:09:22 +0100431 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800432 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800433 # Set this with 'keypair' or others to log in with keypair or
434 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000435 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800436 password = None
437 if private_key is None:
438 private_key = self.keypair['private_key']
439 else:
lanoux283273b2015-12-04 03:01:54 -0800440 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800441 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800442 linux_client = remote_client.RemoteClient(
443 ip_address, username, pkey=private_key, password=password,
444 server=server, servers_client=self.servers_client)
445 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100446 return linux_client
447
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000448 def _image_create(self, name, fmt, path,
449 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900450 if properties is None:
451 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100452 name = data_utils.rand_name('%s-' % name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100453 params = {
454 'name': name,
455 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000456 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100457 }
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400458 if CONF.image_feature_enabled.api_v1:
459 params['is_public'] = 'False'
460 params['properties'] = properties
Ken'ichi Ohmichi02bcdf32016-06-17 16:41:26 -0700461 params = {'headers': common_image.image_meta_to_headers(**params)}
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400462 else:
463 params['visibility'] = 'private'
464 # Additional properties are flattened out in the v2 API.
465 params.update(properties)
466 body = self.image_client.create_image(**params)
467 image = body['image'] if 'image' in body else body
Andrea Frittoli247058f2014-07-16 16:09:22 +0100468 self.addCleanup(self.image_client.delete_image, image['id'])
469 self.assertEqual("queued", image['status'])
zhang.leia4b1cef2016-03-01 10:50:01 +0800470 with open(path, 'rb') as image_file:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400471 if CONF.image_feature_enabled.api_v1:
472 self.image_client.update_image(image['id'], data=image_file)
473 else:
474 self.image_client.store_image_file(image['id'], image_file)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100475 return image['id']
476
477 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300478 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100479 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
480 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
481 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300482 img_container_format = CONF.scenario.img_container_format
483 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000484 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400485 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Jordan Pittier525ec712016-12-07 17:51:26 +0100486 "properties: %s, ami: %s, ari: %s, aki: %s",
487 img_path, img_container_format, img_disk_format,
488 img_properties, ami_img_path, ari_img_path, aki_img_path)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100489 try:
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100490 image = self._image_create('scenario-img',
491 img_container_format,
492 img_path,
493 disk_format=img_disk_format,
494 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100495 except IOError:
Ryan Hsue5107be2015-12-03 13:55:03 -0800496 LOG.warning(
497 "A(n) %s image was not found. Retrying with uec image.",
498 img_disk_format)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100499 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
500 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000501 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100502 image = self._image_create('scenario-ami', 'ami',
503 path=ami_img_path,
504 properties=properties)
Jordan Pittier525ec712016-12-07 17:51:26 +0100505 LOG.debug("image:%s", image)
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100506
507 return image
Andrea Frittoli247058f2014-07-16 16:09:22 +0100508
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700509 def _log_console_output(self, servers=None, client=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400510 if not CONF.compute_feature_enabled.console_output:
511 LOG.debug('Console output not supported, cannot log')
512 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700513 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100514 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700515 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100516 servers = servers['servers']
517 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100518 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700519 console_output = client.get_console_output(
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100520 server['id'])['output']
521 LOG.debug('Console output for %s\nbody=\n%s',
522 server['id'], console_output)
523 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100524 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100525 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100526
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000527 def _log_net_info(self, exc):
528 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300529 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000530 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000531
nithya-ganesan882595e2014-07-29 18:51:07 +0000532 def create_server_snapshot(self, server, name=None):
533 # Glance client
534 _image_client = self.image_client
535 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900536 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000537 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800538 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000539 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000540 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500541 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300542 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200543
544 self.addCleanup(_image_client.wait_for_resource_deletion,
545 image_id)
546 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
547 _image_client.delete_image, image_id)
548
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400549 if CONF.image_feature_enabled.api_v1:
550 # In glance v1 the additional properties are stored in the headers.
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700551 resp = _image_client.check_image(image_id)
552 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400553 image_props = snapshot_image.get('properties', {})
554 else:
555 # In glance v2 the additional properties are flattened.
556 snapshot_image = _image_client.show_image(image_id)
557 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300558
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400559 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300560 if bdm:
561 bdm = json.loads(bdm)
562 if bdm and 'snapshot_id' in bdm[0]:
563 snapshot_id = bdm[0]['snapshot_id']
564 self.addCleanup(
565 self.snapshots_client.wait_for_resource_deletion,
566 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100567 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
568 self.snapshots_client.delete_snapshot,
569 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200570 waiters.wait_for_volume_resource_status(self.snapshots_client,
571 snapshot_id,
572 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000573 image_name = snapshot_image['name']
574 self.assertEqual(name, image_name)
575 LOG.debug("Created snapshot image %s for server %s",
576 image_name, server['name'])
577 return snapshot_image
578
Jordan Pittier7cf64762015-10-14 15:01:12 +0200579 def nova_volume_attach(self, server, volume_to_attach):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000580 volume = self.servers_client.attach_volume(
Jordan Pittier7cf64762015-10-14 15:01:12 +0200581 server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900582 % CONF.compute.volume_device_name)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200583 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200584 waiters.wait_for_volume_resource_status(self.volumes_client,
585 volume['id'], 'in-use')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900586
Jordan Pittier7cf64762015-10-14 15:01:12 +0200587 # Return the updated volume after the attachment
588 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900589
Jordan Pittier7cf64762015-10-14 15:01:12 +0200590 def nova_volume_detach(self, server, volume):
591 self.servers_client.detach_volume(server['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200592 waiters.wait_for_volume_resource_status(self.volumes_client,
593 volume['id'], 'available')
Jordan Pittier7cf64762015-10-14 15:01:12 +0200594
Steven Hardyda2a8352014-10-02 12:52:20 +0100595 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800596 ping_timeout=None, mtu=None, server=None):
lanoux5fc14522015-09-21 08:17:35 +0000597 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000598 cmd = ['ping', '-c1', '-w1']
599
600 if mtu:
601 cmd += [
602 # don't fragment
603 '-M', 'do',
604 # ping receives just the size of ICMP payload
605 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
606 ]
607 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700608
609 def ping():
610 proc = subprocess.Popen(cmd,
611 stdout=subprocess.PIPE,
612 stderr=subprocess.PIPE)
613 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000614
Aaron Rosena7df13b2014-09-23 09:45:45 -0700615 return (proc.returncode == 0) == should_succeed
616
Jordan Pittier9e227c52016-02-09 14:35:18 +0100617 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000618 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800619 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000620 'caller': caller, 'ip': ip_address, 'timeout': timeout,
621 'should_succeed':
622 'reachable' if should_succeed else 'unreachable'
623 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200624 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000625 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800626 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000627 'caller': caller, 'ip': ip_address, 'timeout': timeout,
628 'result': 'expected' if result else 'unexpected'
629 })
zhufl0ec74c42017-11-15 14:02:28 +0800630 if server:
631 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000632 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700633
Yair Friedae0e73d2014-11-24 11:56:26 +0200634 def check_vm_connectivity(self, ip_address,
635 username=None,
636 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000637 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800638 extra_msg="",
639 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000640 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000641 """Check server connectivity
642
Yair Friedae0e73d2014-11-24 11:56:26 +0200643 :param ip_address: server to test against
644 :param username: server's ssh username
645 :param private_key: server's ssh private key to be used
646 :param should_connect: True/False indicates positive/negative test
647 positive - attempt ping and ssh
648 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800649 :param extra_msg: Message to help with debugging if ``ping_ip_address``
650 fails
651 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000652 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200653
654 :raises: AssertError if the result of the connectivity check does
655 not match the value of the should_connect param
656 """
zhufl0ec74c42017-11-15 14:02:28 +0800657 LOG.debug('checking network connections to IP %s with user: %s',
658 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200659 if should_connect:
660 msg = "Timed out waiting for %s to become reachable" % ip_address
661 else:
662 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800663 if extra_msg:
664 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200665 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000666 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800667 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200668 msg=msg)
669 if should_connect:
670 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800671 try:
672 self.get_remote_client(ip_address, username, private_key,
673 server=server)
674 except Exception:
675 if not extra_msg:
676 extra_msg = 'Failed to ssh to %s' % ip_address
677 LOG.exception(extra_msg)
678 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200679
680 def create_floating_ip(self, thing, pool_name=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000681 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200682
Marc Koderer3b57d802016-03-22 15:23:31 +0100683 if not pool_name:
684 pool_name = CONF.network.floating_network_name
John Warrene74890a2015-11-11 15:18:01 -0500685 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000686 create_floating_ip(pool=pool_name)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100687 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500688 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200689 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500690 self.compute_floating_ips_client.associate_floating_ip_to_server(
Yair Friedae0e73d2014-11-24 11:56:26 +0200691 floating_ip['ip'], thing['id'])
692 return floating_ip
693
Sean Dague20e98612016-01-06 14:33:28 -0500694 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200695 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500696 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200697 private_key=private_key,
698 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300699 if dev_name is not None:
700 ssh_client.make_fs(dev_name)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800701 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
702 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300703 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
704 ssh_client.exec_command(cmd_timestamp)
705 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
706 % mount_path)
707 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800708 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300709 return timestamp
710
Sean Dague20e98612016-01-06 14:33:28 -0500711 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200712 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500713 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200714 private_key=private_key,
715 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300716 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700717 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300718 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
719 % mount_path)
720 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800721 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300722 return timestamp
723
Sean Dague20e98612016-01-06 14:33:28 -0500724 def get_server_ip(self, server):
725 """Get the server fixed or floating IP.
726
727 Based on the configuration we're in, return a correct ip
728 address for validating that a guest is up.
729 """
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200730 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500731 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800732 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500733 # method is creating the floating IP there.
734 return self.create_floating_ip(server)['ip']
735 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400736 # Determine the network name to look for based on config or creds
737 # provider network resources.
738 if CONF.validation.network_for_ssh:
739 addresses = server['addresses'][
740 CONF.validation.network_for_ssh]
741 else:
zhufl7b4a7202017-09-28 10:29:27 +0800742 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400743 addresses = (server['addresses'][network['name']]
744 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500745 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200746 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
747 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500748 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800749 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200750 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400751 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200752
zhufl7bc916d2018-08-22 14:47:39 +0800753 @classmethod
754 def get_host_for_server(cls, server_id):
755 server_details = cls.os_admin.servers_client.show_server(server_id)
756 return server_details['server']['OS-EXT-SRV-ATTR:host']
757
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100758
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100759class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300760 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000761
Yair Fried1fc32a12014-08-04 09:11:30 +0300762 This class provide helpers for network scenario tests, using the neutron
763 API. Helpers from ancestor which use the nova network API are overridden
764 with the neutron API.
765
766 This Class also enforces using Neutron instead of novanetwork.
767 Subclassed tests will be skipped if Neutron is not enabled
768
769 """
770
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000771 credentials = ['primary', 'admin']
772
Yair Fried1fc32a12014-08-04 09:11:30 +0300773 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000774 def skip_checks(cls):
775 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100776 if not CONF.service_available.neutron:
777 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300778
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700779 def _create_network(self, networks_client=None,
zhoubin5058bead72017-02-04 18:01:15 +0800780 tenant_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200781 namestart='network-smoke-',
782 port_security_enabled=True):
John Warren94d8faf2015-09-15 12:22:24 -0400783 if not networks_client:
784 networks_client = self.networks_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300785 if not tenant_id:
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700786 tenant_id = networks_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300787 name = data_utils.rand_name(namestart)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400788 network_kwargs = dict(name=name, tenant_id=tenant_id)
789 # Neutron disables port security by default so we have to check the
790 # config before trying to create the network with port_security_enabled
791 if CONF.network_feature_enabled.port_security:
792 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +0200793 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -0500794 network = result['network']
795
796 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100797 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800798 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -0500799 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300800 return network
801
zhufl5b0a52f2017-10-24 15:48:20 +0800802 def create_subnet(self, network, subnets_client=None,
803 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000804 """Create a subnet for the given network
805
806 within the cidr block configured for tenant networks.
Yair Fried1fc32a12014-08-04 09:11:30 +0300807 """
John Warren3961acd2015-10-02 14:38:53 -0400808 if not subnets_client:
809 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300810
811 def cidr_in_use(cidr, tenant_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000812 """Check cidr existence
813
lei zhangdd552b22015-11-25 20:41:48 +0800814 :returns: True if subnet with cidr already exist in tenant
815 False else
Yair Fried1fc32a12014-08-04 09:11:30 +0300816 """
jeremy.zhang5870ff12017-05-25 11:24:23 +0800817 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100818 tenant_id=tenant_id, cidr=cidr)['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300819 return len(cidr_in_use) != 0
820
Kirill Shileev14113572014-11-21 16:58:02 +0300821 ip_version = kwargs.pop('ip_version', 4)
822
823 if ip_version == 6:
824 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -0400825 CONF.network.project_network_v6_cidr)
826 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300827 else:
Sean Dagueed6e5862016-04-04 10:49:13 -0400828 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
829 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300830
Yair Fried1fc32a12014-08-04 09:11:30 +0300831 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300832 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300833 # Repeatedly attempt subnet creation with sequential cidr
834 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300835 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300836 str_cidr = str(subnet_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -0500837 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300838 continue
839
840 subnet = dict(
841 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -0500842 network_id=network['id'],
843 tenant_id=network['tenant_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +0300844 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300845 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300846 **kwargs
847 )
848 try:
John Warren3961acd2015-10-02 14:38:53 -0400849 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300850 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900851 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300852 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
853 if not is_overlapping_cidr:
854 raise
855 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -0500856
857 subnet = result['subnet']
858 self.assertEqual(subnet['cidr'], str_cidr)
859
860 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
861 subnets_client.delete_subnet, subnet['id'])
862
Yair Fried1fc32a12014-08-04 09:11:30 +0300863 return subnet
864
Kirill Shileev14113572014-11-21 16:58:02 +0300865 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Hongbin Lu95a31692018-06-13 23:17:54 +0000866 if ip_addr:
867 ports = self.os_admin.ports_client.list_ports(
868 device_id=server['id'],
869 fixed_ips='ip_address=%s' % ip_addr)['ports']
870 else:
871 ports = self.os_admin.ports_client.list_ports(
872 device_id=server['id'])['ports']
Kobi Samoray166500a2016-10-09 14:42:48 +0300873 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -0500874 # If the network is dual-stack (IPv4 + IPv6), this port is associated
875 # with 2 subnets
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300876 p_status = ['ACTIVE']
877 # NOTE(vsaienko) With Ironic, instances live on separate hardware
878 # servers. Neutron does not bind ports for Ironic instances, as a
879 # result the port remains in the DOWN state.
Vasyl Saienkoc8aa34b2016-08-01 14:18:37 +0300880 # TODO(vsaienko) remove once bug: #1599836 is resolved.
Thiago Paiva66cded22016-08-15 14:55:58 -0300881 if getattr(CONF.service_available, 'ironic', False):
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300882 p_status.append('DOWN')
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200883 port_map = [(p["id"], fxip["ip_address"])
884 for p in ports
885 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200886 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
887 p['status'] in p_status)]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -0800888 inactive = [p for p in ports if p['status'] != 'ACTIVE']
889 if inactive:
890 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200891
Masayuki Igawaf9009b42017-04-10 14:49:29 +0900892 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -0800893 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200894 self.assertEqual(len(port_map), 1,
895 "Found multiple IPv4 addresses: %s. "
896 "Unable to determine which port to target."
897 % port_map)
898 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300899
David Shrewsbury9bac3662014-08-07 15:07:01 -0400900 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +0800901 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100902 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +0200903 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700904 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -0500905 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -0400906
Yair Friedae0e73d2014-11-24 11:56:26 +0200907 def create_floating_ip(self, thing, external_network_id=None,
908 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000909 """Create a floating IP and associates to a resource/port on Neutron"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200910 if not external_network_id:
911 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300912 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -0500913 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300914 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300915 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
916 else:
917 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500918 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300919 floating_network_id=external_network_id,
920 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300921 tenant_id=thing['tenant_id'],
922 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300923 )
Steve Heyman33735f22016-05-24 09:28:08 -0500924 floating_ip = result['floatingip']
Jordan Pittier9e227c52016-02-09 14:35:18 +0100925 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800926 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -0500927 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300928 return floating_ip
929
Yair Fried45f92952014-06-26 05:19:19 +0300930 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000931 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300932
Steve Heyman33735f22016-05-24 09:28:08 -0500933 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +0300934 :param status: target status
935 :raises: AssertionError if status doesn't match
936 """
Steve Heyman33735f22016-05-24 09:28:08 -0500937 floatingip_id = floating_ip['id']
938
Carl Baldwina754e2d2014-10-23 22:47:41 +0000939 def refresh():
Steve Heyman33735f22016-05-24 09:28:08 -0500940 result = (self.floating_ips_client.
941 show_floatingip(floatingip_id)['floatingip'])
942 return status == result['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +0000943
zhufl4dda94e2017-03-14 16:14:46 +0800944 if not test_utils.call_until_true(refresh,
945 CONF.network.build_timeout,
946 CONF.network.build_interval):
947 floating_ip = self.floating_ips_client.show_floatingip(
948 floatingip_id)['floatingip']
949 self.assertEqual(status, floating_ip['status'],
950 message="FloatingIP: {fp} is at status: {cst}. "
951 "failed to reach status: {st}"
952 .format(fp=floating_ip, cst=floating_ip['status'],
953 st=status))
Yair Fried45f92952014-06-26 05:19:19 +0300954 LOG.info("FloatingIP: {fp} is at status: {st}"
955 .format(fp=floating_ip, st=status))
956
zhufl420a0192017-09-28 11:04:50 +0800957 def check_tenant_network_connectivity(self, server,
958 username,
959 private_key,
960 should_connect=True,
961 servers_for_debug=None):
Sean Dagueed6e5862016-04-04 10:49:13 -0400962 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +0300963 msg = 'Tenant networks not configured to be reachable.'
964 LOG.info(msg)
965 return
966 # The target login is assumed to have been configured for
967 # key-based authentication by cloud-init.
968 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +0100969 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +0300970 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900971 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200972 username,
973 private_key,
974 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300975 except Exception as e:
976 LOG.exception('Tenant network connectivity check failed')
977 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000978 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300979 raise
980
zhufle9877c62017-10-13 09:38:19 +0800981 def check_remote_connectivity(self, source, dest, should_succeed=True,
982 nic=None):
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900983 """assert ping server via source ssh connection
984
Yair Fried1fc32a12014-08-04 09:11:30 +0300985 :param source: RemoteClient: an ssh connection from which to ping
zhufle9877c62017-10-13 09:38:19 +0800986 :param dest: an IP to ping against
987 :param should_succeed: boolean: should ping succeed or not
Yair Friedbc46f592015-11-18 16:29:34 +0200988 :param nic: specific network interface to ping from
Yair Fried1fc32a12014-08-04 09:11:30 +0300989 """
990 def ping_remote():
991 try:
Yair Friedbc46f592015-11-18 16:29:34 +0200992 source.ping_host(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +0300993 except lib_exc.SSHExecCommandFailed:
zhangguoqing6c096642016-01-04 06:17:21 +0000994 LOG.warning('Failed to ping IP: %s via a ssh connection '
Jordan Pittier525ec712016-12-07 17:51:26 +0100995 'from: %s.', dest, source.ssh_client.host)
Yair Fried1fc32a12014-08-04 09:11:30 +0300996 return not should_succeed
997 return should_succeed
998
zhufle9877c62017-10-13 09:38:19 +0800999 result = test_utils.call_until_true(ping_remote,
1000 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001001 if result:
1002 return
1003
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001004 source_host = source.ssh_client.host
1005 if should_succeed:
1006 msg = "Timed out waiting for %s to become reachable from %s" \
1007 % (dest, source_host)
1008 else:
1009 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -08001010 self._log_console_output()
1011 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +09001012
John Warren456d9ae2016-01-12 15:36:33 -05001013 def _create_security_group(self, security_group_rules_client=None,
1014 tenant_id=None,
John Warrenf9606e92015-12-10 12:12:42 -05001015 namestart='secgroup-smoke',
1016 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -05001017 if security_group_rules_client is None:
1018 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001019 if security_groups_client is None:
1020 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001021 if tenant_id is None:
John Warrenf9606e92015-12-10 12:12:42 -05001022 tenant_id = security_groups_client.tenant_id
1023 secgroup = self._create_empty_security_group(
1024 namestart=namestart, client=security_groups_client,
1025 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001026
1027 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -05001028 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001029 security_group_rules_client=security_group_rules_client,
1030 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001031 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +03001032 for rule in rules:
Steve Heyman33735f22016-05-24 09:28:08 -05001033 self.assertEqual(tenant_id, rule['tenant_id'])
1034 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001035 return secgroup
1036
Yair Frieddb6c9e92014-08-06 08:53:13 +03001037 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +03001038 namestart='secgroup-smoke'):
1039 """Create a security group without rules.
1040
1041 Default rules will be created:
1042 - IPv4 egress to any
1043 - IPv6 egress to any
1044
1045 :param tenant_id: secgroup will be created in this tenant
Steve Heyman33735f22016-05-24 09:28:08 -05001046 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001047 """
1048 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001049 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001050 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001051 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001052 sg_name = data_utils.rand_name(namestart)
1053 sg_desc = sg_name + " description"
1054 sg_dict = dict(name=sg_name,
1055 description=sg_desc)
1056 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -05001057 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001058
1059 secgroup = result['security_group']
1060 self.assertEqual(secgroup['name'], sg_name)
1061 self.assertEqual(tenant_id, secgroup['tenant_id'])
1062 self.assertEqual(secgroup['description'], sg_desc)
1063
Jordan Pittier9e227c52016-02-09 14:35:18 +01001064 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001065 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001066 return secgroup
1067
John Warren456d9ae2016-01-12 15:36:33 -05001068 def _create_security_group_rule(self, secgroup=None,
1069 sec_group_rules_client=None,
John Warrenf9606e92015-12-10 12:12:42 -05001070 tenant_id=None,
1071 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001072 """Create a rule from a dictionary of rule parameters.
1073
1074 Create a rule in a secgroup. if secgroup not defined will search for
1075 default secgroup in tenant_id.
1076
Steve Heyman33735f22016-05-24 09:28:08 -05001077 :param secgroup: the security group.
Yair Fried1fc32a12014-08-04 09:11:30 +03001078 :param tenant_id: if secgroup not passed -- the tenant in which to
1079 search for default secgroup
1080 :param kwargs: a dictionary containing rule parameters:
1081 for example, to allow incoming ssh:
1082 rule = {
1083 direction: 'ingress'
1084 protocol:'tcp',
1085 port_range_min: 22,
1086 port_range_max: 22
1087 }
1088 """
John Warren456d9ae2016-01-12 15:36:33 -05001089 if sec_group_rules_client is None:
1090 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001091 if security_groups_client is None:
1092 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001093 if not tenant_id:
John Warrenf9606e92015-12-10 12:12:42 -05001094 tenant_id = security_groups_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001095 if secgroup is None:
zhuflb0b272e2017-09-22 16:01:46 +08001096 # Get default secgroup for tenant_id
1097 default_secgroups = security_groups_client.list_security_groups(
1098 name='default', tenant_id=tenant_id)['security_groups']
1099 msg = "No default security group for tenant %s." % (tenant_id)
1100 self.assertNotEmpty(default_secgroups, msg)
1101 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001102
Steve Heyman33735f22016-05-24 09:28:08 -05001103 ruleset = dict(security_group_id=secgroup['id'],
1104 tenant_id=secgroup['tenant_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001105 ruleset.update(kwargs)
1106
John Warren456d9ae2016-01-12 15:36:33 -05001107 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001108 sg_rule = sg_rule['security_group_rule']
1109
1110 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1111 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001112
1113 return sg_rule
1114
John Warren456d9ae2016-01-12 15:36:33 -05001115 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1116 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001117 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001118 """Create loginable security group rule
1119
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001120 This function will create:
1121 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1122 access for ipv4.
1123 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1124 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001125 """
1126
John Warren456d9ae2016-01-12 15:36:33 -05001127 if security_group_rules_client is None:
1128 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001129 if security_groups_client is None:
1130 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001131 rules = []
1132 rulesets = [
1133 dict(
1134 # ssh
1135 protocol='tcp',
1136 port_range_min=22,
1137 port_range_max=22,
1138 ),
1139 dict(
1140 # ping
1141 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001142 ),
1143 dict(
1144 # ipv6-icmp for ping6
1145 protocol='icmp',
1146 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001147 )
1148 ]
John Warren456d9ae2016-01-12 15:36:33 -05001149 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001150 for ruleset in rulesets:
1151 for r_direction in ['ingress', 'egress']:
1152 ruleset['direction'] = r_direction
1153 try:
1154 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001155 sec_group_rules_client=sec_group_rules_client,
1156 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001157 security_groups_client=security_groups_client,
1158 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001159 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001160 # if rule already exist - skip rule and continue
1161 msg = 'Security group rule already exists'
1162 if msg not in ex._error_string:
1163 raise ex
1164 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001165 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001166 rules.append(sg_rule)
1167
1168 return rules
1169
Yair Frieddb6c9e92014-08-06 08:53:13 +03001170 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001171 """Retrieve a router for the given tenant id.
1172
1173 If a public router has been configured, it will be returned.
1174
1175 If a public router has not been configured, but a public
1176 network has, a tenant router will be created and returned that
1177 routes traffic to the public network.
1178 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001179 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001180 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001181 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001182 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001183 router_id = CONF.network.public_router_id
1184 network_id = CONF.network.public_network_id
1185 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001186 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001187 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001188 elif network_id:
zhufl3484f992017-10-10 16:18:29 +08001189 router = client.create_router(
1190 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
1191 admin_state_up=True,
1192 tenant_id=tenant_id,
1193 external_gateway_info=dict(network_id=network_id))['router']
1194 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1195 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001196 return router
1197 else:
1198 raise Exception("Neither of 'public_router_id' or "
1199 "'public_network_id' has been defined.")
1200
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001201 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001202 routers_client=None, subnets_client=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001203 tenant_id=None, dns_nameservers=None,
1204 port_security_enabled=True):
Yair Fried1fc32a12014-08-04 09:11:30 +03001205 """Create a network with a subnet connected to a router.
1206
David Shrewsbury9bac3662014-08-07 15:07:01 -04001207 The baremetal driver is a special case since all nodes are
1208 on the same shared network.
1209
Yair Fried413bf2d2014-11-19 17:07:11 +02001210 :param tenant_id: id of tenant to create resources in.
1211 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001212 :returns: network, subnet, router
1213 """
Thiago Paiva66cded22016-08-15 14:55:58 -03001214 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001215 # NOTE(Shrews): This exception is for environments where tenant
1216 # credential isolation is available, but network separation is
1217 # not (the current baremetal case). Likely can be removed when
1218 # test account mgmt is reworked:
1219 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001220 if not CONF.compute.fixed_network_name:
1221 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001222 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001223 network = self._get_network_by_name(
1224 CONF.compute.fixed_network_name)
1225 router = None
1226 subnet = None
1227 else:
John Warren94d8faf2015-09-15 12:22:24 -04001228 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001229 networks_client=networks_client,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001230 tenant_id=tenant_id,
1231 port_security_enabled=port_security_enabled)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001232 router = self._get_router(client=routers_client,
1233 tenant_id=tenant_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001234 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001235 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001236 # use explicit check because empty list is a valid option
1237 if dns_nameservers is not None:
1238 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001239 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001240 if not routers_client:
1241 routers_client = self.routers_client
1242 router_id = router['id']
1243 routers_client.add_router_interface(router_id,
1244 subnet_id=subnet['id'])
1245
1246 # save a cleanup job to remove this association between
1247 # router and subnet
1248 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1249 routers_client.remove_router_interface, router_id,
1250 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001251 return network, subnet, router
1252
1253
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001254class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001255 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001256
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001257 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001258
1259 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001260 def setup_clients(cls):
1261 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001262 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001263 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001264 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001265
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001266 def create_encryption_type(self, client=None, type_id=None, provider=None,
1267 key_size=None, cipher=None,
1268 control_location=None):
1269 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001270 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001271 if not type_id:
1272 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001273 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001274 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001275 client.create_encryption_type(
1276 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001277 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001278
lkuchlan3023e752017-06-08 12:53:13 +03001279 def create_encrypted_volume(self, encryption_provider, volume_type,
1280 key_size=256, cipher='aes-xts-plain64',
1281 control_location='front-end'):
1282 volume_type = self.create_volume_type(name=volume_type)
1283 self.create_encryption_type(type_id=volume_type['id'],
1284 provider=encryption_provider,
1285 key_size=key_size,
1286 cipher=cipher,
1287 control_location=control_location)
1288 return self.create_volume(volume_type=volume_type['name'])
1289
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001290
Masayuki Igawa0870db52015-09-18 21:08:36 +09001291class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001292 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001293
1294 Subclasses implement the tests that use the methods provided by this
1295 class.
1296 """
1297
1298 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001299 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001300 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001301 if not CONF.service_available.swift:
1302 skip_msg = ("%s skipped as swift is not available" %
1303 cls.__name__)
1304 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001305
1306 @classmethod
1307 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001308 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001309 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001310 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001311 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001312
1313 @classmethod
1314 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001315 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001316 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001317 cls.account_client = cls.os_operator.account_client
1318 cls.container_client = cls.os_operator.container_client
1319 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001320
Chris Dentde456a12014-09-10 12:41:15 +01001321 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001322 """get swift status for our user account."""
1323 self.account_client.list_account_containers()
1324 LOG.debug('Swift status information obtained successfully')
1325
Chris Dentde456a12014-09-10 12:41:15 +01001326 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001327 name = container_name or data_utils.rand_name(
1328 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001329 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001330 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001331 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001332 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001333 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001334 self.container_client.delete_container,
1335 name)
Chris Dent0d494112014-08-26 13:48:30 +01001336 return name
1337
Chris Dentde456a12014-09-10 12:41:15 +01001338 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001339 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001340 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001341
Chris Dentde456a12014-09-10 12:41:15 +01001342 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001343 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001344 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001345 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001346 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001347 self.object_client.delete_object,
1348 container_name,
1349 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001350 return obj_name, obj_data
1351
Chris Dentde456a12014-09-10 12:41:15 +01001352 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001353 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001354 self.list_and_check_container_objects(container_name,
1355 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001356
Chris Dentde456a12014-09-10 12:41:15 +01001357 def list_and_check_container_objects(self, container_name,
1358 present_obj=None,
1359 not_present_obj=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001360 # List objects for a given container and assert which are present and
1361 # which are not.
Ghanshyam2a180b82014-06-16 13:54:22 +09001362 if present_obj is None:
1363 present_obj = []
1364 if not_present_obj is None:
1365 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001366 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001367 container_name)
1368 if present_obj:
1369 for obj in present_obj:
1370 self.assertIn(obj, object_list)
1371 if not_present_obj:
1372 for obj in not_present_obj:
1373 self.assertNotIn(obj, object_list)
1374
Chris Dentde456a12014-09-10 12:41:15 +01001375 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001376 _, obj = self.object_client.get_object(container_name, obj_name)
1377 self.assertEqual(obj, expected_data)