blob: 00d45cd886eee3a41c066fe1199bbb4fb849a76f [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.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100128 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100129
lanoux5fc14522015-09-21 08:17:35 +0000130 # NOTE(jlanoux): As a first step, ssh checks in the scenario
131 # tests need to be run regardless of the run_validation and
132 # validatable parameters and thus until the ssh validation job
133 # becomes voting in CI. The test resources management and IP
134 # association are taken care of in the scenario tests.
135 # Therefore, the validatable parameter is set to false in all
136 # those tests. In this way create_server just return a standard
137 # server and the scenario tests always perform ssh checks.
138
139 # Needed for the cross_tenant_traffic test:
140 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800141 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000142
zhufl24208c22016-10-25 15:23:48 +0800143 if name is None:
144 name = data_utils.rand_name(self.__class__.__name__ + "-server")
145
lanoux5fc14522015-09-21 08:17:35 +0000146 vnic_type = CONF.network.port_vnic_type
Moshe Levie2c2fb12017-08-31 15:18:58 +0300147 profile = CONF.network.port_profile
lanoux5fc14522015-09-21 08:17:35 +0000148
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000149 # If vnic_type or profile are configured create port for
lanoux5fc14522015-09-21 08:17:35 +0000150 # every network
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000151 if vnic_type or profile:
lanoux5fc14522015-09-21 08:17:35 +0000152 ports = []
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000153 create_port_body = {}
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300154
Lenny Verkhovskyfe3a03f2018-02-28 10:19:37 +0000155 if vnic_type:
156 create_port_body['binding:vnic_type'] = vnic_type
157
158 if profile:
159 create_port_body['binding:profile'] = profile
160
lanoux5fc14522015-09-21 08:17:35 +0000161 if kwargs:
162 # Convert security group names to security group ids
163 # to pass to create_port
164 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300165 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500166 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000167 ).get('security_groups')
168 sec_dict = dict([(s['name'], s['id'])
169 for s in security_groups])
170
171 sec_groups_names = [s['name'] for s in kwargs.pop(
172 'security_groups')]
173 security_groups_ids = [sec_dict[s]
174 for s in sec_groups_names]
175
176 if security_groups_ids:
177 create_port_body[
178 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300179 networks = kwargs.pop('networks', [])
180 else:
181 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000182
183 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300184 # for the project's private networks and create a port.
185 # The same behaviour as we would expect when passing
186 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000187 if not networks:
188 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300189 **{'router:external': False, 'fields': 'id'})['networks']
190
191 # It's net['uuid'] if networks come from kwargs
192 # and net['id'] if they come from
193 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000194 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000195 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300196 if 'port' not in net:
zhufl1e446b52017-10-16 16:54:57 +0800197 port = self.create_port(network_id=net_id,
198 client=clients.ports_client,
199 **create_port_body)
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300200 ports.append({'port': port['id']})
201 else:
202 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000203 if ports:
204 kwargs['networks'] = ports
205 self.ports = ports
206
207 tenant_network = self.get_tenant_network()
208
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200209 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000210 clients,
211 tenant_network=tenant_network,
212 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530213 name=name, flavor=flavor,
214 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000215
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200216 self.addCleanup(waiters.wait_for_server_termination,
217 clients.servers_client, body['id'])
218 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
219 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000220 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100221 return server
222
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100223 def create_volume(self, size=None, name=None, snapshot_id=None,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100224 imageRef=None, volume_type=None):
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700225 if size is None:
226 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500227 if imageRef:
zhufl66275c22018-03-28 15:32:14 +0800228 if CONF.image_feature_enabled.api_v1:
229 resp = self.image_client.check_image(imageRef)
230 image = common_image.get_image_meta_from_headers(resp)
231 else:
232 image = self.image_client.show_image(imageRef)
233 min_disk = image.get('min_disk')
Nuno Santosb746d992016-11-17 15:41:55 -0500234 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100235 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800236 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900237 kwargs = {'display_name': name,
238 'snapshot_id': snapshot_id,
239 'imageRef': imageRef,
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700240 'volume_type': volume_type,
241 'size': size}
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900242 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700243
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100244 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
245 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100246 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100247 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300248 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200249 waiters.wait_for_volume_resource_status(self.volumes_client,
250 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100251 # The volume retrieved on creation has a non-up-to-date status.
252 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000253 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100254 return volume
255
lkuchlane20e6a82018-05-08 11:28:46 +0300256 def create_backup(self, volume_id, name=None, description=None,
257 force=False, snapshot_id=None, incremental=False,
258 container=None):
259
260 name = name or data_utils.rand_name(
261 self.__class__.__name__ + "-backup")
262 kwargs = {'name': name,
263 'description': description,
264 'force': force,
265 'snapshot_id': snapshot_id,
266 'incremental': incremental,
267 'container': container}
268 backup = self.backups_client.create_backup(volume_id=volume_id,
269 **kwargs)['backup']
270 self.addCleanup(self.backups_client.delete_backup, backup['id'])
271 waiters.wait_for_volume_resource_status(self.backups_client,
272 backup['id'], 'available')
273 return backup
274
275 def restore_backup(self, backup_id):
276 restore = self.backups_client.restore_backup(backup_id)['restore']
277 self.addCleanup(self.volumes_client.delete_volume,
278 restore['volume_id'])
279 waiters.wait_for_volume_resource_status(self.backups_client,
280 backup_id, 'available')
281 waiters.wait_for_volume_resource_status(self.volumes_client,
282 restore['volume_id'],
283 'available')
284 self.assertEqual(backup_id, restore['backup_id'])
285 return restore
286
lkuchlan73ed1f32017-07-06 16:22:12 +0300287 def create_volume_snapshot(self, volume_id, name=None, description=None,
288 metadata=None, force=False):
289 name = name or data_utils.rand_name(
290 self.__class__.__name__ + '-snapshot')
291 snapshot = self.snapshots_client.create_snapshot(
292 volume_id=volume_id,
293 force=force,
294 display_name=name,
295 description=description,
296 metadata=metadata)['snapshot']
297 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
298 snapshot['id'])
299 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
300 waiters.wait_for_volume_resource_status(self.snapshots_client,
301 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200302 snapshot = self.snapshots_client.show_snapshot(
303 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300304 return snapshot
305
scottda61f68ac2016-06-07 12:07:55 -0600306 def create_volume_type(self, client=None, name=None, backend_name=None):
307 if not client:
ghanshyam6c682ff2018-08-06 09:54:45 +0000308 client = self.os_admin.volume_types_client_latest
lkuchlane3e7b272018-05-23 13:16:06 +0300309 randomized_name = name or data_utils.rand_name(
310 'volume-type-' + self.__class__.__name__)
scottda61f68ac2016-06-07 12:07:55 -0600311
312 LOG.debug("Creating a volume type: %s on backend %s",
313 randomized_name, backend_name)
314 extra_specs = {}
315 if backend_name:
316 extra_specs = {"volume_backend_name": backend_name}
317
lkuchlanbbabe542017-09-26 10:47:23 +0300318 volume_type = client.create_volume_type(
319 name=randomized_name, extra_specs=extra_specs)['volume_type']
scottda61f68ac2016-06-07 12:07:55 -0600320 self.addCleanup(client.delete_volume_type, volume_type['id'])
321 return volume_type
322
Yair Fried1fc32a12014-08-04 09:11:30 +0300323 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500324 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500325 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100326 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900327 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100328 for sg in sgs:
329 if sg['name'] == 'default':
330 secgroup_id = sg['id']
331
332 # These rules are intended to permit inbound ssh and icmp
333 # traffic from all sources, so no group_id is provided.
334 # Setting a group_id would only permit traffic from ports
335 # belonging to the same security group.
336 rulesets = [
337 {
338 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000339 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100340 'from_port': 22,
341 'to_port': 22,
342 'cidr': '0.0.0.0/0',
343 },
344 {
345 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000346 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100347 'from_port': -1,
348 'to_port': -1,
349 'cidr': '0.0.0.0/0',
350 }
351 ]
352 rules = list()
353 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000354 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900355 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100356 rules.append(sg_rule)
357 return rules
358
Yair Fried1fc32a12014-08-04 09:11:30 +0300359 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100360 # Create security group
361 sg_name = data_utils.rand_name(self.__class__.__name__)
362 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500363 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900364 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100365 self.assertEqual(secgroup['name'], sg_name)
366 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500367 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100368 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500369 self.compute_security_groups_client.delete_security_group,
370 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100371
372 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300373 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100374
375 return secgroup
376
zhuflf52c7592017-05-25 13:55:24 +0800377 def get_remote_client(self, ip_address, username=None, private_key=None,
378 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100379 """Get a SSH client to a remote server
380
Sean Dague20e98612016-01-06 14:33:28 -0500381 @param ip_address the server floating or fixed IP address to use
382 for ssh validation
JordanP3fe2dc32014-11-17 13:06:01 +0100383 @param username name of the Linux account on the remote server
384 @param private_key the SSH private key to use
zhuflf52c7592017-05-25 13:55:24 +0800385 @param server: server dict, used for debugging purposes
JordanP3fe2dc32014-11-17 13:06:01 +0100386 @return a RemoteClient object
387 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700388
Andrea Frittoli247058f2014-07-16 16:09:22 +0100389 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800390 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800391 # Set this with 'keypair' or others to log in with keypair or
392 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000393 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800394 password = None
395 if private_key is None:
396 private_key = self.keypair['private_key']
397 else:
lanoux283273b2015-12-04 03:01:54 -0800398 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800399 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800400 linux_client = remote_client.RemoteClient(
401 ip_address, username, pkey=private_key, password=password,
402 server=server, servers_client=self.servers_client)
403 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100404 return linux_client
405
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000406 def _image_create(self, name, fmt, path,
407 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900408 if properties is None:
409 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100410 name = data_utils.rand_name('%s-' % name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100411 params = {
412 'name': name,
413 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000414 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100415 }
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400416 if CONF.image_feature_enabled.api_v1:
417 params['is_public'] = 'False'
418 params['properties'] = properties
Ken'ichi Ohmichi02bcdf32016-06-17 16:41:26 -0700419 params = {'headers': common_image.image_meta_to_headers(**params)}
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400420 else:
421 params['visibility'] = 'private'
422 # Additional properties are flattened out in the v2 API.
423 params.update(properties)
424 body = self.image_client.create_image(**params)
425 image = body['image'] if 'image' in body else body
Andrea Frittoli247058f2014-07-16 16:09:22 +0100426 self.addCleanup(self.image_client.delete_image, image['id'])
427 self.assertEqual("queued", image['status'])
zhang.leia4b1cef2016-03-01 10:50:01 +0800428 with open(path, 'rb') as image_file:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400429 if CONF.image_feature_enabled.api_v1:
430 self.image_client.update_image(image['id'], data=image_file)
431 else:
432 self.image_client.store_image_file(image['id'], image_file)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100433 return image['id']
434
435 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300436 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100437 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
438 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
439 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300440 img_container_format = CONF.scenario.img_container_format
441 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000442 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400443 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Jordan Pittier525ec712016-12-07 17:51:26 +0100444 "properties: %s, ami: %s, ari: %s, aki: %s",
445 img_path, img_container_format, img_disk_format,
446 img_properties, ami_img_path, ari_img_path, aki_img_path)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100447 try:
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100448 image = self._image_create('scenario-img',
449 img_container_format,
450 img_path,
451 disk_format=img_disk_format,
452 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100453 except IOError:
Ryan Hsue5107be2015-12-03 13:55:03 -0800454 LOG.warning(
455 "A(n) %s image was not found. Retrying with uec image.",
456 img_disk_format)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100457 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
458 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000459 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100460 image = self._image_create('scenario-ami', 'ami',
461 path=ami_img_path,
462 properties=properties)
Jordan Pittier525ec712016-12-07 17:51:26 +0100463 LOG.debug("image:%s", image)
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100464
465 return image
Andrea Frittoli247058f2014-07-16 16:09:22 +0100466
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700467 def _log_console_output(self, servers=None, client=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400468 if not CONF.compute_feature_enabled.console_output:
469 LOG.debug('Console output not supported, cannot log')
470 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700471 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100472 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700473 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100474 servers = servers['servers']
475 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100476 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700477 console_output = client.get_console_output(
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100478 server['id'])['output']
479 LOG.debug('Console output for %s\nbody=\n%s',
480 server['id'], console_output)
481 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100482 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100483 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100484
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000485 def _log_net_info(self, exc):
486 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300487 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000488 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000489
nithya-ganesan882595e2014-07-29 18:51:07 +0000490 def create_server_snapshot(self, server, name=None):
491 # Glance client
492 _image_client = self.image_client
493 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900494 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000495 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800496 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000497 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000498 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500499 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300500 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200501
502 self.addCleanup(_image_client.wait_for_resource_deletion,
503 image_id)
504 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
505 _image_client.delete_image, image_id)
506
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400507 if CONF.image_feature_enabled.api_v1:
508 # In glance v1 the additional properties are stored in the headers.
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700509 resp = _image_client.check_image(image_id)
510 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400511 image_props = snapshot_image.get('properties', {})
512 else:
513 # In glance v2 the additional properties are flattened.
514 snapshot_image = _image_client.show_image(image_id)
515 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300516
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400517 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300518 if bdm:
519 bdm = json.loads(bdm)
520 if bdm and 'snapshot_id' in bdm[0]:
521 snapshot_id = bdm[0]['snapshot_id']
522 self.addCleanup(
523 self.snapshots_client.wait_for_resource_deletion,
524 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100525 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
526 self.snapshots_client.delete_snapshot,
527 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200528 waiters.wait_for_volume_resource_status(self.snapshots_client,
529 snapshot_id,
530 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000531 image_name = snapshot_image['name']
532 self.assertEqual(name, image_name)
533 LOG.debug("Created snapshot image %s for server %s",
534 image_name, server['name'])
535 return snapshot_image
536
Jordan Pittier7cf64762015-10-14 15:01:12 +0200537 def nova_volume_attach(self, server, volume_to_attach):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000538 volume = self.servers_client.attach_volume(
Jordan Pittier7cf64762015-10-14 15:01:12 +0200539 server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900540 % CONF.compute.volume_device_name)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200541 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200542 waiters.wait_for_volume_resource_status(self.volumes_client,
543 volume['id'], 'in-use')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900544
Jordan Pittier7cf64762015-10-14 15:01:12 +0200545 # Return the updated volume after the attachment
546 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900547
Jordan Pittier7cf64762015-10-14 15:01:12 +0200548 def nova_volume_detach(self, server, volume):
549 self.servers_client.detach_volume(server['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200550 waiters.wait_for_volume_resource_status(self.volumes_client,
551 volume['id'], 'available')
Jordan Pittier7cf64762015-10-14 15:01:12 +0200552
Steven Hardyda2a8352014-10-02 12:52:20 +0100553 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800554 ping_timeout=None, mtu=None, server=None):
lanoux5fc14522015-09-21 08:17:35 +0000555 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000556 cmd = ['ping', '-c1', '-w1']
557
558 if mtu:
559 cmd += [
560 # don't fragment
561 '-M', 'do',
562 # ping receives just the size of ICMP payload
563 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
564 ]
565 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700566
567 def ping():
568 proc = subprocess.Popen(cmd,
569 stdout=subprocess.PIPE,
570 stderr=subprocess.PIPE)
571 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000572
Aaron Rosena7df13b2014-09-23 09:45:45 -0700573 return (proc.returncode == 0) == should_succeed
574
Jordan Pittier9e227c52016-02-09 14:35:18 +0100575 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000576 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800577 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000578 'caller': caller, 'ip': ip_address, 'timeout': timeout,
579 'should_succeed':
580 'reachable' if should_succeed else 'unreachable'
581 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200582 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000583 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800584 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000585 'caller': caller, 'ip': ip_address, 'timeout': timeout,
586 'result': 'expected' if result else 'unexpected'
587 })
zhufl0ec74c42017-11-15 14:02:28 +0800588 if server:
589 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000590 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700591
Yair Friedae0e73d2014-11-24 11:56:26 +0200592 def check_vm_connectivity(self, ip_address,
593 username=None,
594 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000595 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800596 extra_msg="",
597 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000598 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000599 """Check server connectivity
600
Yair Friedae0e73d2014-11-24 11:56:26 +0200601 :param ip_address: server to test against
602 :param username: server's ssh username
603 :param private_key: server's ssh private key to be used
604 :param should_connect: True/False indicates positive/negative test
605 positive - attempt ping and ssh
606 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800607 :param extra_msg: Message to help with debugging if ``ping_ip_address``
608 fails
609 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000610 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200611
612 :raises: AssertError if the result of the connectivity check does
613 not match the value of the should_connect param
614 """
zhufl0ec74c42017-11-15 14:02:28 +0800615 LOG.debug('checking network connections to IP %s with user: %s',
616 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200617 if should_connect:
618 msg = "Timed out waiting for %s to become reachable" % ip_address
619 else:
620 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800621 if extra_msg:
622 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200623 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000624 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800625 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200626 msg=msg)
627 if should_connect:
628 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800629 try:
630 self.get_remote_client(ip_address, username, private_key,
631 server=server)
632 except Exception:
633 if not extra_msg:
634 extra_msg = 'Failed to ssh to %s' % ip_address
635 LOG.exception(extra_msg)
636 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200637
638 def create_floating_ip(self, thing, pool_name=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000639 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200640
Marc Koderer3b57d802016-03-22 15:23:31 +0100641 if not pool_name:
642 pool_name = CONF.network.floating_network_name
John Warrene74890a2015-11-11 15:18:01 -0500643 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000644 create_floating_ip(pool=pool_name)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100645 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500646 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200647 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500648 self.compute_floating_ips_client.associate_floating_ip_to_server(
Yair Friedae0e73d2014-11-24 11:56:26 +0200649 floating_ip['ip'], thing['id'])
650 return floating_ip
651
Sean Dague20e98612016-01-06 14:33:28 -0500652 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200653 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500654 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200655 private_key=private_key,
656 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300657 if dev_name is not None:
658 ssh_client.make_fs(dev_name)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800659 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
660 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300661 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
662 ssh_client.exec_command(cmd_timestamp)
663 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
664 % mount_path)
665 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800666 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300667 return timestamp
668
Sean Dague20e98612016-01-06 14:33:28 -0500669 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200670 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500671 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200672 private_key=private_key,
673 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300674 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700675 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300676 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
677 % mount_path)
678 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800679 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300680 return timestamp
681
Sean Dague20e98612016-01-06 14:33:28 -0500682 def get_server_ip(self, server):
683 """Get the server fixed or floating IP.
684
685 Based on the configuration we're in, return a correct ip
686 address for validating that a guest is up.
687 """
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200688 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500689 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800690 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500691 # method is creating the floating IP there.
692 return self.create_floating_ip(server)['ip']
693 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400694 # Determine the network name to look for based on config or creds
695 # provider network resources.
696 if CONF.validation.network_for_ssh:
697 addresses = server['addresses'][
698 CONF.validation.network_for_ssh]
699 else:
zhufl7b4a7202017-09-28 10:29:27 +0800700 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400701 addresses = (server['addresses'][network['name']]
702 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500703 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200704 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
705 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500706 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800707 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200708 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400709 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200710
zhufl7bc916d2018-08-22 14:47:39 +0800711 @classmethod
712 def get_host_for_server(cls, server_id):
713 server_details = cls.os_admin.servers_client.show_server(server_id)
714 return server_details['server']['OS-EXT-SRV-ATTR:host']
715
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100716
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100717class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300718 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000719
Yair Fried1fc32a12014-08-04 09:11:30 +0300720 This class provide helpers for network scenario tests, using the neutron
721 API. Helpers from ancestor which use the nova network API are overridden
722 with the neutron API.
723
724 This Class also enforces using Neutron instead of novanetwork.
725 Subclassed tests will be skipped if Neutron is not enabled
726
727 """
728
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000729 credentials = ['primary', 'admin']
730
Yair Fried1fc32a12014-08-04 09:11:30 +0300731 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000732 def skip_checks(cls):
733 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100734 if not CONF.service_available.neutron:
735 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300736
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700737 def _create_network(self, networks_client=None,
zhoubin5058bead72017-02-04 18:01:15 +0800738 tenant_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200739 namestart='network-smoke-',
740 port_security_enabled=True):
John Warren94d8faf2015-09-15 12:22:24 -0400741 if not networks_client:
742 networks_client = self.networks_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300743 if not tenant_id:
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700744 tenant_id = networks_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300745 name = data_utils.rand_name(namestart)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400746 network_kwargs = dict(name=name, tenant_id=tenant_id)
747 # Neutron disables port security by default so we have to check the
748 # config before trying to create the network with port_security_enabled
749 if CONF.network_feature_enabled.port_security:
750 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +0200751 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -0500752 network = result['network']
753
754 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100755 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800756 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -0500757 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300758 return network
759
zhufl5b0a52f2017-10-24 15:48:20 +0800760 def create_subnet(self, network, subnets_client=None,
761 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000762 """Create a subnet for the given network
763
764 within the cidr block configured for tenant networks.
Yair Fried1fc32a12014-08-04 09:11:30 +0300765 """
John Warren3961acd2015-10-02 14:38:53 -0400766 if not subnets_client:
767 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300768
769 def cidr_in_use(cidr, tenant_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000770 """Check cidr existence
771
lei zhangdd552b22015-11-25 20:41:48 +0800772 :returns: True if subnet with cidr already exist in tenant
773 False else
Yair Fried1fc32a12014-08-04 09:11:30 +0300774 """
jeremy.zhang5870ff12017-05-25 11:24:23 +0800775 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100776 tenant_id=tenant_id, cidr=cidr)['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300777 return len(cidr_in_use) != 0
778
Kirill Shileev14113572014-11-21 16:58:02 +0300779 ip_version = kwargs.pop('ip_version', 4)
780
781 if ip_version == 6:
782 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -0400783 CONF.network.project_network_v6_cidr)
784 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300785 else:
Sean Dagueed6e5862016-04-04 10:49:13 -0400786 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
787 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300788
Yair Fried1fc32a12014-08-04 09:11:30 +0300789 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300790 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300791 # Repeatedly attempt subnet creation with sequential cidr
792 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300793 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300794 str_cidr = str(subnet_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -0500795 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300796 continue
797
798 subnet = dict(
799 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -0500800 network_id=network['id'],
801 tenant_id=network['tenant_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +0300802 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300803 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300804 **kwargs
805 )
806 try:
John Warren3961acd2015-10-02 14:38:53 -0400807 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300808 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900809 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300810 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
811 if not is_overlapping_cidr:
812 raise
813 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -0500814
815 subnet = result['subnet']
816 self.assertEqual(subnet['cidr'], str_cidr)
817
818 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
819 subnets_client.delete_subnet, subnet['id'])
820
Yair Fried1fc32a12014-08-04 09:11:30 +0300821 return subnet
822
Kirill Shileev14113572014-11-21 16:58:02 +0300823 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Hongbin Lu95a31692018-06-13 23:17:54 +0000824 if ip_addr:
825 ports = self.os_admin.ports_client.list_ports(
826 device_id=server['id'],
827 fixed_ips='ip_address=%s' % ip_addr)['ports']
828 else:
829 ports = self.os_admin.ports_client.list_ports(
830 device_id=server['id'])['ports']
Kobi Samoray166500a2016-10-09 14:42:48 +0300831 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -0500832 # If the network is dual-stack (IPv4 + IPv6), this port is associated
833 # with 2 subnets
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300834 p_status = ['ACTIVE']
835 # NOTE(vsaienko) With Ironic, instances live on separate hardware
836 # servers. Neutron does not bind ports for Ironic instances, as a
837 # result the port remains in the DOWN state.
Vasyl Saienkoc8aa34b2016-08-01 14:18:37 +0300838 # TODO(vsaienko) remove once bug: #1599836 is resolved.
Thiago Paiva66cded22016-08-15 14:55:58 -0300839 if getattr(CONF.service_available, 'ironic', False):
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300840 p_status.append('DOWN')
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200841 port_map = [(p["id"], fxip["ip_address"])
842 for p in ports
843 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200844 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
845 p['status'] in p_status)]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -0800846 inactive = [p for p in ports if p['status'] != 'ACTIVE']
847 if inactive:
848 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200849
Masayuki Igawaf9009b42017-04-10 14:49:29 +0900850 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -0800851 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200852 self.assertEqual(len(port_map), 1,
853 "Found multiple IPv4 addresses: %s. "
854 "Unable to determine which port to target."
855 % port_map)
856 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300857
David Shrewsbury9bac3662014-08-07 15:07:01 -0400858 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +0800859 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100860 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +0200861 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700862 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -0500863 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -0400864
Yair Friedae0e73d2014-11-24 11:56:26 +0200865 def create_floating_ip(self, thing, external_network_id=None,
866 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000867 """Create a floating IP and associates to a resource/port on Neutron"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200868 if not external_network_id:
869 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300870 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -0500871 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300872 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300873 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
874 else:
875 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500876 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300877 floating_network_id=external_network_id,
878 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300879 tenant_id=thing['tenant_id'],
880 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300881 )
Steve Heyman33735f22016-05-24 09:28:08 -0500882 floating_ip = result['floatingip']
Jordan Pittier9e227c52016-02-09 14:35:18 +0100883 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800884 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -0500885 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300886 return floating_ip
887
Yair Fried45f92952014-06-26 05:19:19 +0300888 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000889 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300890
Steve Heyman33735f22016-05-24 09:28:08 -0500891 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +0300892 :param status: target status
893 :raises: AssertionError if status doesn't match
894 """
Steve Heyman33735f22016-05-24 09:28:08 -0500895 floatingip_id = floating_ip['id']
896
Carl Baldwina754e2d2014-10-23 22:47:41 +0000897 def refresh():
Steve Heyman33735f22016-05-24 09:28:08 -0500898 result = (self.floating_ips_client.
899 show_floatingip(floatingip_id)['floatingip'])
900 return status == result['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +0000901
zhufl4dda94e2017-03-14 16:14:46 +0800902 if not test_utils.call_until_true(refresh,
903 CONF.network.build_timeout,
904 CONF.network.build_interval):
905 floating_ip = self.floating_ips_client.show_floatingip(
906 floatingip_id)['floatingip']
907 self.assertEqual(status, floating_ip['status'],
908 message="FloatingIP: {fp} is at status: {cst}. "
909 "failed to reach status: {st}"
910 .format(fp=floating_ip, cst=floating_ip['status'],
911 st=status))
Yair Fried45f92952014-06-26 05:19:19 +0300912 LOG.info("FloatingIP: {fp} is at status: {st}"
913 .format(fp=floating_ip, st=status))
914
zhufl420a0192017-09-28 11:04:50 +0800915 def check_tenant_network_connectivity(self, server,
916 username,
917 private_key,
918 should_connect=True,
919 servers_for_debug=None):
Sean Dagueed6e5862016-04-04 10:49:13 -0400920 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +0300921 msg = 'Tenant networks not configured to be reachable.'
922 LOG.info(msg)
923 return
924 # The target login is assumed to have been configured for
925 # key-based authentication by cloud-init.
926 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +0100927 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +0300928 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900929 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200930 username,
931 private_key,
932 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300933 except Exception as e:
934 LOG.exception('Tenant network connectivity check failed')
935 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000936 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300937 raise
938
zhufle9877c62017-10-13 09:38:19 +0800939 def check_remote_connectivity(self, source, dest, should_succeed=True,
940 nic=None):
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900941 """assert ping server via source ssh connection
942
Yair Fried1fc32a12014-08-04 09:11:30 +0300943 :param source: RemoteClient: an ssh connection from which to ping
zhufle9877c62017-10-13 09:38:19 +0800944 :param dest: an IP to ping against
945 :param should_succeed: boolean: should ping succeed or not
Yair Friedbc46f592015-11-18 16:29:34 +0200946 :param nic: specific network interface to ping from
Yair Fried1fc32a12014-08-04 09:11:30 +0300947 """
948 def ping_remote():
949 try:
Yair Friedbc46f592015-11-18 16:29:34 +0200950 source.ping_host(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +0300951 except lib_exc.SSHExecCommandFailed:
zhangguoqing6c096642016-01-04 06:17:21 +0000952 LOG.warning('Failed to ping IP: %s via a ssh connection '
Jordan Pittier525ec712016-12-07 17:51:26 +0100953 'from: %s.', dest, source.ssh_client.host)
Yair Fried1fc32a12014-08-04 09:11:30 +0300954 return not should_succeed
955 return should_succeed
956
zhufle9877c62017-10-13 09:38:19 +0800957 result = test_utils.call_until_true(ping_remote,
958 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -0800959 if result:
960 return
961
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900962 source_host = source.ssh_client.host
963 if should_succeed:
964 msg = "Timed out waiting for %s to become reachable from %s" \
965 % (dest, source_host)
966 else:
967 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -0800968 self._log_console_output()
969 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900970
John Warren456d9ae2016-01-12 15:36:33 -0500971 def _create_security_group(self, security_group_rules_client=None,
972 tenant_id=None,
John Warrenf9606e92015-12-10 12:12:42 -0500973 namestart='secgroup-smoke',
974 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -0500975 if security_group_rules_client is None:
976 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -0500977 if security_groups_client is None:
978 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300979 if tenant_id is None:
John Warrenf9606e92015-12-10 12:12:42 -0500980 tenant_id = security_groups_client.tenant_id
981 secgroup = self._create_empty_security_group(
982 namestart=namestart, client=security_groups_client,
983 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300984
985 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -0500986 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -0500987 security_group_rules_client=security_group_rules_client,
988 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -0500989 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +0300990 for rule in rules:
Steve Heyman33735f22016-05-24 09:28:08 -0500991 self.assertEqual(tenant_id, rule['tenant_id'])
992 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300993 return secgroup
994
Yair Frieddb6c9e92014-08-06 08:53:13 +0300995 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300996 namestart='secgroup-smoke'):
997 """Create a security group without rules.
998
999 Default rules will be created:
1000 - IPv4 egress to any
1001 - IPv6 egress to any
1002
1003 :param tenant_id: secgroup will be created in this tenant
Steve Heyman33735f22016-05-24 09:28:08 -05001004 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001005 """
1006 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001007 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001008 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001009 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001010 sg_name = data_utils.rand_name(namestart)
1011 sg_desc = sg_name + " description"
1012 sg_dict = dict(name=sg_name,
1013 description=sg_desc)
1014 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -05001015 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001016
1017 secgroup = result['security_group']
1018 self.assertEqual(secgroup['name'], sg_name)
1019 self.assertEqual(tenant_id, secgroup['tenant_id'])
1020 self.assertEqual(secgroup['description'], sg_desc)
1021
Jordan Pittier9e227c52016-02-09 14:35:18 +01001022 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001023 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001024 return secgroup
1025
John Warren456d9ae2016-01-12 15:36:33 -05001026 def _create_security_group_rule(self, secgroup=None,
1027 sec_group_rules_client=None,
John Warrenf9606e92015-12-10 12:12:42 -05001028 tenant_id=None,
1029 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001030 """Create a rule from a dictionary of rule parameters.
1031
1032 Create a rule in a secgroup. if secgroup not defined will search for
1033 default secgroup in tenant_id.
1034
Steve Heyman33735f22016-05-24 09:28:08 -05001035 :param secgroup: the security group.
Yair Fried1fc32a12014-08-04 09:11:30 +03001036 :param tenant_id: if secgroup not passed -- the tenant in which to
1037 search for default secgroup
1038 :param kwargs: a dictionary containing rule parameters:
1039 for example, to allow incoming ssh:
1040 rule = {
1041 direction: 'ingress'
1042 protocol:'tcp',
1043 port_range_min: 22,
1044 port_range_max: 22
1045 }
1046 """
John Warren456d9ae2016-01-12 15:36:33 -05001047 if sec_group_rules_client is None:
1048 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001049 if security_groups_client is None:
1050 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001051 if not tenant_id:
John Warrenf9606e92015-12-10 12:12:42 -05001052 tenant_id = security_groups_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001053 if secgroup is None:
zhuflb0b272e2017-09-22 16:01:46 +08001054 # Get default secgroup for tenant_id
1055 default_secgroups = security_groups_client.list_security_groups(
1056 name='default', tenant_id=tenant_id)['security_groups']
1057 msg = "No default security group for tenant %s." % (tenant_id)
1058 self.assertNotEmpty(default_secgroups, msg)
1059 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001060
Steve Heyman33735f22016-05-24 09:28:08 -05001061 ruleset = dict(security_group_id=secgroup['id'],
1062 tenant_id=secgroup['tenant_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001063 ruleset.update(kwargs)
1064
John Warren456d9ae2016-01-12 15:36:33 -05001065 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001066 sg_rule = sg_rule['security_group_rule']
1067
1068 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1069 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001070
1071 return sg_rule
1072
John Warren456d9ae2016-01-12 15:36:33 -05001073 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1074 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001075 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001076 """Create loginable security group rule
1077
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001078 This function will create:
1079 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1080 access for ipv4.
1081 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1082 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001083 """
1084
John Warren456d9ae2016-01-12 15:36:33 -05001085 if security_group_rules_client is None:
1086 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001087 if security_groups_client is None:
1088 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001089 rules = []
1090 rulesets = [
1091 dict(
1092 # ssh
1093 protocol='tcp',
1094 port_range_min=22,
1095 port_range_max=22,
1096 ),
1097 dict(
1098 # ping
1099 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001100 ),
1101 dict(
1102 # ipv6-icmp for ping6
1103 protocol='icmp',
1104 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001105 )
1106 ]
John Warren456d9ae2016-01-12 15:36:33 -05001107 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001108 for ruleset in rulesets:
1109 for r_direction in ['ingress', 'egress']:
1110 ruleset['direction'] = r_direction
1111 try:
1112 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001113 sec_group_rules_client=sec_group_rules_client,
1114 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001115 security_groups_client=security_groups_client,
1116 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001117 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001118 # if rule already exist - skip rule and continue
1119 msg = 'Security group rule already exists'
1120 if msg not in ex._error_string:
1121 raise ex
1122 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001123 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001124 rules.append(sg_rule)
1125
1126 return rules
1127
Yair Frieddb6c9e92014-08-06 08:53:13 +03001128 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001129 """Retrieve a router for the given tenant id.
1130
1131 If a public router has been configured, it will be returned.
1132
1133 If a public router has not been configured, but a public
1134 network has, a tenant router will be created and returned that
1135 routes traffic to the public network.
1136 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001137 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001138 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001139 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001140 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001141 router_id = CONF.network.public_router_id
1142 network_id = CONF.network.public_network_id
1143 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001144 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001145 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001146 elif network_id:
zhufl3484f992017-10-10 16:18:29 +08001147 router = client.create_router(
1148 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
1149 admin_state_up=True,
1150 tenant_id=tenant_id,
1151 external_gateway_info=dict(network_id=network_id))['router']
1152 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1153 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001154 return router
1155 else:
1156 raise Exception("Neither of 'public_router_id' or "
1157 "'public_network_id' has been defined.")
1158
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001159 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001160 routers_client=None, subnets_client=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001161 tenant_id=None, dns_nameservers=None,
1162 port_security_enabled=True):
Yair Fried1fc32a12014-08-04 09:11:30 +03001163 """Create a network with a subnet connected to a router.
1164
David Shrewsbury9bac3662014-08-07 15:07:01 -04001165 The baremetal driver is a special case since all nodes are
1166 on the same shared network.
1167
Yair Fried413bf2d2014-11-19 17:07:11 +02001168 :param tenant_id: id of tenant to create resources in.
1169 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001170 :returns: network, subnet, router
1171 """
Thiago Paiva66cded22016-08-15 14:55:58 -03001172 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001173 # NOTE(Shrews): This exception is for environments where tenant
1174 # credential isolation is available, but network separation is
1175 # not (the current baremetal case). Likely can be removed when
1176 # test account mgmt is reworked:
1177 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001178 if not CONF.compute.fixed_network_name:
1179 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001180 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001181 network = self._get_network_by_name(
1182 CONF.compute.fixed_network_name)
1183 router = None
1184 subnet = None
1185 else:
John Warren94d8faf2015-09-15 12:22:24 -04001186 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001187 networks_client=networks_client,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001188 tenant_id=tenant_id,
1189 port_security_enabled=port_security_enabled)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001190 router = self._get_router(client=routers_client,
1191 tenant_id=tenant_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001192 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001193 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001194 # use explicit check because empty list is a valid option
1195 if dns_nameservers is not None:
1196 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001197 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001198 if not routers_client:
1199 routers_client = self.routers_client
1200 router_id = router['id']
1201 routers_client.add_router_interface(router_id,
1202 subnet_id=subnet['id'])
1203
1204 # save a cleanup job to remove this association between
1205 # router and subnet
1206 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1207 routers_client.remove_router_interface, router_id,
1208 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001209 return network, subnet, router
1210
1211
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001212class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001213 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001214
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001215 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001216
1217 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001218 def setup_clients(cls):
1219 super(EncryptionScenarioTest, cls).setup_clients()
ghanshyam6c682ff2018-08-06 09:54:45 +00001220 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001221 cls.admin_encryption_types_client =\
ghanshyam6c682ff2018-08-06 09:54:45 +00001222 cls.os_admin.encryption_types_client_latest
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001223
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001224 def create_encryption_type(self, client=None, type_id=None, provider=None,
1225 key_size=None, cipher=None,
1226 control_location=None):
1227 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001228 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001229 if not type_id:
1230 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001231 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001232 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001233 client.create_encryption_type(
1234 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001235 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001236
lkuchlan3023e752017-06-08 12:53:13 +03001237 def create_encrypted_volume(self, encryption_provider, volume_type,
1238 key_size=256, cipher='aes-xts-plain64',
1239 control_location='front-end'):
1240 volume_type = self.create_volume_type(name=volume_type)
1241 self.create_encryption_type(type_id=volume_type['id'],
1242 provider=encryption_provider,
1243 key_size=key_size,
1244 cipher=cipher,
1245 control_location=control_location)
1246 return self.create_volume(volume_type=volume_type['name'])
1247
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001248
Masayuki Igawa0870db52015-09-18 21:08:36 +09001249class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001250 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001251
1252 Subclasses implement the tests that use the methods provided by this
1253 class.
1254 """
1255
1256 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001257 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001258 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001259 if not CONF.service_available.swift:
1260 skip_msg = ("%s skipped as swift is not available" %
1261 cls.__name__)
1262 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001263
1264 @classmethod
1265 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001266 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001267 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001268 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001269 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001270
1271 @classmethod
1272 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001273 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001274 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001275 cls.account_client = cls.os_operator.account_client
1276 cls.container_client = cls.os_operator.container_client
1277 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001278
Chris Dentde456a12014-09-10 12:41:15 +01001279 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001280 """get swift status for our user account."""
1281 self.account_client.list_account_containers()
1282 LOG.debug('Swift status information obtained successfully')
1283
Chris Dentde456a12014-09-10 12:41:15 +01001284 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001285 name = container_name or data_utils.rand_name(
1286 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001287 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001288 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001289 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001290 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001291 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001292 self.container_client.delete_container,
1293 name)
Chris Dent0d494112014-08-26 13:48:30 +01001294 return name
1295
Chris Dentde456a12014-09-10 12:41:15 +01001296 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001297 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001298 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001299
Chris Dentde456a12014-09-10 12:41:15 +01001300 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001301 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001302 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001303 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001304 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001305 self.object_client.delete_object,
1306 container_name,
1307 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001308 return obj_name, obj_data
1309
Chris Dentde456a12014-09-10 12:41:15 +01001310 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001311 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001312 self.list_and_check_container_objects(container_name,
1313 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001314
Chris Dentde456a12014-09-10 12:41:15 +01001315 def list_and_check_container_objects(self, container_name,
1316 present_obj=None,
1317 not_present_obj=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001318 # List objects for a given container and assert which are present and
1319 # which are not.
Ghanshyam2a180b82014-06-16 13:54:22 +09001320 if present_obj is None:
1321 present_obj = []
1322 if not_present_obj is None:
1323 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001324 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001325 container_name)
1326 if present_obj:
1327 for obj in present_obj:
1328 self.assertIn(obj, object_list)
1329 if not_present_obj:
1330 for obj in not_present_obj:
1331 self.assertNotIn(obj, object_list)
1332
Chris Dentde456a12014-09-10 12:41:15 +01001333 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001334 _, obj = self.object_client.get_object(container_name, obj_name)
1335 self.assertEqual(obj, expected_data)