blob: be52eef4c964dea3bb456e80528fe5ad1a077a98 [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:
228 image = self.compute_images_client.show_image(imageRef)['image']
229 min_disk = image.get('minDisk')
230 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100231 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800232 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900233 kwargs = {'display_name': name,
234 'snapshot_id': snapshot_id,
235 'imageRef': imageRef,
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700236 'volume_type': volume_type,
237 'size': size}
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900238 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700239
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100240 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
241 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100242 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100243 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300244 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200245 waiters.wait_for_volume_resource_status(self.volumes_client,
246 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100247 # The volume retrieved on creation has a non-up-to-date status.
248 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000249 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100250 return volume
251
lkuchlane20e6a82018-05-08 11:28:46 +0300252 def create_backup(self, volume_id, name=None, description=None,
253 force=False, snapshot_id=None, incremental=False,
254 container=None):
255
256 name = name or data_utils.rand_name(
257 self.__class__.__name__ + "-backup")
258 kwargs = {'name': name,
259 'description': description,
260 'force': force,
261 'snapshot_id': snapshot_id,
262 'incremental': incremental,
263 'container': container}
264 backup = self.backups_client.create_backup(volume_id=volume_id,
265 **kwargs)['backup']
266 self.addCleanup(self.backups_client.delete_backup, backup['id'])
267 waiters.wait_for_volume_resource_status(self.backups_client,
268 backup['id'], 'available')
269 return backup
270
271 def restore_backup(self, backup_id):
272 restore = self.backups_client.restore_backup(backup_id)['restore']
273 self.addCleanup(self.volumes_client.delete_volume,
274 restore['volume_id'])
275 waiters.wait_for_volume_resource_status(self.backups_client,
276 backup_id, 'available')
277 waiters.wait_for_volume_resource_status(self.volumes_client,
278 restore['volume_id'],
279 'available')
280 self.assertEqual(backup_id, restore['backup_id'])
281 return restore
282
lkuchlan73ed1f32017-07-06 16:22:12 +0300283 def create_volume_snapshot(self, volume_id, name=None, description=None,
284 metadata=None, force=False):
285 name = name or data_utils.rand_name(
286 self.__class__.__name__ + '-snapshot')
287 snapshot = self.snapshots_client.create_snapshot(
288 volume_id=volume_id,
289 force=force,
290 display_name=name,
291 description=description,
292 metadata=metadata)['snapshot']
293 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
294 snapshot['id'])
295 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
296 waiters.wait_for_volume_resource_status(self.snapshots_client,
297 snapshot['id'], 'available')
Benny Kopilov11b28002017-12-19 12:46:19 +0200298 snapshot = self.snapshots_client.show_snapshot(
299 snapshot['id'])['snapshot']
lkuchlan73ed1f32017-07-06 16:22:12 +0300300 return snapshot
301
scottda61f68ac2016-06-07 12:07:55 -0600302 def create_volume_type(self, client=None, name=None, backend_name=None):
303 if not client:
zhufl708821c2017-07-12 16:08:34 +0800304 client = self.os_admin.volume_types_v2_client
lkuchlane3e7b272018-05-23 13:16:06 +0300305 randomized_name = name or data_utils.rand_name(
306 'volume-type-' + self.__class__.__name__)
scottda61f68ac2016-06-07 12:07:55 -0600307
308 LOG.debug("Creating a volume type: %s on backend %s",
309 randomized_name, backend_name)
310 extra_specs = {}
311 if backend_name:
312 extra_specs = {"volume_backend_name": backend_name}
313
lkuchlanbbabe542017-09-26 10:47:23 +0300314 volume_type = client.create_volume_type(
315 name=randomized_name, extra_specs=extra_specs)['volume_type']
scottda61f68ac2016-06-07 12:07:55 -0600316 self.addCleanup(client.delete_volume_type, volume_type['id'])
317 return volume_type
318
Yair Fried1fc32a12014-08-04 09:11:30 +0300319 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500320 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500321 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100322 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900323 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100324 for sg in sgs:
325 if sg['name'] == 'default':
326 secgroup_id = sg['id']
327
328 # These rules are intended to permit inbound ssh and icmp
329 # traffic from all sources, so no group_id is provided.
330 # Setting a group_id would only permit traffic from ports
331 # belonging to the same security group.
332 rulesets = [
333 {
334 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000335 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100336 'from_port': 22,
337 'to_port': 22,
338 'cidr': '0.0.0.0/0',
339 },
340 {
341 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000342 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100343 'from_port': -1,
344 'to_port': -1,
345 'cidr': '0.0.0.0/0',
346 }
347 ]
348 rules = list()
349 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000350 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900351 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100352 rules.append(sg_rule)
353 return rules
354
Yair Fried1fc32a12014-08-04 09:11:30 +0300355 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100356 # Create security group
357 sg_name = data_utils.rand_name(self.__class__.__name__)
358 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500359 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900360 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100361 self.assertEqual(secgroup['name'], sg_name)
362 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500363 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100364 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500365 self.compute_security_groups_client.delete_security_group,
366 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100367
368 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300369 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100370
371 return secgroup
372
zhuflf52c7592017-05-25 13:55:24 +0800373 def get_remote_client(self, ip_address, username=None, private_key=None,
374 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100375 """Get a SSH client to a remote server
376
Sean Dague20e98612016-01-06 14:33:28 -0500377 @param ip_address the server floating or fixed IP address to use
378 for ssh validation
JordanP3fe2dc32014-11-17 13:06:01 +0100379 @param username name of the Linux account on the remote server
380 @param private_key the SSH private key to use
zhuflf52c7592017-05-25 13:55:24 +0800381 @param server: server dict, used for debugging purposes
JordanP3fe2dc32014-11-17 13:06:01 +0100382 @return a RemoteClient object
383 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700384
Andrea Frittoli247058f2014-07-16 16:09:22 +0100385 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800386 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800387 # Set this with 'keypair' or others to log in with keypair or
388 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000389 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800390 password = None
391 if private_key is None:
392 private_key = self.keypair['private_key']
393 else:
lanoux283273b2015-12-04 03:01:54 -0800394 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800395 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800396 linux_client = remote_client.RemoteClient(
397 ip_address, username, pkey=private_key, password=password,
398 server=server, servers_client=self.servers_client)
399 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100400 return linux_client
401
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000402 def _image_create(self, name, fmt, path,
403 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900404 if properties is None:
405 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100406 name = data_utils.rand_name('%s-' % name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100407 params = {
408 'name': name,
409 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000410 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100411 }
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400412 if CONF.image_feature_enabled.api_v1:
413 params['is_public'] = 'False'
414 params['properties'] = properties
Ken'ichi Ohmichi02bcdf32016-06-17 16:41:26 -0700415 params = {'headers': common_image.image_meta_to_headers(**params)}
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400416 else:
417 params['visibility'] = 'private'
418 # Additional properties are flattened out in the v2 API.
419 params.update(properties)
420 body = self.image_client.create_image(**params)
421 image = body['image'] if 'image' in body else body
Andrea Frittoli247058f2014-07-16 16:09:22 +0100422 self.addCleanup(self.image_client.delete_image, image['id'])
423 self.assertEqual("queued", image['status'])
zhang.leia4b1cef2016-03-01 10:50:01 +0800424 with open(path, 'rb') as image_file:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400425 if CONF.image_feature_enabled.api_v1:
426 self.image_client.update_image(image['id'], data=image_file)
427 else:
428 self.image_client.store_image_file(image['id'], image_file)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100429 return image['id']
430
431 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300432 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100433 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
434 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
435 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300436 img_container_format = CONF.scenario.img_container_format
437 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000438 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400439 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Jordan Pittier525ec712016-12-07 17:51:26 +0100440 "properties: %s, ami: %s, ari: %s, aki: %s",
441 img_path, img_container_format, img_disk_format,
442 img_properties, ami_img_path, ari_img_path, aki_img_path)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100443 try:
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100444 image = self._image_create('scenario-img',
445 img_container_format,
446 img_path,
447 disk_format=img_disk_format,
448 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100449 except IOError:
Ryan Hsue5107be2015-12-03 13:55:03 -0800450 LOG.warning(
451 "A(n) %s image was not found. Retrying with uec image.",
452 img_disk_format)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100453 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
454 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000455 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100456 image = self._image_create('scenario-ami', 'ami',
457 path=ami_img_path,
458 properties=properties)
Jordan Pittier525ec712016-12-07 17:51:26 +0100459 LOG.debug("image:%s", image)
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100460
461 return image
Andrea Frittoli247058f2014-07-16 16:09:22 +0100462
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700463 def _log_console_output(self, servers=None, client=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400464 if not CONF.compute_feature_enabled.console_output:
465 LOG.debug('Console output not supported, cannot log')
466 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700467 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100468 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700469 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100470 servers = servers['servers']
471 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100472 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700473 console_output = client.get_console_output(
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100474 server['id'])['output']
475 LOG.debug('Console output for %s\nbody=\n%s',
476 server['id'], console_output)
477 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100478 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100479 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100480
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000481 def _log_net_info(self, exc):
482 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300483 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000484 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000485
nithya-ganesan882595e2014-07-29 18:51:07 +0000486 def create_server_snapshot(self, server, name=None):
487 # Glance client
488 _image_client = self.image_client
489 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900490 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000491 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800492 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000493 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000494 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500495 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300496 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200497
498 self.addCleanup(_image_client.wait_for_resource_deletion,
499 image_id)
500 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
501 _image_client.delete_image, image_id)
502
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400503 if CONF.image_feature_enabled.api_v1:
504 # In glance v1 the additional properties are stored in the headers.
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700505 resp = _image_client.check_image(image_id)
506 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400507 image_props = snapshot_image.get('properties', {})
508 else:
509 # In glance v2 the additional properties are flattened.
510 snapshot_image = _image_client.show_image(image_id)
511 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300512
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400513 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300514 if bdm:
515 bdm = json.loads(bdm)
516 if bdm and 'snapshot_id' in bdm[0]:
517 snapshot_id = bdm[0]['snapshot_id']
518 self.addCleanup(
519 self.snapshots_client.wait_for_resource_deletion,
520 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100521 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
522 self.snapshots_client.delete_snapshot,
523 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200524 waiters.wait_for_volume_resource_status(self.snapshots_client,
525 snapshot_id,
526 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000527 image_name = snapshot_image['name']
528 self.assertEqual(name, image_name)
529 LOG.debug("Created snapshot image %s for server %s",
530 image_name, server['name'])
531 return snapshot_image
532
Jordan Pittier7cf64762015-10-14 15:01:12 +0200533 def nova_volume_attach(self, server, volume_to_attach):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000534 volume = self.servers_client.attach_volume(
Jordan Pittier7cf64762015-10-14 15:01:12 +0200535 server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900536 % CONF.compute.volume_device_name)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200537 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200538 waiters.wait_for_volume_resource_status(self.volumes_client,
539 volume['id'], 'in-use')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900540
Jordan Pittier7cf64762015-10-14 15:01:12 +0200541 # Return the updated volume after the attachment
542 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900543
Jordan Pittier7cf64762015-10-14 15:01:12 +0200544 def nova_volume_detach(self, server, volume):
545 self.servers_client.detach_volume(server['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200546 waiters.wait_for_volume_resource_status(self.volumes_client,
547 volume['id'], 'available')
Jordan Pittier7cf64762015-10-14 15:01:12 +0200548
Steven Hardyda2a8352014-10-02 12:52:20 +0100549 def ping_ip_address(self, ip_address, should_succeed=True,
zhufl0ec74c42017-11-15 14:02:28 +0800550 ping_timeout=None, mtu=None, server=None):
lanoux5fc14522015-09-21 08:17:35 +0000551 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000552 cmd = ['ping', '-c1', '-w1']
553
554 if mtu:
555 cmd += [
556 # don't fragment
557 '-M', 'do',
558 # ping receives just the size of ICMP payload
559 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
560 ]
561 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700562
563 def ping():
564 proc = subprocess.Popen(cmd,
565 stdout=subprocess.PIPE,
566 stderr=subprocess.PIPE)
567 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000568
Aaron Rosena7df13b2014-09-23 09:45:45 -0700569 return (proc.returncode == 0) == should_succeed
570
Jordan Pittier9e227c52016-02-09 14:35:18 +0100571 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000572 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800573 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000574 'caller': caller, 'ip': ip_address, 'timeout': timeout,
575 'should_succeed':
576 'reachable' if should_succeed else 'unreachable'
577 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200578 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000579 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800580 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000581 'caller': caller, 'ip': ip_address, 'timeout': timeout,
582 'result': 'expected' if result else 'unexpected'
583 })
zhufl0ec74c42017-11-15 14:02:28 +0800584 if server:
585 self._log_console_output([server])
Shuquan Huang753629e2015-07-20 08:52:29 +0000586 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700587
Yair Friedae0e73d2014-11-24 11:56:26 +0200588 def check_vm_connectivity(self, ip_address,
589 username=None,
590 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000591 should_connect=True,
zhufl0ec74c42017-11-15 14:02:28 +0800592 extra_msg="",
593 server=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000594 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000595 """Check server connectivity
596
Yair Friedae0e73d2014-11-24 11:56:26 +0200597 :param ip_address: server to test against
598 :param username: server's ssh username
599 :param private_key: server's ssh private key to be used
600 :param should_connect: True/False indicates positive/negative test
601 positive - attempt ping and ssh
602 negative - attempt ping and fail if succeed
zhufl0ec74c42017-11-15 14:02:28 +0800603 :param extra_msg: Message to help with debugging if ``ping_ip_address``
604 fails
605 :param server: The server whose console to log for debugging
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000606 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200607
608 :raises: AssertError if the result of the connectivity check does
609 not match the value of the should_connect param
610 """
zhufl0ec74c42017-11-15 14:02:28 +0800611 LOG.debug('checking network connections to IP %s with user: %s',
612 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200613 if should_connect:
614 msg = "Timed out waiting for %s to become reachable" % ip_address
615 else:
616 msg = "ip address %s is reachable" % ip_address
zhufl0ec74c42017-11-15 14:02:28 +0800617 if extra_msg:
618 msg = "%s\n%s" % (extra_msg, msg)
Yair Friedae0e73d2014-11-24 11:56:26 +0200619 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000620 should_succeed=should_connect,
zhufl0ec74c42017-11-15 14:02:28 +0800621 mtu=mtu, server=server),
Yair Friedae0e73d2014-11-24 11:56:26 +0200622 msg=msg)
623 if should_connect:
624 # no need to check ssh for negative connectivity
zhufl0ec74c42017-11-15 14:02:28 +0800625 try:
626 self.get_remote_client(ip_address, username, private_key,
627 server=server)
628 except Exception:
629 if not extra_msg:
630 extra_msg = 'Failed to ssh to %s' % ip_address
631 LOG.exception(extra_msg)
632 raise
Yair Friedae0e73d2014-11-24 11:56:26 +0200633
634 def create_floating_ip(self, thing, pool_name=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000635 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200636
Marc Koderer3b57d802016-03-22 15:23:31 +0100637 if not pool_name:
638 pool_name = CONF.network.floating_network_name
John Warrene74890a2015-11-11 15:18:01 -0500639 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000640 create_floating_ip(pool=pool_name)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100641 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500642 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200643 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500644 self.compute_floating_ips_client.associate_floating_ip_to_server(
Yair Friedae0e73d2014-11-24 11:56:26 +0200645 floating_ip['ip'], thing['id'])
646 return floating_ip
647
Sean Dague20e98612016-01-06 14:33:28 -0500648 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200649 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500650 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200651 private_key=private_key,
652 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300653 if dev_name is not None:
654 ssh_client.make_fs(dev_name)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800655 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
656 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300657 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
658 ssh_client.exec_command(cmd_timestamp)
659 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
660 % mount_path)
661 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800662 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300663 return timestamp
664
Sean Dague20e98612016-01-06 14:33:28 -0500665 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200666 private_key=None, server=None):
Sean Dague20e98612016-01-06 14:33:28 -0500667 ssh_client = self.get_remote_client(ip_address,
Slawek Kaplonski79d8b0f2018-07-30 22:43:41 +0200668 private_key=private_key,
669 server=server)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300670 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700671 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300672 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
673 % mount_path)
674 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800675 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300676 return timestamp
677
Sean Dague20e98612016-01-06 14:33:28 -0500678 def get_server_ip(self, server):
679 """Get the server fixed or floating IP.
680
681 Based on the configuration we're in, return a correct ip
682 address for validating that a guest is up.
683 """
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200684 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500685 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800686 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500687 # method is creating the floating IP there.
688 return self.create_floating_ip(server)['ip']
689 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400690 # Determine the network name to look for based on config or creds
691 # provider network resources.
692 if CONF.validation.network_for_ssh:
693 addresses = server['addresses'][
694 CONF.validation.network_for_ssh]
695 else:
zhufl7b4a7202017-09-28 10:29:27 +0800696 network = self.get_tenant_network()
Matt Riedemanna7782552016-08-08 16:26:01 -0400697 addresses = (server['addresses'][network['name']]
698 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500699 for address in addresses:
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200700 if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
701 address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500702 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800703 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200704 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400705 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200706
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100707
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100708class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300709 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000710
Yair Fried1fc32a12014-08-04 09:11:30 +0300711 This class provide helpers for network scenario tests, using the neutron
712 API. Helpers from ancestor which use the nova network API are overridden
713 with the neutron API.
714
715 This Class also enforces using Neutron instead of novanetwork.
716 Subclassed tests will be skipped if Neutron is not enabled
717
718 """
719
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000720 credentials = ['primary', 'admin']
721
Yair Fried1fc32a12014-08-04 09:11:30 +0300722 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000723 def skip_checks(cls):
724 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100725 if not CONF.service_available.neutron:
726 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300727
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700728 def _create_network(self, networks_client=None,
zhoubin5058bead72017-02-04 18:01:15 +0800729 tenant_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200730 namestart='network-smoke-',
731 port_security_enabled=True):
John Warren94d8faf2015-09-15 12:22:24 -0400732 if not networks_client:
733 networks_client = self.networks_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300734 if not tenant_id:
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700735 tenant_id = networks_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300736 name = data_utils.rand_name(namestart)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400737 network_kwargs = dict(name=name, tenant_id=tenant_id)
738 # Neutron disables port security by default so we have to check the
739 # config before trying to create the network with port_security_enabled
740 if CONF.network_feature_enabled.port_security:
741 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +0200742 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -0500743 network = result['network']
744
745 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100746 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800747 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -0500748 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300749 return network
750
zhufl5b0a52f2017-10-24 15:48:20 +0800751 def create_subnet(self, network, subnets_client=None,
752 namestart='subnet-smoke', **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000753 """Create a subnet for the given network
754
755 within the cidr block configured for tenant networks.
Yair Fried1fc32a12014-08-04 09:11:30 +0300756 """
John Warren3961acd2015-10-02 14:38:53 -0400757 if not subnets_client:
758 subnets_client = self.subnets_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300759
760 def cidr_in_use(cidr, tenant_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000761 """Check cidr existence
762
lei zhangdd552b22015-11-25 20:41:48 +0800763 :returns: True if subnet with cidr already exist in tenant
764 False else
Yair Fried1fc32a12014-08-04 09:11:30 +0300765 """
jeremy.zhang5870ff12017-05-25 11:24:23 +0800766 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100767 tenant_id=tenant_id, cidr=cidr)['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300768 return len(cidr_in_use) != 0
769
Kirill Shileev14113572014-11-21 16:58:02 +0300770 ip_version = kwargs.pop('ip_version', 4)
771
772 if ip_version == 6:
773 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -0400774 CONF.network.project_network_v6_cidr)
775 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300776 else:
Sean Dagueed6e5862016-04-04 10:49:13 -0400777 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
778 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300779
Yair Fried1fc32a12014-08-04 09:11:30 +0300780 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300781 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300782 # Repeatedly attempt subnet creation with sequential cidr
783 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300784 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300785 str_cidr = str(subnet_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -0500786 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300787 continue
788
789 subnet = dict(
790 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -0500791 network_id=network['id'],
792 tenant_id=network['tenant_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +0300793 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300794 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300795 **kwargs
796 )
797 try:
John Warren3961acd2015-10-02 14:38:53 -0400798 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300799 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900800 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300801 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
802 if not is_overlapping_cidr:
803 raise
804 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -0500805
806 subnet = result['subnet']
807 self.assertEqual(subnet['cidr'], str_cidr)
808
809 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
810 subnets_client.delete_subnet, subnet['id'])
811
Yair Fried1fc32a12014-08-04 09:11:30 +0300812 return subnet
813
Kirill Shileev14113572014-11-21 16:58:02 +0300814 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Hongbin Lu95a31692018-06-13 23:17:54 +0000815 if ip_addr:
816 ports = self.os_admin.ports_client.list_ports(
817 device_id=server['id'],
818 fixed_ips='ip_address=%s' % ip_addr)['ports']
819 else:
820 ports = self.os_admin.ports_client.list_ports(
821 device_id=server['id'])['ports']
Kobi Samoray166500a2016-10-09 14:42:48 +0300822 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -0500823 # If the network is dual-stack (IPv4 + IPv6), this port is associated
824 # with 2 subnets
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300825 p_status = ['ACTIVE']
826 # NOTE(vsaienko) With Ironic, instances live on separate hardware
827 # servers. Neutron does not bind ports for Ironic instances, as a
828 # result the port remains in the DOWN state.
Vasyl Saienkoc8aa34b2016-08-01 14:18:37 +0300829 # TODO(vsaienko) remove once bug: #1599836 is resolved.
Thiago Paiva66cded22016-08-15 14:55:58 -0300830 if getattr(CONF.service_available, 'ironic', False):
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300831 p_status.append('DOWN')
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200832 port_map = [(p["id"], fxip["ip_address"])
833 for p in ports
834 for fxip in p["fixed_ips"]
Federico Ressi2d6bcaa2018-04-11 12:37:36 +0200835 if (netutils.is_valid_ipv4(fxip["ip_address"]) and
836 p['status'] in p_status)]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -0800837 inactive = [p for p in ports if p['status'] != 'ACTIVE']
838 if inactive:
839 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200840
Masayuki Igawaf9009b42017-04-10 14:49:29 +0900841 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -0800842 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200843 self.assertEqual(len(port_map), 1,
844 "Found multiple IPv4 addresses: %s. "
845 "Unable to determine which port to target."
846 % port_map)
847 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300848
David Shrewsbury9bac3662014-08-07 15:07:01 -0400849 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +0800850 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100851 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +0200852 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700853 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -0500854 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -0400855
Yair Friedae0e73d2014-11-24 11:56:26 +0200856 def create_floating_ip(self, thing, external_network_id=None,
857 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000858 """Create a floating IP and associates to a resource/port on Neutron"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200859 if not external_network_id:
860 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300861 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -0500862 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300863 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300864 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
865 else:
866 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500867 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300868 floating_network_id=external_network_id,
869 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300870 tenant_id=thing['tenant_id'],
871 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300872 )
Steve Heyman33735f22016-05-24 09:28:08 -0500873 floating_ip = result['floatingip']
Jordan Pittier9e227c52016-02-09 14:35:18 +0100874 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800875 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -0500876 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300877 return floating_ip
878
Yair Fried45f92952014-06-26 05:19:19 +0300879 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000880 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300881
Steve Heyman33735f22016-05-24 09:28:08 -0500882 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +0300883 :param status: target status
884 :raises: AssertionError if status doesn't match
885 """
Steve Heyman33735f22016-05-24 09:28:08 -0500886 floatingip_id = floating_ip['id']
887
Carl Baldwina754e2d2014-10-23 22:47:41 +0000888 def refresh():
Steve Heyman33735f22016-05-24 09:28:08 -0500889 result = (self.floating_ips_client.
890 show_floatingip(floatingip_id)['floatingip'])
891 return status == result['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +0000892
zhufl4dda94e2017-03-14 16:14:46 +0800893 if not test_utils.call_until_true(refresh,
894 CONF.network.build_timeout,
895 CONF.network.build_interval):
896 floating_ip = self.floating_ips_client.show_floatingip(
897 floatingip_id)['floatingip']
898 self.assertEqual(status, floating_ip['status'],
899 message="FloatingIP: {fp} is at status: {cst}. "
900 "failed to reach status: {st}"
901 .format(fp=floating_ip, cst=floating_ip['status'],
902 st=status))
Yair Fried45f92952014-06-26 05:19:19 +0300903 LOG.info("FloatingIP: {fp} is at status: {st}"
904 .format(fp=floating_ip, st=status))
905
zhufl420a0192017-09-28 11:04:50 +0800906 def check_tenant_network_connectivity(self, server,
907 username,
908 private_key,
909 should_connect=True,
910 servers_for_debug=None):
Sean Dagueed6e5862016-04-04 10:49:13 -0400911 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +0300912 msg = 'Tenant networks not configured to be reachable.'
913 LOG.info(msg)
914 return
915 # The target login is assumed to have been configured for
916 # key-based authentication by cloud-init.
917 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +0100918 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +0300919 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900920 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200921 username,
922 private_key,
923 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300924 except Exception as e:
925 LOG.exception('Tenant network connectivity check failed')
926 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000927 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300928 raise
929
zhufle9877c62017-10-13 09:38:19 +0800930 def check_remote_connectivity(self, source, dest, should_succeed=True,
931 nic=None):
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900932 """assert ping server via source ssh connection
933
Yair Fried1fc32a12014-08-04 09:11:30 +0300934 :param source: RemoteClient: an ssh connection from which to ping
zhufle9877c62017-10-13 09:38:19 +0800935 :param dest: an IP to ping against
936 :param should_succeed: boolean: should ping succeed or not
Yair Friedbc46f592015-11-18 16:29:34 +0200937 :param nic: specific network interface to ping from
Yair Fried1fc32a12014-08-04 09:11:30 +0300938 """
939 def ping_remote():
940 try:
Yair Friedbc46f592015-11-18 16:29:34 +0200941 source.ping_host(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +0300942 except lib_exc.SSHExecCommandFailed:
zhangguoqing6c096642016-01-04 06:17:21 +0000943 LOG.warning('Failed to ping IP: %s via a ssh connection '
Jordan Pittier525ec712016-12-07 17:51:26 +0100944 'from: %s.', dest, source.ssh_client.host)
Yair Fried1fc32a12014-08-04 09:11:30 +0300945 return not should_succeed
946 return should_succeed
947
zhufle9877c62017-10-13 09:38:19 +0800948 result = test_utils.call_until_true(ping_remote,
949 CONF.validation.ping_timeout, 1)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -0800950 if result:
951 return
952
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900953 source_host = source.ssh_client.host
954 if should_succeed:
955 msg = "Timed out waiting for %s to become reachable from %s" \
956 % (dest, source_host)
957 else:
958 msg = "%s is reachable from %s" % (dest, source_host)
Ihar Hrachyshkaf9fda2d2017-11-06 13:16:09 -0800959 self._log_console_output()
960 self.fail(msg)
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900961
John Warren456d9ae2016-01-12 15:36:33 -0500962 def _create_security_group(self, security_group_rules_client=None,
963 tenant_id=None,
John Warrenf9606e92015-12-10 12:12:42 -0500964 namestart='secgroup-smoke',
965 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -0500966 if security_group_rules_client is None:
967 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -0500968 if security_groups_client is None:
969 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300970 if tenant_id is None:
John Warrenf9606e92015-12-10 12:12:42 -0500971 tenant_id = security_groups_client.tenant_id
972 secgroup = self._create_empty_security_group(
973 namestart=namestart, client=security_groups_client,
974 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300975
976 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -0500977 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -0500978 security_group_rules_client=security_group_rules_client,
979 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -0500980 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +0300981 for rule in rules:
Steve Heyman33735f22016-05-24 09:28:08 -0500982 self.assertEqual(tenant_id, rule['tenant_id'])
983 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300984 return secgroup
985
Yair Frieddb6c9e92014-08-06 08:53:13 +0300986 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300987 namestart='secgroup-smoke'):
988 """Create a security group without rules.
989
990 Default rules will be created:
991 - IPv4 egress to any
992 - IPv6 egress to any
993
994 :param tenant_id: secgroup will be created in this tenant
Steve Heyman33735f22016-05-24 09:28:08 -0500995 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300996 """
997 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -0500998 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300999 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001000 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001001 sg_name = data_utils.rand_name(namestart)
1002 sg_desc = sg_name + " description"
1003 sg_dict = dict(name=sg_name,
1004 description=sg_desc)
1005 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -05001006 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001007
1008 secgroup = result['security_group']
1009 self.assertEqual(secgroup['name'], sg_name)
1010 self.assertEqual(tenant_id, secgroup['tenant_id'])
1011 self.assertEqual(secgroup['description'], sg_desc)
1012
Jordan Pittier9e227c52016-02-09 14:35:18 +01001013 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001014 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001015 return secgroup
1016
John Warren456d9ae2016-01-12 15:36:33 -05001017 def _create_security_group_rule(self, secgroup=None,
1018 sec_group_rules_client=None,
John Warrenf9606e92015-12-10 12:12:42 -05001019 tenant_id=None,
1020 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001021 """Create a rule from a dictionary of rule parameters.
1022
1023 Create a rule in a secgroup. if secgroup not defined will search for
1024 default secgroup in tenant_id.
1025
Steve Heyman33735f22016-05-24 09:28:08 -05001026 :param secgroup: the security group.
Yair Fried1fc32a12014-08-04 09:11:30 +03001027 :param tenant_id: if secgroup not passed -- the tenant in which to
1028 search for default secgroup
1029 :param kwargs: a dictionary containing rule parameters:
1030 for example, to allow incoming ssh:
1031 rule = {
1032 direction: 'ingress'
1033 protocol:'tcp',
1034 port_range_min: 22,
1035 port_range_max: 22
1036 }
1037 """
John Warren456d9ae2016-01-12 15:36:33 -05001038 if sec_group_rules_client is None:
1039 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001040 if security_groups_client is None:
1041 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001042 if not tenant_id:
John Warrenf9606e92015-12-10 12:12:42 -05001043 tenant_id = security_groups_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001044 if secgroup is None:
zhuflb0b272e2017-09-22 16:01:46 +08001045 # Get default secgroup for tenant_id
1046 default_secgroups = security_groups_client.list_security_groups(
1047 name='default', tenant_id=tenant_id)['security_groups']
1048 msg = "No default security group for tenant %s." % (tenant_id)
1049 self.assertNotEmpty(default_secgroups, msg)
1050 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001051
Steve Heyman33735f22016-05-24 09:28:08 -05001052 ruleset = dict(security_group_id=secgroup['id'],
1053 tenant_id=secgroup['tenant_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001054 ruleset.update(kwargs)
1055
John Warren456d9ae2016-01-12 15:36:33 -05001056 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001057 sg_rule = sg_rule['security_group_rule']
1058
1059 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1060 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001061
1062 return sg_rule
1063
John Warren456d9ae2016-01-12 15:36:33 -05001064 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1065 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001066 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001067 """Create loginable security group rule
1068
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001069 This function will create:
1070 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1071 access for ipv4.
1072 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1073 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001074 """
1075
John Warren456d9ae2016-01-12 15:36:33 -05001076 if security_group_rules_client is None:
1077 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001078 if security_groups_client is None:
1079 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001080 rules = []
1081 rulesets = [
1082 dict(
1083 # ssh
1084 protocol='tcp',
1085 port_range_min=22,
1086 port_range_max=22,
1087 ),
1088 dict(
1089 # ping
1090 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001091 ),
1092 dict(
1093 # ipv6-icmp for ping6
1094 protocol='icmp',
1095 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001096 )
1097 ]
John Warren456d9ae2016-01-12 15:36:33 -05001098 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001099 for ruleset in rulesets:
1100 for r_direction in ['ingress', 'egress']:
1101 ruleset['direction'] = r_direction
1102 try:
1103 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001104 sec_group_rules_client=sec_group_rules_client,
1105 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001106 security_groups_client=security_groups_client,
1107 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001108 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001109 # if rule already exist - skip rule and continue
1110 msg = 'Security group rule already exists'
1111 if msg not in ex._error_string:
1112 raise ex
1113 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001114 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001115 rules.append(sg_rule)
1116
1117 return rules
1118
Yair Frieddb6c9e92014-08-06 08:53:13 +03001119 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001120 """Retrieve a router for the given tenant id.
1121
1122 If a public router has been configured, it will be returned.
1123
1124 If a public router has not been configured, but a public
1125 network has, a tenant router will be created and returned that
1126 routes traffic to the public network.
1127 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001128 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001129 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001130 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001131 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001132 router_id = CONF.network.public_router_id
1133 network_id = CONF.network.public_network_id
1134 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001135 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001136 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001137 elif network_id:
zhufl3484f992017-10-10 16:18:29 +08001138 router = client.create_router(
1139 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
1140 admin_state_up=True,
1141 tenant_id=tenant_id,
1142 external_gateway_info=dict(network_id=network_id))['router']
1143 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1144 client.delete_router, router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001145 return router
1146 else:
1147 raise Exception("Neither of 'public_router_id' or "
1148 "'public_network_id' has been defined.")
1149
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001150 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001151 routers_client=None, subnets_client=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001152 tenant_id=None, dns_nameservers=None,
1153 port_security_enabled=True):
Yair Fried1fc32a12014-08-04 09:11:30 +03001154 """Create a network with a subnet connected to a router.
1155
David Shrewsbury9bac3662014-08-07 15:07:01 -04001156 The baremetal driver is a special case since all nodes are
1157 on the same shared network.
1158
Yair Fried413bf2d2014-11-19 17:07:11 +02001159 :param tenant_id: id of tenant to create resources in.
1160 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001161 :returns: network, subnet, router
1162 """
Thiago Paiva66cded22016-08-15 14:55:58 -03001163 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001164 # NOTE(Shrews): This exception is for environments where tenant
1165 # credential isolation is available, but network separation is
1166 # not (the current baremetal case). Likely can be removed when
1167 # test account mgmt is reworked:
1168 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001169 if not CONF.compute.fixed_network_name:
1170 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001171 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001172 network = self._get_network_by_name(
1173 CONF.compute.fixed_network_name)
1174 router = None
1175 subnet = None
1176 else:
John Warren94d8faf2015-09-15 12:22:24 -04001177 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001178 networks_client=networks_client,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001179 tenant_id=tenant_id,
1180 port_security_enabled=port_security_enabled)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001181 router = self._get_router(client=routers_client,
1182 tenant_id=tenant_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001183 subnet_kwargs = dict(network=network,
zhufl5b0a52f2017-10-24 15:48:20 +08001184 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001185 # use explicit check because empty list is a valid option
1186 if dns_nameservers is not None:
1187 subnet_kwargs['dns_nameservers'] = dns_nameservers
zhufl5b0a52f2017-10-24 15:48:20 +08001188 subnet = self.create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001189 if not routers_client:
1190 routers_client = self.routers_client
1191 router_id = router['id']
1192 routers_client.add_router_interface(router_id,
1193 subnet_id=subnet['id'])
1194
1195 # save a cleanup job to remove this association between
1196 # router and subnet
1197 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1198 routers_client.remove_router_interface, router_id,
1199 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001200 return network, subnet, router
1201
1202
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001203class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001204 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001205
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001206 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001207
1208 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001209 def setup_clients(cls):
1210 super(EncryptionScenarioTest, cls).setup_clients()
Jordan Pittier8160d312017-04-18 11:52:23 +02001211 cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001212 cls.admin_encryption_types_client =\
Jordan Pittier8160d312017-04-18 11:52:23 +02001213 cls.os_admin.encryption_types_v2_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001214
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001215 def create_encryption_type(self, client=None, type_id=None, provider=None,
1216 key_size=None, cipher=None,
1217 control_location=None):
1218 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001219 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001220 if not type_id:
1221 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001222 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001223 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001224 client.create_encryption_type(
1225 type_id, provider=provider, key_size=key_size, cipher=cipher,
jeremy.zhangb6f67f62018-02-11 09:28:52 +08001226 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001227
lkuchlan3023e752017-06-08 12:53:13 +03001228 def create_encrypted_volume(self, encryption_provider, volume_type,
1229 key_size=256, cipher='aes-xts-plain64',
1230 control_location='front-end'):
1231 volume_type = self.create_volume_type(name=volume_type)
1232 self.create_encryption_type(type_id=volume_type['id'],
1233 provider=encryption_provider,
1234 key_size=key_size,
1235 cipher=cipher,
1236 control_location=control_location)
1237 return self.create_volume(volume_type=volume_type['name'])
1238
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001239
Masayuki Igawa0870db52015-09-18 21:08:36 +09001240class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001241 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001242
1243 Subclasses implement the tests that use the methods provided by this
1244 class.
1245 """
1246
1247 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001248 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001249 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001250 if not CONF.service_available.swift:
1251 skip_msg = ("%s skipped as swift is not available" %
1252 cls.__name__)
1253 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001254
1255 @classmethod
1256 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001257 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001258 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001259 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001260 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001261
1262 @classmethod
1263 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001264 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001265 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001266 cls.account_client = cls.os_operator.account_client
1267 cls.container_client = cls.os_operator.container_client
1268 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001269
Chris Dentde456a12014-09-10 12:41:15 +01001270 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001271 """get swift status for our user account."""
1272 self.account_client.list_account_containers()
1273 LOG.debug('Swift status information obtained successfully')
1274
Chris Dentde456a12014-09-10 12:41:15 +01001275 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001276 name = container_name or data_utils.rand_name(
1277 'swift-scenario-container')
ghanshyameed40312017-09-15 18:30:04 +03001278 self.container_client.update_container(name)
Chris Dent0d494112014-08-26 13:48:30 +01001279 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001280 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001281 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001282 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001283 self.container_client.delete_container,
1284 name)
Chris Dent0d494112014-08-26 13:48:30 +01001285 return name
1286
Chris Dentde456a12014-09-10 12:41:15 +01001287 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001288 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001289 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001290
Chris Dentde456a12014-09-10 12:41:15 +01001291 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001292 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001293 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001294 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001295 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001296 self.object_client.delete_object,
1297 container_name,
1298 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001299 return obj_name, obj_data
1300
Chris Dentde456a12014-09-10 12:41:15 +01001301 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001302 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001303 self.list_and_check_container_objects(container_name,
1304 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001305
Chris Dentde456a12014-09-10 12:41:15 +01001306 def list_and_check_container_objects(self, container_name,
1307 present_obj=None,
1308 not_present_obj=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001309 # List objects for a given container and assert which are present and
1310 # which are not.
Ghanshyam2a180b82014-06-16 13:54:22 +09001311 if present_obj is None:
1312 present_obj = []
1313 if not_present_obj is None:
1314 not_present_obj = []
ghanshyam871b1a82017-09-14 02:56:16 +03001315 _, object_list = self.container_client.list_container_objects(
Chris Dent0d494112014-08-26 13:48:30 +01001316 container_name)
1317 if present_obj:
1318 for obj in present_obj:
1319 self.assertIn(obj, object_list)
1320 if not_present_obj:
1321 for obj in not_present_obj:
1322 self.assertNotIn(obj, object_list)
1323
Chris Dentde456a12014-09-10 12:41:15 +01001324 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001325 _, obj = self.object_client.get_object(container_name, obj_name)
1326 self.assertEqual(obj, expected_data)