blob: 48cb17e81398d6e3343d600ea70625401819ec22 [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
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +030086
Jordan Pittierf672b7d2016-06-20 18:50:40 +020087 # ## Test functions library
88 #
89 # The create_[resource] functions only return body and discard the
90 # resp part which is not used in scenario tests
Andrea Frittoli247058f2014-07-16 16:09:22 +010091
Lenny Verkhovsky136376f2016-06-29 14:33:34 +030092 def _create_port(self, network_id, client=None, namestart='port-quotatest',
93 **kwargs):
94 if not client:
95 client = self.ports_client
96 name = data_utils.rand_name(namestart)
97 result = client.create_port(
98 name=name,
99 network_id=network_id,
100 **kwargs)
101 self.assertIsNotNone(result, 'Unable to allocate port')
102 port = result['port']
103 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
104 client.delete_port, port['id'])
105 return port
106
Yair Frieddb6c9e92014-08-06 08:53:13 +0300107 def create_keypair(self, client=None):
108 if not client:
109 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100110 name = data_utils.rand_name(self.__class__.__name__)
111 # We don't need to create a keypair by pubkey in scenario
Ken'ichi Ohmichie364bce2015-07-17 10:27:59 +0000112 body = client.create_keypair(name=name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300113 self.addCleanup(client.delete_keypair, name)
ghanshyamdee01f22015-08-17 11:41:47 +0900114 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100115
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530116 def create_server(self, name=None, image_id=None, flavor=None,
zhufl13c9c892017-02-10 12:04:07 +0800117 validatable=False, wait_until='ACTIVE',
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200118 clients=None, **kwargs):
lanoux5fc14522015-09-21 08:17:35 +0000119 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100120
lanoux5fc14522015-09-21 08:17:35 +0000121 This wrapper utility calls the common create test server and
122 returns a test server. The purpose of this wrapper is to minimize
123 the impact on the code of the tests already using this
124 function.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100125 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100126
lanoux5fc14522015-09-21 08:17:35 +0000127 # NOTE(jlanoux): As a first step, ssh checks in the scenario
128 # tests need to be run regardless of the run_validation and
129 # validatable parameters and thus until the ssh validation job
130 # becomes voting in CI. The test resources management and IP
131 # association are taken care of in the scenario tests.
132 # Therefore, the validatable parameter is set to false in all
133 # those tests. In this way create_server just return a standard
134 # server and the scenario tests always perform ssh checks.
135
136 # Needed for the cross_tenant_traffic test:
137 if clients is None:
jeremy.zhang0343be52017-05-25 21:29:57 +0800138 clients = self.os_primary
lanoux5fc14522015-09-21 08:17:35 +0000139
zhufl24208c22016-10-25 15:23:48 +0800140 if name is None:
141 name = data_utils.rand_name(self.__class__.__name__ + "-server")
142
lanoux5fc14522015-09-21 08:17:35 +0000143 vnic_type = CONF.network.port_vnic_type
144
145 # If vnic_type is configured create port for
146 # every network
147 if vnic_type:
148 ports = []
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300149
lanoux5fc14522015-09-21 08:17:35 +0000150 create_port_body = {'binding:vnic_type': vnic_type,
151 'namestart': 'port-smoke'}
152 if kwargs:
153 # Convert security group names to security group ids
154 # to pass to create_port
155 if 'security_groups' in kwargs:
Thiago Paiva66cded22016-08-15 14:55:58 -0300156 security_groups = \
John Warrenf9606e92015-12-10 12:12:42 -0500157 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000158 ).get('security_groups')
159 sec_dict = dict([(s['name'], s['id'])
160 for s in security_groups])
161
162 sec_groups_names = [s['name'] for s in kwargs.pop(
163 'security_groups')]
164 security_groups_ids = [sec_dict[s]
165 for s in sec_groups_names]
166
167 if security_groups_ids:
168 create_port_body[
169 'security_groups'] = security_groups_ids
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300170 networks = kwargs.pop('networks', [])
171 else:
172 networks = []
lanoux5fc14522015-09-21 08:17:35 +0000173
174 # If there are no networks passed to us we look up
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300175 # for the project's private networks and create a port.
176 # The same behaviour as we would expect when passing
177 # the call to the clients with no networks
lanoux5fc14522015-09-21 08:17:35 +0000178 if not networks:
179 networks = clients.networks_client.list_networks(
Lenny Verkhovsky136376f2016-06-29 14:33:34 +0300180 **{'router:external': False, 'fields': 'id'})['networks']
181
182 # It's net['uuid'] if networks come from kwargs
183 # and net['id'] if they come from
184 # clients.networks_client.list_networks
lanoux5fc14522015-09-21 08:17:35 +0000185 for net in networks:
Lenny Verkhovsky97f7cea2016-08-15 13:29:48 +0000186 net_id = net.get('uuid', net.get('id'))
Lenny Verkhovsky69363502016-07-17 16:33:33 +0300187 if 'port' not in net:
188 port = self._create_port(network_id=net_id,
189 client=clients.ports_client,
190 **create_port_body)
191 ports.append({'port': port['id']})
192 else:
193 ports.append({'port': net['port']})
lanoux5fc14522015-09-21 08:17:35 +0000194 if ports:
195 kwargs['networks'] = ports
196 self.ports = ports
197
198 tenant_network = self.get_tenant_network()
199
Ferenc Horváthbce1fcf2017-06-07 11:19:51 +0200200 body, _ = compute.create_test_server(
lanoux5fc14522015-09-21 08:17:35 +0000201 clients,
202 tenant_network=tenant_network,
203 wait_until=wait_until,
Anusha Ramineni9aaef8b2016-01-19 10:56:40 +0530204 name=name, flavor=flavor,
205 image_id=image_id, **kwargs)
lanoux5fc14522015-09-21 08:17:35 +0000206
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200207 self.addCleanup(waiters.wait_for_server_termination,
208 clients.servers_client, body['id'])
209 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
210 clients.servers_client.delete_server, body['id'])
lanoux5fc14522015-09-21 08:17:35 +0000211 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100212 return server
213
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100214 def create_volume(self, size=None, name=None, snapshot_id=None,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100215 imageRef=None, volume_type=None):
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700216 if size is None:
217 size = CONF.volume.volume_size
Nuno Santosb746d992016-11-17 15:41:55 -0500218 if imageRef:
219 image = self.compute_images_client.show_image(imageRef)['image']
220 min_disk = image.get('minDisk')
221 size = max(size, min_disk)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100222 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800223 name = data_utils.rand_name(self.__class__.__name__ + "-volume")
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900224 kwargs = {'display_name': name,
225 'snapshot_id': snapshot_id,
226 'imageRef': imageRef,
Ken'ichi Ohmichiadb905e2016-08-26 15:16:23 -0700227 'volume_type': volume_type,
228 'size': size}
Ghanshyam8fc0ed22015-12-18 10:25:14 +0900229 volume = self.volumes_client.create_volume(**kwargs)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700230
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100231 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
232 volume['id'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100233 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Jordan Pittier5e1741c2016-03-02 18:25:51 +0100234 self.volumes_client.delete_volume, volume['id'])
lkuchlan5cbc00a2017-03-26 11:49:54 +0300235 self.assertEqual(name, volume['name'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200236 waiters.wait_for_volume_resource_status(self.volumes_client,
237 volume['id'], 'available')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100238 # The volume retrieved on creation has a non-up-to-date status.
239 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000240 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100241 return volume
242
lkuchlan73ed1f32017-07-06 16:22:12 +0300243 def create_volume_snapshot(self, volume_id, name=None, description=None,
244 metadata=None, force=False):
245 name = name or data_utils.rand_name(
246 self.__class__.__name__ + '-snapshot')
247 snapshot = self.snapshots_client.create_snapshot(
248 volume_id=volume_id,
249 force=force,
250 display_name=name,
251 description=description,
252 metadata=metadata)['snapshot']
253 self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
254 snapshot['id'])
255 self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
256 waiters.wait_for_volume_resource_status(self.snapshots_client,
257 snapshot['id'], 'available')
258 return snapshot
259
scottda61f68ac2016-06-07 12:07:55 -0600260 def create_volume_type(self, client=None, name=None, backend_name=None):
261 if not client:
zhufl708821c2017-07-12 16:08:34 +0800262 client = self.os_admin.volume_types_v2_client
scottda61f68ac2016-06-07 12:07:55 -0600263 if not name:
264 class_name = self.__class__.__name__
265 name = data_utils.rand_name(class_name + '-volume-type')
266 randomized_name = data_utils.rand_name('scenario-type-' + name)
267
268 LOG.debug("Creating a volume type: %s on backend %s",
269 randomized_name, backend_name)
270 extra_specs = {}
271 if backend_name:
272 extra_specs = {"volume_backend_name": backend_name}
273
274 body = client.create_volume_type(name=randomized_name,
275 extra_specs=extra_specs)
276 volume_type = body['volume_type']
277 self.assertIn('id', volume_type)
278 self.addCleanup(client.delete_volume_type, volume_type['id'])
279 return volume_type
280
Yair Fried1fc32a12014-08-04 09:11:30 +0300281 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500282 _client = self.compute_security_groups_client
John Warren5cdbf422016-01-05 12:42:43 -0500283 _client_rules = self.compute_security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100284 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900285 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100286 for sg in sgs:
287 if sg['name'] == 'default':
288 secgroup_id = sg['id']
289
290 # These rules are intended to permit inbound ssh and icmp
291 # traffic from all sources, so no group_id is provided.
292 # Setting a group_id would only permit traffic from ports
293 # belonging to the same security group.
294 rulesets = [
295 {
296 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000297 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100298 'from_port': 22,
299 'to_port': 22,
300 'cidr': '0.0.0.0/0',
301 },
302 {
303 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000304 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100305 'from_port': -1,
306 'to_port': -1,
307 'cidr': '0.0.0.0/0',
308 }
309 ]
310 rules = list()
311 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000312 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900313 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100314 rules.append(sg_rule)
315 return rules
316
Yair Fried1fc32a12014-08-04 09:11:30 +0300317 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100318 # Create security group
319 sg_name = data_utils.rand_name(self.__class__.__name__)
320 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500321 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900322 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100323 self.assertEqual(secgroup['name'], sg_name)
324 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500325 self.addCleanup(
Jordan Pittier9e227c52016-02-09 14:35:18 +0100326 test_utils.call_and_ignore_notfound_exc,
John Warrenf2345512015-12-10 13:39:30 -0500327 self.compute_security_groups_client.delete_security_group,
328 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100329
330 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300331 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100332
333 return secgroup
334
zhuflf52c7592017-05-25 13:55:24 +0800335 def get_remote_client(self, ip_address, username=None, private_key=None,
336 server=None):
JordanP3fe2dc32014-11-17 13:06:01 +0100337 """Get a SSH client to a remote server
338
Sean Dague20e98612016-01-06 14:33:28 -0500339 @param ip_address the server floating or fixed IP address to use
340 for ssh validation
JordanP3fe2dc32014-11-17 13:06:01 +0100341 @param username name of the Linux account on the remote server
342 @param private_key the SSH private key to use
zhuflf52c7592017-05-25 13:55:24 +0800343 @param server: server dict, used for debugging purposes
JordanP3fe2dc32014-11-17 13:06:01 +0100344 @return a RemoteClient object
345 """
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700346
Andrea Frittoli247058f2014-07-16 16:09:22 +0100347 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800348 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800349 # Set this with 'keypair' or others to log in with keypair or
350 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000351 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800352 password = None
353 if private_key is None:
354 private_key = self.keypair['private_key']
355 else:
lanoux283273b2015-12-04 03:01:54 -0800356 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800357 private_key = None
zhuflf52c7592017-05-25 13:55:24 +0800358 linux_client = remote_client.RemoteClient(
359 ip_address, username, pkey=private_key, password=password,
360 server=server, servers_client=self.servers_client)
361 linux_client.validate_authentication()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100362 return linux_client
363
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000364 def _image_create(self, name, fmt, path,
365 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900366 if properties is None:
367 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100368 name = data_utils.rand_name('%s-' % name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100369 params = {
370 'name': name,
371 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000372 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100373 }
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400374 if CONF.image_feature_enabled.api_v1:
375 params['is_public'] = 'False'
376 params['properties'] = properties
Ken'ichi Ohmichi02bcdf32016-06-17 16:41:26 -0700377 params = {'headers': common_image.image_meta_to_headers(**params)}
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400378 else:
379 params['visibility'] = 'private'
380 # Additional properties are flattened out in the v2 API.
381 params.update(properties)
382 body = self.image_client.create_image(**params)
383 image = body['image'] if 'image' in body else body
Andrea Frittoli247058f2014-07-16 16:09:22 +0100384 self.addCleanup(self.image_client.delete_image, image['id'])
385 self.assertEqual("queued", image['status'])
zhang.leia4b1cef2016-03-01 10:50:01 +0800386 with open(path, 'rb') as image_file:
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400387 if CONF.image_feature_enabled.api_v1:
388 self.image_client.update_image(image['id'], data=image_file)
389 else:
390 self.image_client.store_image_file(image['id'], image_file)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100391 return image['id']
392
393 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300394 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100395 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
396 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
397 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300398 img_container_format = CONF.scenario.img_container_format
399 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000400 img_properties = CONF.scenario.img_properties
PranaliD2aa523c2016-06-07 03:54:34 -0400401 LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
Jordan Pittier525ec712016-12-07 17:51:26 +0100402 "properties: %s, ami: %s, ari: %s, aki: %s",
403 img_path, img_container_format, img_disk_format,
404 img_properties, ami_img_path, ari_img_path, aki_img_path)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100405 try:
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100406 image = self._image_create('scenario-img',
407 img_container_format,
408 img_path,
409 disk_format=img_disk_format,
410 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100411 except IOError:
412 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
413 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
414 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000415 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100416 image = self._image_create('scenario-ami', 'ami',
417 path=ami_img_path,
418 properties=properties)
Jordan Pittier525ec712016-12-07 17:51:26 +0100419 LOG.debug("image:%s", image)
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100420
421 return image
Andrea Frittoli247058f2014-07-16 16:09:22 +0100422
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700423 def _log_console_output(self, servers=None, client=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400424 if not CONF.compute_feature_enabled.console_output:
425 LOG.debug('Console output not supported, cannot log')
426 return
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700427 client = client or self.servers_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100428 if not servers:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700429 servers = client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100430 servers = servers['servers']
431 for server in servers:
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100432 try:
Ihar Hrachyshkaa9dca2b2017-04-04 14:17:11 -0700433 console_output = client.get_console_output(
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100434 server['id'])['output']
435 LOG.debug('Console output for %s\nbody=\n%s',
436 server['id'], console_output)
437 except lib_exc.NotFound:
Attila Fazekase1360482016-11-10 11:28:08 +0100438 LOG.debug("Server %s disappeared(deleted) while looking "
Attila Fazekas9a5a1122016-11-08 10:24:57 +0100439 "for the console log", server['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100440
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000441 def _log_net_info(self, exc):
442 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300443 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000444 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000445
nithya-ganesan882595e2014-07-29 18:51:07 +0000446 def create_server_snapshot(self, server, name=None):
447 # Glance client
448 _image_client = self.image_client
449 # Compute client
Ghanshyamae76c122015-12-22 13:41:35 +0900450 _images_client = self.compute_images_client
nithya-ganesan882595e2014-07-29 18:51:07 +0000451 if name is None:
zhuflf9d95722016-10-19 16:06:17 +0800452 name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000453 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000454 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500455 image_id = image.response['location'].split('images/')[1]
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +0300456 waiters.wait_for_image_status(_image_client, image_id, 'active')
Jordan Pittierf672b7d2016-06-20 18:50:40 +0200457
458 self.addCleanup(_image_client.wait_for_resource_deletion,
459 image_id)
460 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
461 _image_client.delete_image, image_id)
462
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400463 if CONF.image_feature_enabled.api_v1:
464 # In glance v1 the additional properties are stored in the headers.
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700465 resp = _image_client.check_image(image_id)
466 snapshot_image = common_image.get_image_meta_from_headers(resp)
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400467 image_props = snapshot_image.get('properties', {})
468 else:
469 # In glance v2 the additional properties are flattened.
470 snapshot_image = _image_client.show_image(image_id)
471 image_props = snapshot_image
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300472
Matt Riedemann2aa19d42016-06-06 17:45:41 -0400473 bdm = image_props.get('block_device_mapping')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300474 if bdm:
475 bdm = json.loads(bdm)
476 if bdm and 'snapshot_id' in bdm[0]:
477 snapshot_id = bdm[0]['snapshot_id']
478 self.addCleanup(
479 self.snapshots_client.wait_for_resource_deletion,
480 snapshot_id)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100481 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
482 self.snapshots_client.delete_snapshot,
483 snapshot_id)
lkuchlan52d7b0d2016-11-07 20:53:19 +0200484 waiters.wait_for_volume_resource_status(self.snapshots_client,
485 snapshot_id,
486 'available')
nithya-ganesan882595e2014-07-29 18:51:07 +0000487 image_name = snapshot_image['name']
488 self.assertEqual(name, image_name)
489 LOG.debug("Created snapshot image %s for server %s",
490 image_name, server['name'])
491 return snapshot_image
492
Jordan Pittier7cf64762015-10-14 15:01:12 +0200493 def nova_volume_attach(self, server, volume_to_attach):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000494 volume = self.servers_client.attach_volume(
Jordan Pittier7cf64762015-10-14 15:01:12 +0200495 server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900496 % CONF.compute.volume_device_name)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200497 self.assertEqual(volume_to_attach['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200498 waiters.wait_for_volume_resource_status(self.volumes_client,
499 volume['id'], 'in-use')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900500
Jordan Pittier7cf64762015-10-14 15:01:12 +0200501 # Return the updated volume after the attachment
502 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900503
Jordan Pittier7cf64762015-10-14 15:01:12 +0200504 def nova_volume_detach(self, server, volume):
505 self.servers_client.detach_volume(server['id'], volume['id'])
lkuchlan52d7b0d2016-11-07 20:53:19 +0200506 waiters.wait_for_volume_resource_status(self.volumes_client,
507 volume['id'], 'available')
Jordan Pittier7cf64762015-10-14 15:01:12 +0200508
509 volume = self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900510 self.assertEqual('available', volume['status'])
511
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700512 def rebuild_server(self, server_id, image=None,
513 preserve_ephemeral=False, wait=True,
514 rebuild_kwargs=None):
515 if image is None:
516 image = CONF.compute.image_ref
517
518 rebuild_kwargs = rebuild_kwargs or {}
519
520 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
521 server_id, image, preserve_ephemeral)
Ken'ichi Ohmichi5271b0f2015-08-10 07:53:27 +0000522 self.servers_client.rebuild_server(
523 server_id=server_id, image_ref=image,
524 preserve_ephemeral=preserve_ephemeral,
525 **rebuild_kwargs)
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700526 if wait:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000527 waiters.wait_for_server_status(self.servers_client,
528 server_id, 'ACTIVE')
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700529
Steven Hardyda2a8352014-10-02 12:52:20 +0100530 def ping_ip_address(self, ip_address, should_succeed=True,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000531 ping_timeout=None, mtu=None):
lanoux5fc14522015-09-21 08:17:35 +0000532 timeout = ping_timeout or CONF.validation.ping_timeout
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000533 cmd = ['ping', '-c1', '-w1']
534
535 if mtu:
536 cmd += [
537 # don't fragment
538 '-M', 'do',
539 # ping receives just the size of ICMP payload
540 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
541 ]
542 cmd.append(ip_address)
Aaron Rosena7df13b2014-09-23 09:45:45 -0700543
544 def ping():
545 proc = subprocess.Popen(cmd,
546 stdout=subprocess.PIPE,
547 stderr=subprocess.PIPE)
548 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000549
Aaron Rosena7df13b2014-09-23 09:45:45 -0700550 return (proc.returncode == 0) == should_succeed
551
Jordan Pittier9e227c52016-02-09 14:35:18 +0100552 caller = test_utils.find_test_caller()
Shuquan Huang753629e2015-07-20 08:52:29 +0000553 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
John L. Villalovosa898aec2017-01-13 14:46:46 -0800554 ' expected result is %(should_succeed)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000555 'caller': caller, 'ip': ip_address, 'timeout': timeout,
556 'should_succeed':
557 'reachable' if should_succeed else 'unreachable'
558 })
Jordan Pittier35a63752016-08-30 13:09:12 +0200559 result = test_utils.call_until_true(ping, timeout, 1)
Shuquan Huang753629e2015-07-20 08:52:29 +0000560 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
John L. Villalovosa898aec2017-01-13 14:46:46 -0800561 'ping result is %(result)s', {
Shuquan Huang753629e2015-07-20 08:52:29 +0000562 'caller': caller, 'ip': ip_address, 'timeout': timeout,
563 'result': 'expected' if result else 'unexpected'
564 })
565 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700566
Yair Friedae0e73d2014-11-24 11:56:26 +0200567 def check_vm_connectivity(self, ip_address,
568 username=None,
569 private_key=None,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000570 should_connect=True,
571 mtu=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000572 """Check server connectivity
573
Yair Friedae0e73d2014-11-24 11:56:26 +0200574 :param ip_address: server to test against
575 :param username: server's ssh username
576 :param private_key: server's ssh private key to be used
577 :param should_connect: True/False indicates positive/negative test
578 positive - attempt ping and ssh
579 negative - attempt ping and fail if succeed
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000580 :param mtu: network MTU to use for connectivity validation
Yair Friedae0e73d2014-11-24 11:56:26 +0200581
582 :raises: AssertError if the result of the connectivity check does
583 not match the value of the should_connect param
584 """
585 if should_connect:
586 msg = "Timed out waiting for %s to become reachable" % ip_address
587 else:
588 msg = "ip address %s is reachable" % ip_address
589 self.assertTrue(self.ping_ip_address(ip_address,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000590 should_succeed=should_connect,
591 mtu=mtu),
Yair Friedae0e73d2014-11-24 11:56:26 +0200592 msg=msg)
593 if should_connect:
594 # no need to check ssh for negative connectivity
595 self.get_remote_client(ip_address, username, private_key)
596
597 def check_public_network_connectivity(self, ip_address, username,
598 private_key, should_connect=True,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000599 msg=None, servers=None, mtu=None):
Yair Friedae0e73d2014-11-24 11:56:26 +0200600 # The target login is assumed to have been configured for
601 # key-based authentication by cloud-init.
Jordan Pittier525ec712016-12-07 17:51:26 +0100602 LOG.debug('checking network connections to IP %s with user: %s',
603 ip_address, username)
Yair Friedae0e73d2014-11-24 11:56:26 +0200604 try:
605 self.check_vm_connectivity(ip_address,
606 username,
607 private_key,
Ihar Hrachyshkaf9227c02016-09-15 11:16:47 +0000608 should_connect=should_connect,
609 mtu=mtu)
Matthew Treinish53483132014-12-09 18:50:06 -0500610 except Exception:
Yair Friedae0e73d2014-11-24 11:56:26 +0200611 ex_msg = 'Public network connectivity check failed'
612 if msg:
613 ex_msg += ": " + msg
614 LOG.exception(ex_msg)
615 self._log_console_output(servers)
Yair Friedae0e73d2014-11-24 11:56:26 +0200616 raise
617
618 def create_floating_ip(self, thing, pool_name=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000619 """Create a floating IP and associates to a server on Nova"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200620
Marc Koderer3b57d802016-03-22 15:23:31 +0100621 if not pool_name:
622 pool_name = CONF.network.floating_network_name
John Warrene74890a2015-11-11 15:18:01 -0500623 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000624 create_floating_ip(pool=pool_name)['floating_ip'])
Jordan Pittier9e227c52016-02-09 14:35:18 +0100625 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
John Warrene74890a2015-11-11 15:18:01 -0500626 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200627 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500628 self.compute_floating_ips_client.associate_floating_ip_to_server(
Yair Friedae0e73d2014-11-24 11:56:26 +0200629 floating_ip['ip'], thing['id'])
630 return floating_ip
631
Sean Dague20e98612016-01-06 14:33:28 -0500632 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700633 private_key=None):
Sean Dague20e98612016-01-06 14:33:28 -0500634 ssh_client = self.get_remote_client(ip_address,
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700635 private_key=private_key)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300636 if dev_name is not None:
637 ssh_client.make_fs(dev_name)
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800638 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
639 mount_path))
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300640 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
641 ssh_client.exec_command(cmd_timestamp)
642 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
643 % mount_path)
644 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800645 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300646 return timestamp
647
Sean Dague20e98612016-01-06 14:33:28 -0500648 def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700649 private_key=None):
Sean Dague20e98612016-01-06 14:33:28 -0500650 ssh_client = self.get_remote_client(ip_address,
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700651 private_key=private_key)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300652 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700653 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300654 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
655 % mount_path)
656 if dev_name is not None:
Ken'ichi Ohmichi4e5a69e2017-03-01 18:15:29 -0800657 ssh_client.exec_command('sudo umount %s' % mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300658 return timestamp
659
Sean Dague20e98612016-01-06 14:33:28 -0500660 def get_server_ip(self, server):
661 """Get the server fixed or floating IP.
662
663 Based on the configuration we're in, return a correct ip
664 address for validating that a guest is up.
665 """
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200666 if CONF.validation.connect_method == 'floating':
Sean Dague20e98612016-01-06 14:33:28 -0500667 # The tests calling this method don't have a floating IP
zhufl0892cb22016-05-06 14:46:00 +0800668 # and can't make use of the validation resources. So the
Sean Dague20e98612016-01-06 14:33:28 -0500669 # method is creating the floating IP there.
670 return self.create_floating_ip(server)['ip']
671 elif CONF.validation.connect_method == 'fixed':
Matt Riedemanna7782552016-08-08 16:26:01 -0400672 # Determine the network name to look for based on config or creds
673 # provider network resources.
674 if CONF.validation.network_for_ssh:
675 addresses = server['addresses'][
676 CONF.validation.network_for_ssh]
677 else:
678 creds_provider = self._get_credentials_provider()
679 net_creds = creds_provider.get_primary_creds()
680 network = getattr(net_creds, 'network', None)
681 addresses = (server['addresses'][network['name']]
682 if network else [])
Sean Dague20e98612016-01-06 14:33:28 -0500683 for address in addresses:
Matt Riedemanna7782552016-08-08 16:26:01 -0400684 if (address['version'] == CONF.validation.ip_version_for_ssh
685 and address['OS-EXT-IPS:type'] == 'fixed'):
Sean Dague20e98612016-01-06 14:33:28 -0500686 return address['addr']
zhufl955f82b2016-07-22 11:14:34 +0800687 raise exceptions.ServerUnreachable(server_id=server['id'])
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200688 else:
Matthew Treinish4217a702016-10-07 17:27:11 -0400689 raise lib_exc.InvalidConfiguration()
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200690
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100691
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100692class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300693 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000694
Yair Fried1fc32a12014-08-04 09:11:30 +0300695 This class provide helpers for network scenario tests, using the neutron
696 API. Helpers from ancestor which use the nova network API are overridden
697 with the neutron API.
698
699 This Class also enforces using Neutron instead of novanetwork.
700 Subclassed tests will be skipped if Neutron is not enabled
701
702 """
703
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000704 credentials = ['primary', 'admin']
705
Yair Fried1fc32a12014-08-04 09:11:30 +0300706 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000707 def skip_checks(cls):
708 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100709 if not CONF.service_available.neutron:
710 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300711
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700712 def _create_network(self, networks_client=None,
zhoubin5058bead72017-02-04 18:01:15 +0800713 tenant_id=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +0200714 namestart='network-smoke-',
715 port_security_enabled=True):
John Warren94d8faf2015-09-15 12:22:24 -0400716 if not networks_client:
717 networks_client = self.networks_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300718 if not tenant_id:
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700719 tenant_id = networks_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300720 name = data_utils.rand_name(namestart)
Matt Riedemann039b2fe2016-09-15 16:12:24 -0400721 network_kwargs = dict(name=name, tenant_id=tenant_id)
722 # Neutron disables port security by default so we have to check the
723 # config before trying to create the network with port_security_enabled
724 if CONF.network_feature_enabled.port_security:
725 network_kwargs['port_security_enabled'] = port_security_enabled
Markus Zoeller156b5da2016-07-11 18:10:31 +0200726 result = networks_client.create_network(**network_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -0500727 network = result['network']
728
729 self.assertEqual(network['name'], name)
Jordan Pittier9e227c52016-02-09 14:35:18 +0100730 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800731 networks_client.delete_network,
Steve Heyman33735f22016-05-24 09:28:08 -0500732 network['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300733 return network
734
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -0700735 def _create_subnet(self, network, subnets_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +0000736 routers_client=None, namestart='subnet-smoke',
737 **kwargs):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000738 """Create a subnet for the given network
739
740 within the cidr block configured for tenant networks.
Yair Fried1fc32a12014-08-04 09:11:30 +0300741 """
John Warren3961acd2015-10-02 14:38:53 -0400742 if not subnets_client:
743 subnets_client = self.subnets_client
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +0000744 if not routers_client:
745 routers_client = self.routers_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300746
747 def cidr_in_use(cidr, tenant_id):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000748 """Check cidr existence
749
lei zhangdd552b22015-11-25 20:41:48 +0800750 :returns: True if subnet with cidr already exist in tenant
751 False else
Yair Fried1fc32a12014-08-04 09:11:30 +0300752 """
jeremy.zhang5870ff12017-05-25 11:24:23 +0800753 cidr_in_use = self.os_admin.subnets_client.list_subnets(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100754 tenant_id=tenant_id, cidr=cidr)['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300755 return len(cidr_in_use) != 0
756
Kirill Shileev14113572014-11-21 16:58:02 +0300757 ip_version = kwargs.pop('ip_version', 4)
758
759 if ip_version == 6:
760 tenant_cidr = netaddr.IPNetwork(
Sean Dagueed6e5862016-04-04 10:49:13 -0400761 CONF.network.project_network_v6_cidr)
762 num_bits = CONF.network.project_network_v6_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300763 else:
Sean Dagueed6e5862016-04-04 10:49:13 -0400764 tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
765 num_bits = CONF.network.project_network_mask_bits
Kirill Shileev14113572014-11-21 16:58:02 +0300766
Yair Fried1fc32a12014-08-04 09:11:30 +0300767 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300768 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300769 # Repeatedly attempt subnet creation with sequential cidr
770 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300771 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300772 str_cidr = str(subnet_cidr)
Steve Heyman33735f22016-05-24 09:28:08 -0500773 if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300774 continue
775
776 subnet = dict(
777 name=data_utils.rand_name(namestart),
Steve Heyman33735f22016-05-24 09:28:08 -0500778 network_id=network['id'],
779 tenant_id=network['tenant_id'],
Yair Fried1fc32a12014-08-04 09:11:30 +0300780 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300781 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300782 **kwargs
783 )
784 try:
John Warren3961acd2015-10-02 14:38:53 -0400785 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300786 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900787 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300788 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
789 if not is_overlapping_cidr:
790 raise
791 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Steve Heyman33735f22016-05-24 09:28:08 -0500792
793 subnet = result['subnet']
794 self.assertEqual(subnet['cidr'], str_cidr)
795
796 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
797 subnets_client.delete_subnet, subnet['id'])
798
Yair Fried1fc32a12014-08-04 09:11:30 +0300799 return subnet
800
Kirill Shileev14113572014-11-21 16:58:02 +0300801 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
jeremy.zhang5870ff12017-05-25 11:24:23 +0800802 ports = self.os_admin.ports_client.list_ports(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100803 device_id=server['id'], fixed_ip=ip_addr)['ports']
Kobi Samoray166500a2016-10-09 14:42:48 +0300804 # A port can have more than one IP address in some cases.
Sean M. Collins2e896832015-12-15 13:58:47 -0500805 # If the network is dual-stack (IPv4 + IPv6), this port is associated
806 # with 2 subnets
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300807 p_status = ['ACTIVE']
808 # NOTE(vsaienko) With Ironic, instances live on separate hardware
809 # servers. Neutron does not bind ports for Ironic instances, as a
810 # result the port remains in the DOWN state.
Vasyl Saienkoc8aa34b2016-08-01 14:18:37 +0300811 # TODO(vsaienko) remove once bug: #1599836 is resolved.
Thiago Paiva66cded22016-08-15 14:55:58 -0300812 if getattr(CONF.service_available, 'ironic', False):
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300813 p_status.append('DOWN')
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200814 port_map = [(p["id"], fxip["ip_address"])
815 for p in ports
816 for fxip in p["fixed_ips"]
Yatin Kumbhareee4924c2016-06-09 15:12:06 +0530817 if netutils.is_valid_ipv4(fxip["ip_address"])
Vasyl Saienko8fd517c2016-05-30 09:52:54 +0300818 and p['status'] in p_status]
Kevin Benton1d0c1dc2016-02-04 14:30:08 -0800819 inactive = [p for p in ports if p['status'] != 'ACTIVE']
820 if inactive:
821 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200822
Masayuki Igawaf9009b42017-04-10 14:49:29 +0900823 self.assertNotEmpty(port_map,
John L. Villalovosb83286f2015-11-04 14:46:57 -0800824 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200825 self.assertEqual(len(port_map), 1,
826 "Found multiple IPv4 addresses: %s. "
827 "Unable to determine which port to target."
828 % port_map)
829 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300830
David Shrewsbury9bac3662014-08-07 15:07:01 -0400831 def _get_network_by_name(self, network_name):
jeremy.zhang5870ff12017-05-25 11:24:23 +0800832 net = self.os_admin.networks_client.list_networks(
Jordan Pittier64e6b442017-02-20 19:29:02 +0100833 name=network_name)['networks']
Ferenc Horváth268ccce2017-06-08 12:39:02 +0200834 self.assertNotEmpty(net,
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700835 "Unable to get network by name: %s" % network_name)
Steve Heyman33735f22016-05-24 09:28:08 -0500836 return net[0]
David Shrewsbury9bac3662014-08-07 15:07:01 -0400837
Yair Friedae0e73d2014-11-24 11:56:26 +0200838 def create_floating_ip(self, thing, external_network_id=None,
839 port_id=None, client=None):
Ken'ichi Ohmichia112a592015-11-17 08:49:37 +0000840 """Create a floating IP and associates to a resource/port on Neutron"""
Yair Friedae0e73d2014-11-24 11:56:26 +0200841 if not external_network_id:
842 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300843 if not client:
John Warrenfbf2a892015-11-17 12:36:14 -0500844 client = self.floating_ips_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300845 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300846 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
847 else:
848 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500849 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300850 floating_network_id=external_network_id,
851 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300852 tenant_id=thing['tenant_id'],
853 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300854 )
Steve Heyman33735f22016-05-24 09:28:08 -0500855 floating_ip = result['floatingip']
Jordan Pittier9e227c52016-02-09 14:35:18 +0100856 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
zhoubin508bf20b32017-02-03 09:39:14 +0800857 client.delete_floatingip,
Steve Heyman33735f22016-05-24 09:28:08 -0500858 floating_ip['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300859 return floating_ip
860
861 def _associate_floating_ip(self, floating_ip, server):
Kirill Shileev14113572014-11-21 16:58:02 +0300862 port_id, _ = self._get_server_port_id_and_ip4(server)
Steve Heyman33735f22016-05-24 09:28:08 -0500863 kwargs = dict(port_id=port_id)
864 floating_ip = self.floating_ips_client.update_floatingip(
865 floating_ip['id'], **kwargs)['floatingip']
866 self.assertEqual(port_id, floating_ip['port_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300867 return floating_ip
868
869 def _disassociate_floating_ip(self, floating_ip):
Steve Heyman33735f22016-05-24 09:28:08 -0500870 """:param floating_ip: floating_ips_client.create_floatingip"""
871 kwargs = dict(port_id=None)
872 floating_ip = self.floating_ips_client.update_floatingip(
873 floating_ip['id'], **kwargs)['floatingip']
874 self.assertIsNone(floating_ip['port_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300875 return floating_ip
876
Yair Fried45f92952014-06-26 05:19:19 +0300877 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000878 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300879
Steve Heyman33735f22016-05-24 09:28:08 -0500880 :param dict floating_ip: floating IP dict to check status
Yair Fried45f92952014-06-26 05:19:19 +0300881 :param status: target status
882 :raises: AssertionError if status doesn't match
883 """
Steve Heyman33735f22016-05-24 09:28:08 -0500884 floatingip_id = floating_ip['id']
885
Carl Baldwina754e2d2014-10-23 22:47:41 +0000886 def refresh():
Steve Heyman33735f22016-05-24 09:28:08 -0500887 result = (self.floating_ips_client.
888 show_floatingip(floatingip_id)['floatingip'])
889 return status == result['status']
Carl Baldwina754e2d2014-10-23 22:47:41 +0000890
zhufl4dda94e2017-03-14 16:14:46 +0800891 if not test_utils.call_until_true(refresh,
892 CONF.network.build_timeout,
893 CONF.network.build_interval):
894 floating_ip = self.floating_ips_client.show_floatingip(
895 floatingip_id)['floatingip']
896 self.assertEqual(status, floating_ip['status'],
897 message="FloatingIP: {fp} is at status: {cst}. "
898 "failed to reach status: {st}"
899 .format(fp=floating_ip, cst=floating_ip['status'],
900 st=status))
Yair Fried45f92952014-06-26 05:19:19 +0300901 LOG.info("FloatingIP: {fp} is at status: {st}"
902 .format(fp=floating_ip, st=status))
903
Yair Fried1fc32a12014-08-04 09:11:30 +0300904 def _check_tenant_network_connectivity(self, server,
905 username,
906 private_key,
907 should_connect=True,
908 servers_for_debug=None):
Sean Dagueed6e5862016-04-04 10:49:13 -0400909 if not CONF.network.project_networks_reachable:
Yair Fried1fc32a12014-08-04 09:11:30 +0300910 msg = 'Tenant networks not configured to be reachable.'
911 LOG.info(msg)
912 return
913 # The target login is assumed to have been configured for
914 # key-based authentication by cloud-init.
915 try:
Béla Vancsicsb6dfa082017-03-01 10:44:58 +0100916 for ip_addresses in server['addresses'].values():
Yair Fried1fc32a12014-08-04 09:11:30 +0300917 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900918 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200919 username,
920 private_key,
921 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300922 except Exception as e:
923 LOG.exception('Tenant network connectivity check failed')
924 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000925 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300926 raise
927
Yair Friedbc46f592015-11-18 16:29:34 +0200928 def _check_remote_connectivity(self, source, dest, should_succeed=True,
929 nic=None):
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900930 """assert ping server via source ssh connection
931
932 Note: This is an internal method. Use check_remote_connectivity
933 instead.
Yair Fried1fc32a12014-08-04 09:11:30 +0300934
935 :param source: RemoteClient: an ssh connection from which to ping
936 :param dest: and IP to ping against
937 :param should_succeed: boolean should ping succeed or not
Yair Friedbc46f592015-11-18 16:29:34 +0200938 :param nic: specific network interface to ping from
Yair Fried1fc32a12014-08-04 09:11:30 +0300939 """
940 def ping_remote():
941 try:
Yair Friedbc46f592015-11-18 16:29:34 +0200942 source.ping_host(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +0300943 except lib_exc.SSHExecCommandFailed:
zhangguoqing6c096642016-01-04 06:17:21 +0000944 LOG.warning('Failed to ping IP: %s via a ssh connection '
Jordan Pittier525ec712016-12-07 17:51:26 +0100945 'from: %s.', dest, source.ssh_client.host)
Yair Fried1fc32a12014-08-04 09:11:30 +0300946 return not should_succeed
947 return should_succeed
948
Jordan Pittier35a63752016-08-30 13:09:12 +0200949 return test_utils.call_until_true(ping_remote,
950 CONF.validation.ping_timeout,
951 1)
Yair Fried1fc32a12014-08-04 09:11:30 +0300952
YAMAMOTO Takashi4c3ebb02017-01-25 16:04:30 +0900953 def check_remote_connectivity(self, source, dest, should_succeed=True,
954 nic=None):
955 """assert ping server via source ssh connection
956
957 :param source: RemoteClient: an ssh connection from which to ping
958 :param dest: and IP to ping against
959 :param should_succeed: boolean should ping succeed or not
960 :param nic: specific network interface to ping from
961 """
962 result = self._check_remote_connectivity(source, dest, should_succeed,
963 nic)
964 source_host = source.ssh_client.host
965 if should_succeed:
966 msg = "Timed out waiting for %s to become reachable from %s" \
967 % (dest, source_host)
968 else:
969 msg = "%s is reachable from %s" % (dest, source_host)
970 self.assertTrue(result, msg)
971
John Warren456d9ae2016-01-12 15:36:33 -0500972 def _create_security_group(self, security_group_rules_client=None,
973 tenant_id=None,
John Warrenf9606e92015-12-10 12:12:42 -0500974 namestart='secgroup-smoke',
975 security_groups_client=None):
John Warren456d9ae2016-01-12 15:36:33 -0500976 if security_group_rules_client is None:
977 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -0500978 if security_groups_client is None:
979 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300980 if tenant_id is None:
John Warrenf9606e92015-12-10 12:12:42 -0500981 tenant_id = security_groups_client.tenant_id
982 secgroup = self._create_empty_security_group(
983 namestart=namestart, client=security_groups_client,
984 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300985
986 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -0500987 rules = self._create_loginable_secgroup_rule(
John Warren456d9ae2016-01-12 15:36:33 -0500988 security_group_rules_client=security_group_rules_client,
989 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -0500990 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +0300991 for rule in rules:
Steve Heyman33735f22016-05-24 09:28:08 -0500992 self.assertEqual(tenant_id, rule['tenant_id'])
993 self.assertEqual(secgroup['id'], rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300994 return secgroup
995
Yair Frieddb6c9e92014-08-06 08:53:13 +0300996 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300997 namestart='secgroup-smoke'):
998 """Create a security group without rules.
999
1000 Default rules will be created:
1001 - IPv4 egress to any
1002 - IPv6 egress to any
1003
1004 :param tenant_id: secgroup will be created in this tenant
Steve Heyman33735f22016-05-24 09:28:08 -05001005 :returns: the created security group
Yair Fried1fc32a12014-08-04 09:11:30 +03001006 """
1007 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001008 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001009 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001010 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001011 sg_name = data_utils.rand_name(namestart)
1012 sg_desc = sg_name + " description"
1013 sg_dict = dict(name=sg_name,
1014 description=sg_desc)
1015 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -05001016 result = client.create_security_group(**sg_dict)
Steve Heyman33735f22016-05-24 09:28:08 -05001017
1018 secgroup = result['security_group']
1019 self.assertEqual(secgroup['name'], sg_name)
1020 self.assertEqual(tenant_id, secgroup['tenant_id'])
1021 self.assertEqual(secgroup['description'], sg_desc)
1022
Jordan Pittier9e227c52016-02-09 14:35:18 +01001023 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Steve Heyman33735f22016-05-24 09:28:08 -05001024 client.delete_security_group, secgroup['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001025 return secgroup
1026
John Warren456d9ae2016-01-12 15:36:33 -05001027 def _create_security_group_rule(self, secgroup=None,
1028 sec_group_rules_client=None,
John Warrenf9606e92015-12-10 12:12:42 -05001029 tenant_id=None,
1030 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001031 """Create a rule from a dictionary of rule parameters.
1032
1033 Create a rule in a secgroup. if secgroup not defined will search for
1034 default secgroup in tenant_id.
1035
Steve Heyman33735f22016-05-24 09:28:08 -05001036 :param secgroup: the security group.
Yair Fried1fc32a12014-08-04 09:11:30 +03001037 :param tenant_id: if secgroup not passed -- the tenant in which to
1038 search for default secgroup
1039 :param kwargs: a dictionary containing rule parameters:
1040 for example, to allow incoming ssh:
1041 rule = {
1042 direction: 'ingress'
1043 protocol:'tcp',
1044 port_range_min: 22,
1045 port_range_max: 22
1046 }
1047 """
John Warren456d9ae2016-01-12 15:36:33 -05001048 if sec_group_rules_client is None:
1049 sec_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001050 if security_groups_client is None:
1051 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001052 if not tenant_id:
John Warrenf9606e92015-12-10 12:12:42 -05001053 tenant_id = security_groups_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001054 if secgroup is None:
zhuflb0b272e2017-09-22 16:01:46 +08001055 # Get default secgroup for tenant_id
1056 default_secgroups = security_groups_client.list_security_groups(
1057 name='default', tenant_id=tenant_id)['security_groups']
1058 msg = "No default security group for tenant %s." % (tenant_id)
1059 self.assertNotEmpty(default_secgroups, msg)
1060 secgroup = default_secgroups[0]
Yair Fried1fc32a12014-08-04 09:11:30 +03001061
Steve Heyman33735f22016-05-24 09:28:08 -05001062 ruleset = dict(security_group_id=secgroup['id'],
1063 tenant_id=secgroup['tenant_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001064 ruleset.update(kwargs)
1065
John Warren456d9ae2016-01-12 15:36:33 -05001066 sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
Steve Heyman33735f22016-05-24 09:28:08 -05001067 sg_rule = sg_rule['security_group_rule']
1068
1069 self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
1070 self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001071
1072 return sg_rule
1073
John Warren456d9ae2016-01-12 15:36:33 -05001074 def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
1075 secgroup=None,
John Warrenf9606e92015-12-10 12:12:42 -05001076 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001077 """Create loginable security group rule
1078
Alex Stafeyevdd5dde92016-05-08 14:35:04 +03001079 This function will create:
1080 1. egress and ingress tcp port 22 allow rule in order to allow ssh
1081 access for ipv4.
1082 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
1083 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
Yair Fried1fc32a12014-08-04 09:11:30 +03001084 """
1085
John Warren456d9ae2016-01-12 15:36:33 -05001086 if security_group_rules_client is None:
1087 security_group_rules_client = self.security_group_rules_client
John Warrenf9606e92015-12-10 12:12:42 -05001088 if security_groups_client is None:
1089 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001090 rules = []
1091 rulesets = [
1092 dict(
1093 # ssh
1094 protocol='tcp',
1095 port_range_min=22,
1096 port_range_max=22,
1097 ),
1098 dict(
1099 # ping
1100 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001101 ),
1102 dict(
1103 # ipv6-icmp for ping6
1104 protocol='icmp',
1105 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001106 )
1107 ]
John Warren456d9ae2016-01-12 15:36:33 -05001108 sec_group_rules_client = security_group_rules_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001109 for ruleset in rulesets:
1110 for r_direction in ['ingress', 'egress']:
1111 ruleset['direction'] = r_direction
1112 try:
1113 sg_rule = self._create_security_group_rule(
John Warren456d9ae2016-01-12 15:36:33 -05001114 sec_group_rules_client=sec_group_rules_client,
1115 secgroup=secgroup,
John Warrenf9606e92015-12-10 12:12:42 -05001116 security_groups_client=security_groups_client,
1117 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001118 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001119 # if rule already exist - skip rule and continue
1120 msg = 'Security group rule already exists'
1121 if msg not in ex._error_string:
1122 raise ex
1123 else:
Steve Heyman33735f22016-05-24 09:28:08 -05001124 self.assertEqual(r_direction, sg_rule['direction'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001125 rules.append(sg_rule)
1126
1127 return rules
1128
Yair Frieddb6c9e92014-08-06 08:53:13 +03001129 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001130 """Retrieve a router for the given tenant id.
1131
1132 If a public router has been configured, it will be returned.
1133
1134 If a public router has not been configured, but a public
1135 network has, a tenant router will be created and returned that
1136 routes traffic to the public network.
1137 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001138 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001139 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001140 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001141 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001142 router_id = CONF.network.public_router_id
1143 network_id = CONF.network.public_network_id
1144 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001145 body = client.show_router(router_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001146 return body['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001147 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001148 router = self._create_router(client, tenant_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001149 kwargs = {'external_gateway_info': dict(network_id=network_id)}
1150 router = client.update_router(router['id'], **kwargs)['router']
Yair Fried1fc32a12014-08-04 09:11:30 +03001151 return router
1152 else:
1153 raise Exception("Neither of 'public_router_id' or "
1154 "'public_network_id' has been defined.")
1155
Yair Frieddb6c9e92014-08-06 08:53:13 +03001156 def _create_router(self, client=None, tenant_id=None,
1157 namestart='router-smoke'):
1158 if not client:
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001159 client = self.routers_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001160 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001161 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001162 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -05001163 result = client.create_router(name=name,
1164 admin_state_up=True,
1165 tenant_id=tenant_id)
Steve Heyman33735f22016-05-24 09:28:08 -05001166 router = result['router']
1167 self.assertEqual(router['name'], name)
1168 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1169 client.delete_router,
1170 router['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001171 return router
1172
Alok Maurya6384bbb2014-07-13 06:44:29 -07001173 def _update_router_admin_state(self, router, admin_state_up):
Steve Heyman33735f22016-05-24 09:28:08 -05001174 kwargs = dict(admin_state_up=admin_state_up)
1175 router = self.routers_client.update_router(
1176 router['id'], **kwargs)['router']
1177 self.assertEqual(admin_state_up, router['admin_state_up'])
Alok Maurya6384bbb2014-07-13 06:44:29 -07001178
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001179 def create_networks(self, networks_client=None,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001180 routers_client=None, subnets_client=None,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001181 tenant_id=None, dns_nameservers=None,
1182 port_security_enabled=True):
Yair Fried1fc32a12014-08-04 09:11:30 +03001183 """Create a network with a subnet connected to a router.
1184
David Shrewsbury9bac3662014-08-07 15:07:01 -04001185 The baremetal driver is a special case since all nodes are
1186 on the same shared network.
1187
Yair Fried413bf2d2014-11-19 17:07:11 +02001188 :param tenant_id: id of tenant to create resources in.
1189 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001190 :returns: network, subnet, router
1191 """
Thiago Paiva66cded22016-08-15 14:55:58 -03001192 if CONF.network.shared_physical_network:
David Shrewsbury9bac3662014-08-07 15:07:01 -04001193 # NOTE(Shrews): This exception is for environments where tenant
1194 # credential isolation is available, but network separation is
1195 # not (the current baremetal case). Likely can be removed when
1196 # test account mgmt is reworked:
1197 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001198 if not CONF.compute.fixed_network_name:
1199 m = 'fixed_network_name must be specified in config'
Matthew Treinish4217a702016-10-07 17:27:11 -04001200 raise lib_exc.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001201 network = self._get_network_by_name(
1202 CONF.compute.fixed_network_name)
1203 router = None
1204 subnet = None
1205 else:
John Warren94d8faf2015-09-15 12:22:24 -04001206 network = self._create_network(
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001207 networks_client=networks_client,
Markus Zoeller156b5da2016-07-11 18:10:31 +02001208 tenant_id=tenant_id,
1209 port_security_enabled=port_security_enabled)
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001210 router = self._get_router(client=routers_client,
1211 tenant_id=tenant_id)
Ken'ichi Ohmichi43e7fcf2016-04-04 11:59:13 -07001212 subnet_kwargs = dict(network=network,
Ken'ichi Ohmichie35f4722015-12-22 04:57:11 +00001213 subnets_client=subnets_client,
1214 routers_client=routers_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001215 # use explicit check because empty list is a valid option
1216 if dns_nameservers is not None:
1217 subnet_kwargs['dns_nameservers'] = dns_nameservers
1218 subnet = self._create_subnet(**subnet_kwargs)
Steve Heyman33735f22016-05-24 09:28:08 -05001219 if not routers_client:
1220 routers_client = self.routers_client
1221 router_id = router['id']
1222 routers_client.add_router_interface(router_id,
1223 subnet_id=subnet['id'])
1224
1225 # save a cleanup job to remove this association between
1226 # router and subnet
1227 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
1228 routers_client.remove_router_interface, router_id,
1229 subnet_id=subnet['id'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001230 return network, subnet, router
1231
1232
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001233class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001234 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001235
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001236 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001237
1238 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001239 def setup_clients(cls):
1240 super(EncryptionScenarioTest, cls).setup_clients()
Jordan Pittier8160d312017-04-18 11:52:23 +02001241 cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
ghanshyam3bd0d2b2017-03-23 01:57:28 +00001242 cls.admin_encryption_types_client =\
Jordan Pittier8160d312017-04-18 11:52:23 +02001243 cls.os_admin.encryption_types_v2_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001244
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001245 def create_encryption_type(self, client=None, type_id=None, provider=None,
1246 key_size=None, cipher=None,
1247 control_location=None):
1248 if not client:
Ken'ichi Ohmichia6ebf622016-08-25 11:52:27 -07001249 client = self.admin_encryption_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001250 if not type_id:
1251 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001252 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001253 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001254 client.create_encryption_type(
1255 type_id, provider=provider, key_size=key_size, cipher=cipher,
John Warrend053ded2015-08-13 15:22:48 +00001256 control_location=control_location)['encryption']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001257
lkuchlan3023e752017-06-08 12:53:13 +03001258 def create_encrypted_volume(self, encryption_provider, volume_type,
1259 key_size=256, cipher='aes-xts-plain64',
1260 control_location='front-end'):
1261 volume_type = self.create_volume_type(name=volume_type)
1262 self.create_encryption_type(type_id=volume_type['id'],
1263 provider=encryption_provider,
1264 key_size=key_size,
1265 cipher=cipher,
1266 control_location=control_location)
1267 return self.create_volume(volume_type=volume_type['name'])
1268
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001269
Masayuki Igawa0870db52015-09-18 21:08:36 +09001270class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001271 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001272
1273 Subclasses implement the tests that use the methods provided by this
1274 class.
1275 """
1276
1277 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001278 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001279 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001280 if not CONF.service_available.swift:
1281 skip_msg = ("%s skipped as swift is not available" %
1282 cls.__name__)
1283 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001284
1285 @classmethod
1286 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001287 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001288 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001289 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001290 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001291
1292 @classmethod
1293 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001294 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001295 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001296 cls.account_client = cls.os_operator.account_client
1297 cls.container_client = cls.os_operator.container_client
1298 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001299
Chris Dentde456a12014-09-10 12:41:15 +01001300 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001301 """get swift status for our user account."""
1302 self.account_client.list_account_containers()
1303 LOG.debug('Swift status information obtained successfully')
1304
Chris Dentde456a12014-09-10 12:41:15 +01001305 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001306 name = container_name or data_utils.rand_name(
1307 'swift-scenario-container')
1308 self.container_client.create_container(name)
1309 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001310 self.list_and_check_container_objects(name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001311 LOG.debug('Container %s created', name)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001312 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001313 self.container_client.delete_container,
1314 name)
Chris Dent0d494112014-08-26 13:48:30 +01001315 return name
1316
Chris Dentde456a12014-09-10 12:41:15 +01001317 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001318 self.container_client.delete_container(container_name)
Jordan Pittier525ec712016-12-07 17:51:26 +01001319 LOG.debug('Container %s deleted', container_name)
Chris Dent0d494112014-08-26 13:48:30 +01001320
Chris Dentde456a12014-09-10 12:41:15 +01001321 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001322 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
Jordan Pittierb84f2d42016-12-21 19:02:15 +01001323 obj_data = data_utils.random_bytes()
Chris Dent0d494112014-08-26 13:48:30 +01001324 self.object_client.create_object(container_name, obj_name, obj_data)
Jordan Pittier9e227c52016-02-09 14:35:18 +01001325 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Chris Dent1d4313a2014-10-28 12:16:48 +00001326 self.object_client.delete_object,
1327 container_name,
1328 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001329 return obj_name, obj_data
1330
Chris Dentde456a12014-09-10 12:41:15 +01001331 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001332 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001333 self.list_and_check_container_objects(container_name,
1334 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001335
Chris Dentde456a12014-09-10 12:41:15 +01001336 def list_and_check_container_objects(self, container_name,
1337 present_obj=None,
1338 not_present_obj=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001339 # List objects for a given container and assert which are present and
1340 # which are not.
Ghanshyam2a180b82014-06-16 13:54:22 +09001341 if present_obj is None:
1342 present_obj = []
1343 if not_present_obj is None:
1344 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001345 _, object_list = self.container_client.list_container_contents(
1346 container_name)
1347 if present_obj:
1348 for obj in present_obj:
1349 self.assertIn(obj, object_list)
1350 if not_present_obj:
1351 for obj in not_present_obj:
1352 self.assertNotIn(obj, object_list)
1353
Chris Dentde456a12014-09-10 12:41:15 +01001354 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001355 metadata_param = {'metadata_prefix': 'x-container-',
1356 'metadata': {'read': acl}}
1357 self.container_client.update_container_metadata(container_name,
1358 **metadata_param)
1359 resp, _ = self.container_client.list_container_metadata(container_name)
1360 self.assertEqual(resp['x-container-read'], acl)
1361
Chris Dentde456a12014-09-10 12:41:15 +01001362 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001363 _, obj = self.object_client.get_object(container_name, obj_name)
1364 self.assertEqual(obj, expected_data)