blob: 8a00c65096f35ef06d6ee434ebf8f3f41bfe46e8 [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
Matthew Treinish96e9e882014-06-09 18:37:19 -040022import six
Matt Riedemann5f0ac522015-05-21 09:16:24 -070023from tempest_lib.common.utils import misc as misc_utils
Masayuki Igawad9388762015-01-20 14:56:42 +090024from tempest_lib import exceptions as lib_exc
Sean Dague6dbc6da2013-05-08 17:49:46 -040025
lanoux5fc14522015-09-21 08:17:35 +000026from tempest.common import compute
Fei Long Wangd39431f2015-05-14 11:30:48 +120027from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090028from tempest.common.utils.linux import remote_client
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000029from tempest.common import waiters
Matthew Treinish6c072292014-01-29 19:15:52 +000030from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020031from tempest import exceptions
Yair Fried1fc32a12014-08-04 09:11:30 +030032from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040033import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040034
Matthew Treinish6c072292014-01-29 19:15:52 +000035CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040036
Attila Fazekasfb7552a2013-08-27 13:02:26 +020037LOG = log.getLogger(__name__)
38
Sean Dague6dbc6da2013-05-08 17:49:46 -040039
Andrea Frittoli2e733b52014-07-16 14:12:11 +010040class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010041 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010042
Andrea Frittolib21de6c2015-02-06 20:12:38 +000043 credentials = ['primary']
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +000044
45 @classmethod
46 def setup_clients(cls):
47 super(ScenarioTest, cls).setup_clients()
Andrea Frittoli247058f2014-07-16 16:09:22 +010048 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070049 cls.flavors_client = cls.manager.flavors_client
John Warrene74890a2015-11-11 15:18:01 -050050 cls.compute_floating_ips_client = (
51 cls.manager.compute_floating_ips_client)
Andrea Frittoli247058f2014-07-16 16:09:22 +010052 # Glance image client v1
53 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000054 # Compute image client
55 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010056 cls.keypairs_client = cls.manager.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010057 # Nova security groups client
John Warrenf2345512015-12-10 13:39:30 -050058 cls.compute_security_groups_client = (
59 cls.manager.compute_security_groups_client)
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +000060 cls.security_group_rules_client = (
61 cls.manager.security_group_rules_client)
Andrea Frittoli247058f2014-07-16 16:09:22 +010062 cls.servers_client = cls.manager.servers_client
Yair Fried1fc32a12014-08-04 09:11:30 +030063 cls.interface_client = cls.manager.interfaces_client
64 # Neutron network client
65 cls.network_client = cls.manager.network_client
John Warren94d8faf2015-09-15 12:22:24 -040066 cls.networks_client = cls.manager.networks_client
John Warren49c0fe52015-10-22 12:35:54 -040067 cls.ports_client = cls.manager.ports_client
John Warren3961acd2015-10-02 14:38:53 -040068 cls.subnets_client = cls.manager.subnets_client
John Warrenfbf2a892015-11-17 12:36:14 -050069 cls.floating_ips_client = cls.manager.floating_ips_client
John Warrenf9606e92015-12-10 12:12:42 -050070 cls.security_groups_client = cls.manager.security_groups_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090071 # Heat client
72 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010073
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +030074 if CONF.volume_feature_enabled.api_v1:
75 cls.volumes_client = cls.manager.volumes_client
76 cls.snapshots_client = cls.manager.snapshots_client
77 else:
78 cls.volumes_client = cls.manager.volumes_v2_client
79 cls.snapshots_client = cls.manager.snapshots_v2_client
80
Andrea Frittoli247058f2014-07-16 16:09:22 +010081 # ## Methods to handle sync and async deletes
82
83 def setUp(self):
84 super(ScenarioTest, self).setUp()
85 self.cleanup_waits = []
86 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
87 # because scenario tests in the same test class should not share
88 # resources. If resources were shared between test cases then it
89 # should be a single scenario test instead of multiples.
90
91 # NOTE(yfried): this list is cleaned at the end of test_methods and
92 # not at the end of the class
93 self.addCleanup(self._wait_for_cleanups)
94
Yair Fried1fc32a12014-08-04 09:11:30 +030095 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +010096 """Ignores NotFound exceptions for delete operations.
97
Yair Fried1fc32a12014-08-04 09:11:30 +030098 @param delete_thing: delete method of a resource. method will be
99 executed as delete_thing(*args, **kwargs)
100
Andrea Frittoli247058f2014-07-16 16:09:22 +0100101 """
102 try:
103 # Tempest clients return dicts, so there is no common delete
104 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300105 delete_thing(*args, **kwargs)
Masayuki Igawabfa07602015-01-20 18:47:17 +0900106 except lib_exc.NotFound:
Andrea Frittoli247058f2014-07-16 16:09:22 +0100107 # If the resource is already missing, mission accomplished.
108 pass
109
110 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900111 cleanup_callable, cleanup_args=None,
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000112 cleanup_kwargs=None, waiter_client=None):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700113 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100114
115 @param waiter_callable: callable to wait for the resource to delete
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000116 with the following waiter_client if specified.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100117 @param thing_id: the id of the resource to be cleaned-up
118 @param thing_id_param: the name of the id param in the waiter
119 @param cleanup_callable: method to load pass to self.addCleanup with
120 the following *cleanup_args, **cleanup_kwargs.
121 usually a delete method.
122 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900123 if cleanup_args is None:
124 cleanup_args = []
125 if cleanup_kwargs is None:
126 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100127 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
128 wait_dict = {
129 'waiter_callable': waiter_callable,
130 thing_id_param: thing_id
131 }
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000132 if waiter_client:
133 wait_dict['client'] = waiter_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100134 self.cleanup_waits.append(wait_dict)
135
136 def _wait_for_cleanups(self):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000137 # To handle async delete actions, a list of waits is added
138 # which will be iterated over as the last step of clearing the
139 # cleanup queue. That way all the delete calls are made up front
140 # and the tests won't succeed unless the deletes are eventually
141 # successful. This is the same basic approach used in the api tests to
142 # limit cleanup execution time except here it is multi-resource,
143 # because of the nature of the scenario tests.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100144 for wait in self.cleanup_waits:
145 waiter_callable = wait.pop('waiter_callable')
146 waiter_callable(**wait)
147
148 # ## Test functions library
149 #
150 # The create_[resource] functions only return body and discard the
151 # resp part which is not used in scenario tests
152
Yair Frieddb6c9e92014-08-06 08:53:13 +0300153 def create_keypair(self, client=None):
154 if not client:
155 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100156 name = data_utils.rand_name(self.__class__.__name__)
157 # We don't need to create a keypair by pubkey in scenario
Ken'ichi Ohmichie364bce2015-07-17 10:27:59 +0000158 body = client.create_keypair(name=name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300159 self.addCleanup(client.delete_keypair, name)
ghanshyamdee01f22015-08-17 11:41:47 +0900160 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100161
162 def create_server(self, name=None, image=None, flavor=None,
lanoux5fc14522015-09-21 08:17:35 +0000163 validatable=False, wait_until=None,
164 wait_on_delete=True, clients=None, **kwargs):
165 """Wrapper utility that returns a test server.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100166
lanoux5fc14522015-09-21 08:17:35 +0000167 This wrapper utility calls the common create test server and
168 returns a test server. The purpose of this wrapper is to minimize
169 the impact on the code of the tests already using this
170 function.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100171 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100172
lanoux5fc14522015-09-21 08:17:35 +0000173 # NOTE(jlanoux): As a first step, ssh checks in the scenario
174 # tests need to be run regardless of the run_validation and
175 # validatable parameters and thus until the ssh validation job
176 # becomes voting in CI. The test resources management and IP
177 # association are taken care of in the scenario tests.
178 # Therefore, the validatable parameter is set to false in all
179 # those tests. In this way create_server just return a standard
180 # server and the scenario tests always perform ssh checks.
181
182 # Needed for the cross_tenant_traffic test:
183 if clients is None:
184 clients = self.manager
185
186 vnic_type = CONF.network.port_vnic_type
187
188 # If vnic_type is configured create port for
189 # every network
190 if vnic_type:
191 ports = []
192 networks = []
193 create_port_body = {'binding:vnic_type': vnic_type,
194 'namestart': 'port-smoke'}
195 if kwargs:
196 # Convert security group names to security group ids
197 # to pass to create_port
198 if 'security_groups' in kwargs:
199 security_groups =\
John Warrenf9606e92015-12-10 12:12:42 -0500200 clients.security_groups_client.list_security_groups(
lanoux5fc14522015-09-21 08:17:35 +0000201 ).get('security_groups')
202 sec_dict = dict([(s['name'], s['id'])
203 for s in security_groups])
204
205 sec_groups_names = [s['name'] for s in kwargs.pop(
206 'security_groups')]
207 security_groups_ids = [sec_dict[s]
208 for s in sec_groups_names]
209
210 if security_groups_ids:
211 create_port_body[
212 'security_groups'] = security_groups_ids
213 networks = kwargs.pop('networks')
214
215 # If there are no networks passed to us we look up
216 # for the tenant's private networks and create a port
217 # if there is only one private network. The same behaviour
218 # as we would expect when passing the call to the clients
219 # with no networks
220 if not networks:
221 networks = clients.networks_client.list_networks(
222 filters={'router:external': False})
223 self.assertEqual(1, len(networks),
224 "There is more than one"
225 " network for the tenant")
226 for net in networks:
227 net_id = net['uuid']
228 port = self._create_port(network_id=net_id,
229 client=clients.ports_client,
230 **create_port_body)
231 ports.append({'port': port.id})
232 if ports:
233 kwargs['networks'] = ports
234 self.ports = ports
235
236 tenant_network = self.get_tenant_network()
237
238 body, servers = compute.create_test_server(
239 clients,
240 tenant_network=tenant_network,
241 wait_until=wait_until,
242 **kwargs)
243
244 # TODO(jlanoux) Move wait_on_delete in compute.py
Andrea Frittoli247058f2014-07-16 16:09:22 +0100245 if wait_on_delete:
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000246 self.addCleanup(waiters.wait_for_server_termination,
lanoux5fc14522015-09-21 08:17:35 +0000247 clients.servers_client,
248 body['id'])
249
Andrea Frittoli247058f2014-07-16 16:09:22 +0100250 self.addCleanup_with_wait(
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000251 waiter_callable=waiters.wait_for_server_termination,
lanoux5fc14522015-09-21 08:17:35 +0000252 thing_id=body['id'], thing_id_param='server_id',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100253 cleanup_callable=self.delete_wrapper,
lanoux5fc14522015-09-21 08:17:35 +0000254 cleanup_args=[clients.servers_client.delete_server, body['id']],
255 waiter_client=clients.servers_client)
256 server = clients.servers_client.show_server(body['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100257 return server
258
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100259 def create_volume(self, size=None, name=None, snapshot_id=None,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100260 imageRef=None, volume_type=None, wait_on_delete=True):
261 if name is None:
262 name = data_utils.rand_name(self.__class__.__name__)
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000263 volume = self.volumes_client.create_volume(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100264 size=size, display_name=name, snapshot_id=snapshot_id,
John Warren6177c9e2015-08-19 20:00:17 +0000265 imageRef=imageRef, volume_type=volume_type)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700266
Andrea Frittoli247058f2014-07-16 16:09:22 +0100267 if wait_on_delete:
268 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
269 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700270 self.addCleanup(self.delete_wrapper,
271 self.volumes_client.delete_volume, volume['id'])
272 else:
273 self.addCleanup_with_wait(
274 waiter_callable=self.volumes_client.wait_for_resource_deletion,
275 thing_id=volume['id'], thing_id_param='id',
276 cleanup_callable=self.delete_wrapper,
277 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100278
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300279 # NOTE(e0ne): Cinder API v2 uses name instead of display_name
280 if 'display_name' in volume:
281 self.assertEqual(name, volume['display_name'])
282 else:
283 self.assertEqual(name, volume['name'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100284 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
285 # The volume retrieved on creation has a non-up-to-date status.
286 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000287 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100288 return volume
289
Yair Fried1fc32a12014-08-04 09:11:30 +0300290 def _create_loginable_secgroup_rule(self, secgroup_id=None):
John Warrenf2345512015-12-10 13:39:30 -0500291 _client = self.compute_security_groups_client
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000292 _client_rules = self.security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100293 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900294 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100295 for sg in sgs:
296 if sg['name'] == 'default':
297 secgroup_id = sg['id']
298
299 # These rules are intended to permit inbound ssh and icmp
300 # traffic from all sources, so no group_id is provided.
301 # Setting a group_id would only permit traffic from ports
302 # belonging to the same security group.
303 rulesets = [
304 {
305 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000306 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100307 'from_port': 22,
308 'to_port': 22,
309 'cidr': '0.0.0.0/0',
310 },
311 {
312 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000313 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100314 'from_port': -1,
315 'to_port': -1,
316 'cidr': '0.0.0.0/0',
317 }
318 ]
319 rules = list()
320 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000321 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900322 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100323 self.addCleanup(self.delete_wrapper,
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000324 _client_rules.delete_security_group_rule,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100325 sg_rule['id'])
326 rules.append(sg_rule)
327 return rules
328
Yair Fried1fc32a12014-08-04 09:11:30 +0300329 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100330 # Create security group
331 sg_name = data_utils.rand_name(self.__class__.__name__)
332 sg_desc = sg_name + " description"
John Warrenf2345512015-12-10 13:39:30 -0500333 secgroup = self.compute_security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900334 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100335 self.assertEqual(secgroup['name'], sg_name)
336 self.assertEqual(secgroup['description'], sg_desc)
John Warrenf2345512015-12-10 13:39:30 -0500337 self.addCleanup(
338 self.delete_wrapper,
339 self.compute_security_groups_client.delete_security_group,
340 secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100341
342 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300343 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100344
345 return secgroup
346
JordanP3fe2dc32014-11-17 13:06:01 +0100347 def get_remote_client(self, server_or_ip, username=None, private_key=None,
348 log_console_of_servers=None):
349 """Get a SSH client to a remote server
350
351 @param server_or_ip a server object as returned by Tempest compute
352 client or an IP address to connect to
353 @param username name of the Linux account on the remote server
354 @param private_key the SSH private key to use
355 @param log_console_of_servers a list of server objects. Each server
356 in the list will have its console printed in the logs in case the
357 SSH connection failed to be established
358 @return a RemoteClient object
359 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100360 if isinstance(server_or_ip, six.string_types):
361 ip = server_or_ip
362 else:
lanoux283273b2015-12-04 03:01:54 -0800363 addrs = server_or_ip['addresses'][CONF.validation.network_for_ssh]
Andrew Boik4a3daf12015-03-27 01:59:31 -0400364 try:
365 ip = (addr['addr'] for addr in addrs if
366 netaddr.valid_ipv4(addr['addr'])).next()
367 except StopIteration:
368 raise lib_exc.NotFound("No IPv4 addresses to use for SSH to "
369 "remote server.")
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700370
Andrea Frittoli247058f2014-07-16 16:09:22 +0100371 if username is None:
lanoux283273b2015-12-04 03:01:54 -0800372 username = CONF.validation.image_ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800373 # Set this with 'keypair' or others to log in with keypair or
374 # username/password.
lanoux5fc14522015-09-21 08:17:35 +0000375 if CONF.validation.auth_method == 'keypair':
wantwatering896300c2015-03-27 15:17:42 +0800376 password = None
377 if private_key is None:
378 private_key = self.keypair['private_key']
379 else:
lanoux283273b2015-12-04 03:01:54 -0800380 password = CONF.validation.image_ssh_password
wantwatering896300c2015-03-27 15:17:42 +0800381 private_key = None
Andrea Frittoli247058f2014-07-16 16:09:22 +0100382 linux_client = remote_client.RemoteClient(ip, username,
wantwatering896300c2015-03-27 15:17:42 +0800383 pkey=private_key,
384 password=password)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100385 try:
386 linux_client.validate_authentication()
Matt Riedemann5f0ac522015-05-21 09:16:24 -0700387 except Exception as e:
388 message = ('Initializing SSH connection to %(ip)s failed. '
389 'Error: %(error)s' % {'ip': ip, 'error': e})
390 caller = misc_utils.find_test_caller()
391 if caller:
392 message = '(%s) %s' % (caller, message)
393 LOG.exception(message)
Marc Kodererb06db502015-04-30 09:31:27 +0200394 # If we don't explicitly set for which servers we want to
JordanP3fe2dc32014-11-17 13:06:01 +0100395 # log the console output then all the servers will be logged.
396 # See the definition of _log_console_output()
397 self._log_console_output(log_console_of_servers)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100398 raise
399
400 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)
407 image_file = open(path, 'rb')
408 self.addCleanup(image_file.close)
409 params = {
410 'name': name,
411 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000412 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100413 'is_public': 'False',
414 }
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000415 params['properties'] = properties
John Warren66207252015-07-31 15:51:02 -0400416 image = self.image_client.create_image(**params)['image']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100417 self.addCleanup(self.image_client.delete_image, image['id'])
418 self.assertEqual("queued", image['status'])
419 self.image_client.update_image(image['id'], data=image_file)
420 return image['id']
421
422 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300423 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100424 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
425 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
426 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300427 img_container_format = CONF.scenario.img_container_format
428 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000429 img_properties = CONF.scenario.img_properties
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300430 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000431 "properties: %s, ami: %s, ari: %s, aki: %s" %
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300432 (img_path, img_container_format, img_disk_format,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000433 img_properties, ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100434 try:
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100435 image = self._image_create('scenario-img',
436 img_container_format,
437 img_path,
438 disk_format=img_disk_format,
439 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100440 except IOError:
441 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
442 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
443 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000444 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Jordan Pittier1e443ec2015-11-20 16:15:58 +0100445 image = self._image_create('scenario-ami', 'ami',
446 path=ami_img_path,
447 properties=properties)
448 LOG.debug("image:%s" % image)
449
450 return image
Andrea Frittoli247058f2014-07-16 16:09:22 +0100451
452 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400453 if not CONF.compute_feature_enabled.console_output:
454 LOG.debug('Console output not supported, cannot log')
455 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100456 if not servers:
David Kranzae99b9a2015-02-16 13:37:01 -0500457 servers = self.servers_client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100458 servers = servers['servers']
459 for server in servers:
Brant Knudson566c5712014-09-24 20:04:50 -0500460 console_output = self.servers_client.get_console_output(
Ken'ichi Ohmichibf4766a2015-12-09 07:48:43 +0000461 server['id'])['output']
David Kranzae99b9a2015-02-16 13:37:01 -0500462 LOG.debug('Console output for %s\nbody=\n%s',
463 server['id'], console_output)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100464
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000465 def _log_net_info(self, exc):
466 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300467 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000468 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000469
nithya-ganesan882595e2014-07-29 18:51:07 +0000470 def create_server_snapshot(self, server, name=None):
471 # Glance client
472 _image_client = self.image_client
473 # Compute client
474 _images_client = self.images_client
475 if name is None:
Ken'ichi Ohmichi6ded8df2015-03-23 02:00:19 +0000476 name = data_utils.rand_name('scenario-snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000477 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000478 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500479 image_id = image.response['location'].split('images/')[1]
nithya-ganesan882595e2014-07-29 18:51:07 +0000480 _image_client.wait_for_image_status(image_id, 'active')
481 self.addCleanup_with_wait(
482 waiter_callable=_image_client.wait_for_resource_deletion,
483 thing_id=image_id, thing_id_param='id',
484 cleanup_callable=self.delete_wrapper,
485 cleanup_args=[_image_client.delete_image, image_id])
David Kranz34f18782015-01-06 13:43:55 -0500486 snapshot_image = _image_client.get_image_meta(image_id)
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300487
488 bdm = snapshot_image.get('properties', {}).get('block_device_mapping')
489 if bdm:
490 bdm = json.loads(bdm)
491 if bdm and 'snapshot_id' in bdm[0]:
492 snapshot_id = bdm[0]['snapshot_id']
493 self.addCleanup(
494 self.snapshots_client.wait_for_resource_deletion,
495 snapshot_id)
496 self.addCleanup(
497 self.delete_wrapper, self.snapshots_client.delete_snapshot,
498 snapshot_id)
Andrey Pavlovd35957b2015-08-27 20:12:15 +0300499 self.snapshots_client.wait_for_snapshot_status(snapshot_id,
500 'available')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300501
nithya-ganesan882595e2014-07-29 18:51:07 +0000502 image_name = snapshot_image['name']
503 self.assertEqual(name, image_name)
504 LOG.debug("Created snapshot image %s for server %s",
505 image_name, server['name'])
506 return snapshot_image
507
Jordan Pittier7cf64762015-10-14 15:01:12 +0200508 def nova_volume_attach(self, server, volume_to_attach):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000509 volume = self.servers_client.attach_volume(
Jordan Pittier7cf64762015-10-14 15:01:12 +0200510 server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900511 % CONF.compute.volume_device_name)['volumeAttachment']
Jordan Pittier7cf64762015-10-14 15:01:12 +0200512 self.assertEqual(volume_to_attach['id'], volume['id'])
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900513 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900514
Jordan Pittier7cf64762015-10-14 15:01:12 +0200515 # Return the updated volume after the attachment
516 return self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900517
Jordan Pittier7cf64762015-10-14 15:01:12 +0200518 def nova_volume_detach(self, server, volume):
519 self.servers_client.detach_volume(server['id'], volume['id'])
520 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
521
522 volume = self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900523 self.assertEqual('available', volume['status'])
524
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700525 def rebuild_server(self, server_id, image=None,
526 preserve_ephemeral=False, wait=True,
527 rebuild_kwargs=None):
528 if image is None:
529 image = CONF.compute.image_ref
530
531 rebuild_kwargs = rebuild_kwargs or {}
532
533 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
534 server_id, image, preserve_ephemeral)
Ken'ichi Ohmichi5271b0f2015-08-10 07:53:27 +0000535 self.servers_client.rebuild_server(
536 server_id=server_id, image_ref=image,
537 preserve_ephemeral=preserve_ephemeral,
538 **rebuild_kwargs)
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700539 if wait:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000540 waiters.wait_for_server_status(self.servers_client,
541 server_id, 'ACTIVE')
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700542
Steven Hardyda2a8352014-10-02 12:52:20 +0100543 def ping_ip_address(self, ip_address, should_succeed=True,
544 ping_timeout=None):
lanoux5fc14522015-09-21 08:17:35 +0000545 timeout = ping_timeout or CONF.validation.ping_timeout
Aaron Rosena7df13b2014-09-23 09:45:45 -0700546 cmd = ['ping', '-c1', '-w1', ip_address]
547
548 def ping():
549 proc = subprocess.Popen(cmd,
550 stdout=subprocess.PIPE,
551 stderr=subprocess.PIPE)
552 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000553
Aaron Rosena7df13b2014-09-23 09:45:45 -0700554 return (proc.returncode == 0) == should_succeed
555
Shuquan Huang753629e2015-07-20 08:52:29 +0000556 caller = misc_utils.find_test_caller()
557 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
558 ' expected result is %(should_succeed)s' % {
559 'caller': caller, 'ip': ip_address, 'timeout': timeout,
560 'should_succeed':
561 'reachable' if should_succeed else 'unreachable'
562 })
563 result = tempest.test.call_until_true(ping, timeout, 1)
564 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
565 'ping result is %(result)s' % {
566 'caller': caller, 'ip': ip_address, 'timeout': timeout,
567 'result': 'expected' if result else 'unexpected'
568 })
569 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700570
Yair Friedae0e73d2014-11-24 11:56:26 +0200571 def check_vm_connectivity(self, ip_address,
572 username=None,
573 private_key=None,
574 should_connect=True):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000575 """Check server connectivity
576
Yair Friedae0e73d2014-11-24 11:56:26 +0200577 :param ip_address: server to test against
578 :param username: server's ssh username
579 :param private_key: server's ssh private key to be used
580 :param should_connect: True/False indicates positive/negative test
581 positive - attempt ping and ssh
582 negative - attempt ping and fail if succeed
583
584 :raises: AssertError if the result of the connectivity check does
585 not match the value of the should_connect param
586 """
587 if should_connect:
588 msg = "Timed out waiting for %s to become reachable" % ip_address
589 else:
590 msg = "ip address %s is reachable" % ip_address
591 self.assertTrue(self.ping_ip_address(ip_address,
592 should_succeed=should_connect),
593 msg=msg)
594 if should_connect:
595 # no need to check ssh for negative connectivity
596 self.get_remote_client(ip_address, username, private_key)
597
598 def check_public_network_connectivity(self, ip_address, username,
599 private_key, should_connect=True,
600 msg=None, servers=None):
601 # The target login is assumed to have been configured for
602 # key-based authentication by cloud-init.
603 LOG.debug('checking network connections to IP %s with user: %s' %
604 (ip_address, username))
605 try:
606 self.check_vm_connectivity(ip_address,
607 username,
608 private_key,
609 should_connect=should_connect)
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
John Warrene74890a2015-11-11 15:18:01 -0500621 floating_ip = (self.compute_floating_ips_client.
Ken'ichi Ohmichie037a6f2015-12-03 06:41:49 +0000622 create_floating_ip(pool=pool_name)['floating_ip'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200623 self.addCleanup(self.delete_wrapper,
John Warrene74890a2015-11-11 15:18:01 -0500624 self.compute_floating_ips_client.delete_floating_ip,
Yair Friedae0e73d2014-11-24 11:56:26 +0200625 floating_ip['id'])
John Warrene74890a2015-11-11 15:18:01 -0500626 self.compute_floating_ips_client.associate_floating_ip_to_server(
Yair Friedae0e73d2014-11-24 11:56:26 +0200627 floating_ip['ip'], thing['id'])
628 return floating_ip
629
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700630 def create_timestamp(self, server_or_ip, dev_name=None, mount_path='/mnt',
631 private_key=None):
632 ssh_client = self.get_remote_client(server_or_ip,
633 private_key=private_key)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300634 if dev_name is not None:
635 ssh_client.make_fs(dev_name)
Matt Riedemann076685a2015-09-30 14:38:16 -0700636 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300637 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
638 ssh_client.exec_command(cmd_timestamp)
639 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
640 % mount_path)
641 if dev_name is not None:
642 ssh_client.umount(mount_path)
643 return timestamp
644
Matt Riedemannfd5657d2015-09-30 14:47:07 -0700645 def get_timestamp(self, server_or_ip, dev_name=None, mount_path='/mnt',
646 private_key=None):
647 ssh_client = self.get_remote_client(server_or_ip,
648 private_key=private_key)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300649 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700650 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300651 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
652 % mount_path)
653 if dev_name is not None:
654 ssh_client.umount(mount_path)
655 return timestamp
656
Alexander Gubanovc8829f82015-11-12 10:35:13 +0200657 def get_server_or_ip(self, server):
658 if CONF.validation.connect_method == 'floating':
659 ip = self.create_floating_ip(server)['ip']
660 else:
661 ip = server
662 return ip
663
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100664
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100665class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300666 """Base class for network scenario tests.
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000667
Yair Fried1fc32a12014-08-04 09:11:30 +0300668 This class provide helpers for network scenario tests, using the neutron
669 API. Helpers from ancestor which use the nova network API are overridden
670 with the neutron API.
671
672 This Class also enforces using Neutron instead of novanetwork.
673 Subclassed tests will be skipped if Neutron is not enabled
674
675 """
676
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000677 credentials = ['primary', 'admin']
678
Yair Fried1fc32a12014-08-04 09:11:30 +0300679 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000680 def skip_checks(cls):
681 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100682 if not CONF.service_available.neutron:
683 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300684
685 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100686 def resource_setup(cls):
687 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300688 cls.tenant_id = cls.manager.identity_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300689
John Warren94d8faf2015-09-15 12:22:24 -0400690 def _create_network(self, client=None, networks_client=None,
691 tenant_id=None, namestart='network-smoke-'):
Yair Frieddb6c9e92014-08-06 08:53:13 +0300692 if not client:
693 client = self.network_client
John Warren94d8faf2015-09-15 12:22:24 -0400694 if not networks_client:
695 networks_client = self.networks_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300696 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000697 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300698 name = data_utils.rand_name(namestart)
John Warren94d8faf2015-09-15 12:22:24 -0400699 result = networks_client.create_network(name=name, tenant_id=tenant_id)
700 network = net_resources.DeletableNetwork(
701 networks_client=networks_client, **result['network'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300702 self.assertEqual(network.name, name)
703 self.addCleanup(self.delete_wrapper, network.delete)
704 return network
705
706 def _list_networks(self, *args, **kwargs):
707 """List networks using admin creds """
John Warren94d8faf2015-09-15 12:22:24 -0400708 networks_list = self.admin_manager.networks_client.list_networks(
ghanshyam2f7cc022015-06-26 18:18:11 +0900709 *args, **kwargs)
710 return networks_list['networks']
Yair Fried1fc32a12014-08-04 09:11:30 +0300711
712 def _list_subnets(self, *args, **kwargs):
713 """List subnets using admin creds """
John Warren3961acd2015-10-02 14:38:53 -0400714 subnets_list = self.admin_manager.subnets_client.list_subnets(
ghanshyam2f7cc022015-06-26 18:18:11 +0900715 *args, **kwargs)
716 return subnets_list['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300717
718 def _list_routers(self, *args, **kwargs):
719 """List routers using admin creds """
ghanshyam2f7cc022015-06-26 18:18:11 +0900720 routers_list = self.admin_manager.network_client.list_routers(
721 *args, **kwargs)
722 return routers_list['routers']
Yair Fried1fc32a12014-08-04 09:11:30 +0300723
724 def _list_ports(self, *args, **kwargs):
725 """List ports using admin creds """
John Warren49c0fe52015-10-22 12:35:54 -0400726 ports_list = self.admin_manager.ports_client.list_ports(
ghanshyam2f7cc022015-06-26 18:18:11 +0900727 *args, **kwargs)
728 return ports_list['ports']
Yair Fried1fc32a12014-08-04 09:11:30 +0300729
Yair Fried564d89d2015-08-06 17:02:12 +0300730 def _list_agents(self, *args, **kwargs):
731 """List agents using admin creds """
732 agents_list = self.admin_manager.network_client.list_agents(
733 *args, **kwargs)
734 return agents_list['agents']
735
John Warren3961acd2015-10-02 14:38:53 -0400736 def _create_subnet(self, network, client=None, subnets_client=None,
737 namestart='subnet-smoke', **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 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300742 if not client:
743 client = self.network_client
John Warren3961acd2015-10-02 14:38:53 -0400744 if not subnets_client:
745 subnets_client = self.subnets_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 """
753 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
754 return len(cidr_in_use) != 0
755
Kirill Shileev14113572014-11-21 16:58:02 +0300756 ip_version = kwargs.pop('ip_version', 4)
757
758 if ip_version == 6:
759 tenant_cidr = netaddr.IPNetwork(
760 CONF.network.tenant_network_v6_cidr)
761 num_bits = CONF.network.tenant_network_v6_mask_bits
762 else:
763 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
764 num_bits = CONF.network.tenant_network_mask_bits
765
Yair Fried1fc32a12014-08-04 09:11:30 +0300766 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300767 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300768 # Repeatedly attempt subnet creation with sequential cidr
769 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300770 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300771 str_cidr = str(subnet_cidr)
772 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
773 continue
774
775 subnet = dict(
776 name=data_utils.rand_name(namestart),
Yair Fried1fc32a12014-08-04 09:11:30 +0300777 network_id=network.id,
778 tenant_id=network.tenant_id,
779 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300780 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300781 **kwargs
782 )
783 try:
John Warren3961acd2015-10-02 14:38:53 -0400784 result = subnets_client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300785 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900786 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300787 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
788 if not is_overlapping_cidr:
789 raise
790 self.assertIsNotNone(result, 'Unable to allocate tenant network')
John Warren3961acd2015-10-02 14:38:53 -0400791 subnet = net_resources.DeletableSubnet(
792 network_client=client, subnets_client=subnets_client,
793 **result['subnet'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300794 self.assertEqual(subnet.cidr, str_cidr)
795 self.addCleanup(self.delete_wrapper, subnet.delete)
796 return subnet
797
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200798 def _create_port(self, network_id, client=None, namestart='port-quotatest',
799 **kwargs):
Yair Frieddb6c9e92014-08-06 08:53:13 +0300800 if not client:
John Warren49c0fe52015-10-22 12:35:54 -0400801 client = self.ports_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300802 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500803 result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300804 name=name,
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200805 network_id=network_id,
806 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300807 self.assertIsNotNone(result, 'Unable to allocate port')
John Warren49c0fe52015-10-22 12:35:54 -0400808 port = net_resources.DeletablePort(ports_client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300809 **result['port'])
810 self.addCleanup(self.delete_wrapper, port.delete)
811 return port
812
Kirill Shileev14113572014-11-21 16:58:02 +0300813 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Ken'ichi Ohmichi30b769e2015-10-14 09:47:22 +0000814 ports = self._list_ports(device_id=server['id'], status='ACTIVE',
Yair Fried1fc32a12014-08-04 09:11:30 +0300815 fixed_ip=ip_addr)
Sean M. Collins2e896832015-12-15 13:58:47 -0500816 # A port can have more then one IP address in some cases.
817 # If the network is dual-stack (IPv4 + IPv6), this port is associated
818 # with 2 subnets
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200819 port_map = [(p["id"], fxip["ip_address"])
820 for p in ports
821 for fxip in p["fixed_ips"]
822 if netaddr.valid_ipv4(fxip["ip_address"])]
823
John L. Villalovosb83286f2015-11-04 14:46:57 -0800824 self.assertNotEqual(0, len(port_map),
825 "No IPv4 addresses found in: %s" % ports)
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200826 self.assertEqual(len(port_map), 1,
827 "Found multiple IPv4 addresses: %s. "
828 "Unable to determine which port to target."
829 % port_map)
830 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300831
David Shrewsbury9bac3662014-08-07 15:07:01 -0400832 def _get_network_by_name(self, network_name):
833 net = self._list_networks(name=network_name)
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700834 self.assertNotEqual(len(net), 0,
835 "Unable to get network by name: %s" % network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300836 return net_resources.AttributeDict(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 )
855 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300856 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300857 **result['floatingip'])
858 self.addCleanup(self.delete_wrapper, floating_ip.delete)
859 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)
Yair Fried1fc32a12014-08-04 09:11:30 +0300863 floating_ip.update(port_id=port_id)
864 self.assertEqual(port_id, floating_ip.port_id)
865 return floating_ip
866
867 def _disassociate_floating_ip(self, floating_ip):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000868 """:param floating_ip: type DeletableFloatingIp"""
Yair Fried1fc32a12014-08-04 09:11:30 +0300869 floating_ip.update(port_id=None)
870 self.assertIsNone(floating_ip.port_id)
871 return floating_ip
872
Yair Fried45f92952014-06-26 05:19:19 +0300873 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000874 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300875
876 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
877 to check status
878 :param status: target status
879 :raises: AssertionError if status doesn't match
880 """
Carl Baldwina754e2d2014-10-23 22:47:41 +0000881 def refresh():
882 floating_ip.refresh()
883 return status == floating_ip.status
884
885 tempest.test.call_until_true(refresh,
886 CONF.network.build_timeout,
887 CONF.network.build_interval)
Yair Fried45f92952014-06-26 05:19:19 +0300888 self.assertEqual(status, floating_ip.status,
889 message="FloatingIP: {fp} is at status: {cst}. "
890 "failed to reach status: {st}"
891 .format(fp=floating_ip, cst=floating_ip.status,
892 st=status))
893 LOG.info("FloatingIP: {fp} is at status: {st}"
894 .format(fp=floating_ip, st=status))
895
Yair Fried1fc32a12014-08-04 09:11:30 +0300896 def _check_tenant_network_connectivity(self, server,
897 username,
898 private_key,
899 should_connect=True,
900 servers_for_debug=None):
901 if not CONF.network.tenant_networks_reachable:
902 msg = 'Tenant networks not configured to be reachable.'
903 LOG.info(msg)
904 return
905 # The target login is assumed to have been configured for
906 # key-based authentication by cloud-init.
907 try:
Matthew Treinish71426682015-04-23 11:19:38 -0400908 for net_name, ip_addresses in six.iteritems(server['addresses']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300909 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900910 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200911 username,
912 private_key,
913 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300914 except Exception as e:
915 LOG.exception('Tenant network connectivity check failed')
916 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000917 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300918 raise
919
Yair Friedbc46f592015-11-18 16:29:34 +0200920 def _check_remote_connectivity(self, source, dest, should_succeed=True,
921 nic=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +0000922 """check ping server via source ssh connection
Yair Fried1fc32a12014-08-04 09:11:30 +0300923
924 :param source: RemoteClient: an ssh connection from which to ping
925 :param dest: and IP to ping against
926 :param should_succeed: boolean should ping succeed or not
Yair Friedbc46f592015-11-18 16:29:34 +0200927 :param nic: specific network interface to ping from
Yair Fried1fc32a12014-08-04 09:11:30 +0300928 :returns: boolean -- should_succeed == ping
929 :returns: ping is false if ping failed
930 """
931 def ping_remote():
932 try:
Yair Friedbc46f592015-11-18 16:29:34 +0200933 source.ping_host(dest, nic=nic)
Andrey Pavlov64723762015-04-29 06:24:58 +0300934 except lib_exc.SSHExecCommandFailed:
Yair Fried1fc32a12014-08-04 09:11:30 +0300935 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
936 % (dest, source.ssh_client.host))
937 return not should_succeed
938 return should_succeed
939
940 return tempest.test.call_until_true(ping_remote,
lanoux5fc14522015-09-21 08:17:35 +0000941 CONF.validation.ping_timeout,
Yair Fried1fc32a12014-08-04 09:11:30 +0300942 1)
943
Yair Frieddb6c9e92014-08-06 08:53:13 +0300944 def _create_security_group(self, client=None, tenant_id=None,
John Warrenf9606e92015-12-10 12:12:42 -0500945 namestart='secgroup-smoke',
946 security_groups_client=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300947 if client is None:
948 client = self.network_client
John Warrenf9606e92015-12-10 12:12:42 -0500949 if security_groups_client is None:
950 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300951 if tenant_id is None:
John Warrenf9606e92015-12-10 12:12:42 -0500952 tenant_id = security_groups_client.tenant_id
953 secgroup = self._create_empty_security_group(
954 namestart=namestart, client=security_groups_client,
955 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300956
957 # Add rules to the security group
John Warrenf9606e92015-12-10 12:12:42 -0500958 rules = self._create_loginable_secgroup_rule(
959 client=client, secgroup=secgroup,
960 security_groups_client=security_groups_client)
Yair Fried1fc32a12014-08-04 09:11:30 +0300961 for rule in rules:
962 self.assertEqual(tenant_id, rule.tenant_id)
963 self.assertEqual(secgroup.id, rule.security_group_id)
964 return secgroup
965
Yair Frieddb6c9e92014-08-06 08:53:13 +0300966 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300967 namestart='secgroup-smoke'):
968 """Create a security group without rules.
969
970 Default rules will be created:
971 - IPv4 egress to any
972 - IPv6 egress to any
973
974 :param tenant_id: secgroup will be created in this tenant
975 :returns: DeletableSecurityGroup -- containing the secgroup created
976 """
977 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -0500978 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300979 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000980 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300981 sg_name = data_utils.rand_name(namestart)
982 sg_desc = sg_name + " description"
983 sg_dict = dict(name=sg_name,
984 description=sg_desc)
985 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -0500986 result = client.create_security_group(**sg_dict)
Yair Fried1fc32a12014-08-04 09:11:30 +0300987 secgroup = net_resources.DeletableSecurityGroup(
988 client=client,
989 **result['security_group']
990 )
991 self.assertEqual(secgroup.name, sg_name)
992 self.assertEqual(tenant_id, secgroup.tenant_id)
993 self.assertEqual(secgroup.description, sg_desc)
994 self.addCleanup(self.delete_wrapper, secgroup.delete)
995 return secgroup
996
Yair Frieddb6c9e92014-08-06 08:53:13 +0300997 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300998 """Get default secgroup for given tenant_id.
999
1000 :returns: DeletableSecurityGroup -- default secgroup for given tenant
1001 """
1002 if client is None:
John Warrenf9606e92015-12-10 12:12:42 -05001003 client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001004 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001005 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001006 sgs = [
1007 sg for sg in client.list_security_groups().values()[0]
1008 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
1009 ]
1010 msg = "No default security group for tenant %s." % (tenant_id)
1011 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +03001012 return net_resources.DeletableSecurityGroup(client=client,
1013 **sgs[0])
1014
Yair Frieddb6c9e92014-08-06 08:53:13 +03001015 def _create_security_group_rule(self, secgroup=None, client=None,
John Warrenf9606e92015-12-10 12:12:42 -05001016 tenant_id=None,
1017 security_groups_client=None, **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +03001018 """Create a rule from a dictionary of rule parameters.
1019
1020 Create a rule in a secgroup. if secgroup not defined will search for
1021 default secgroup in tenant_id.
1022
1023 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +03001024 :param tenant_id: if secgroup not passed -- the tenant in which to
1025 search for default secgroup
1026 :param kwargs: a dictionary containing rule parameters:
1027 for example, to allow incoming ssh:
1028 rule = {
1029 direction: 'ingress'
1030 protocol:'tcp',
1031 port_range_min: 22,
1032 port_range_max: 22
1033 }
1034 """
1035 if client is None:
1036 client = self.network_client
John Warrenf9606e92015-12-10 12:12:42 -05001037 if security_groups_client is None:
1038 security_groups_client = self.security_groups_client
Yair Frieddb6c9e92014-08-06 08:53:13 +03001039 if not tenant_id:
John Warrenf9606e92015-12-10 12:12:42 -05001040 tenant_id = security_groups_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001041 if secgroup is None:
John Warrenf9606e92015-12-10 12:12:42 -05001042 secgroup = self._default_security_group(
1043 client=security_groups_client, tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001044
1045 ruleset = dict(security_group_id=secgroup.id,
1046 tenant_id=secgroup.tenant_id)
1047 ruleset.update(kwargs)
1048
David Kranz34e88122014-12-11 15:24:05 -05001049 sg_rule = client.create_security_group_rule(**ruleset)
Yair Fried1fc32a12014-08-04 09:11:30 +03001050 sg_rule = net_resources.DeletableSecurityGroupRule(
1051 client=client,
1052 **sg_rule['security_group_rule']
1053 )
1054 self.addCleanup(self.delete_wrapper, sg_rule.delete)
1055 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
1056 self.assertEqual(secgroup.id, sg_rule.security_group_id)
1057
1058 return sg_rule
1059
John Warrenf9606e92015-12-10 12:12:42 -05001060 def _create_loginable_secgroup_rule(self, client=None, secgroup=None,
1061 security_groups_client=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001062 """Create loginable security group rule
1063
1064 These rules are intended to permit inbound ssh and icmp
Yair Fried1fc32a12014-08-04 09:11:30 +03001065 traffic from all sources, so no group_id is provided.
1066 Setting a group_id would only permit traffic from ports
1067 belonging to the same security group.
1068 """
1069
1070 if client is None:
1071 client = self.network_client
John Warrenf9606e92015-12-10 12:12:42 -05001072 if security_groups_client is None:
1073 security_groups_client = self.security_groups_client
Yair Fried1fc32a12014-08-04 09:11:30 +03001074 rules = []
1075 rulesets = [
1076 dict(
1077 # ssh
1078 protocol='tcp',
1079 port_range_min=22,
1080 port_range_max=22,
1081 ),
1082 dict(
1083 # ping
1084 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001085 ),
1086 dict(
1087 # ipv6-icmp for ping6
1088 protocol='icmp',
1089 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001090 )
1091 ]
1092 for ruleset in rulesets:
1093 for r_direction in ['ingress', 'egress']:
1094 ruleset['direction'] = r_direction
1095 try:
1096 sg_rule = self._create_security_group_rule(
John Warrenf9606e92015-12-10 12:12:42 -05001097 client=client, secgroup=secgroup,
1098 security_groups_client=security_groups_client,
1099 **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001100 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001101 # if rule already exist - skip rule and continue
1102 msg = 'Security group rule already exists'
1103 if msg not in ex._error_string:
1104 raise ex
1105 else:
1106 self.assertEqual(r_direction, sg_rule.direction)
1107 rules.append(sg_rule)
1108
1109 return rules
1110
1111 def _ssh_to_server(self, server, private_key):
lanoux283273b2015-12-04 03:01:54 -08001112 ssh_login = CONF.validation.image_ssh_user
Yair Fried1fc32a12014-08-04 09:11:30 +03001113 return self.get_remote_client(server,
1114 username=ssh_login,
1115 private_key=private_key)
1116
Yair Frieddb6c9e92014-08-06 08:53:13 +03001117 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001118 """Retrieve a router for the given tenant id.
1119
1120 If a public router has been configured, it will be returned.
1121
1122 If a public router has not been configured, but a public
1123 network has, a tenant router will be created and returned that
1124 routes traffic to the public network.
1125 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001126 if not client:
1127 client = self.network_client
1128 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001129 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001130 router_id = CONF.network.public_router_id
1131 network_id = CONF.network.public_network_id
1132 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001133 body = client.show_router(router_id)
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +04001134 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001135 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001136 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001137 router.set_gateway(network_id)
1138 return router
1139 else:
1140 raise Exception("Neither of 'public_router_id' or "
1141 "'public_network_id' has been defined.")
1142
Yair Frieddb6c9e92014-08-06 08:53:13 +03001143 def _create_router(self, client=None, tenant_id=None,
1144 namestart='router-smoke'):
1145 if not client:
1146 client = self.network_client
1147 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001148 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001149 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -05001150 result = client.create_router(name=name,
1151 admin_state_up=True,
1152 tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +03001153 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +03001154 **result['router'])
1155 self.assertEqual(router.name, name)
1156 self.addCleanup(self.delete_wrapper, router.delete)
1157 return router
1158
Alok Maurya6384bbb2014-07-13 06:44:29 -07001159 def _update_router_admin_state(self, router, admin_state_up):
1160 router.update(admin_state_up=admin_state_up)
1161 self.assertEqual(admin_state_up, router.admin_state_up)
1162
John Warren94d8faf2015-09-15 12:22:24 -04001163 def create_networks(self, client=None, networks_client=None,
John Warren3961acd2015-10-02 14:38:53 -04001164 subnets_client=None, tenant_id=None,
1165 dns_nameservers=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001166 """Create a network with a subnet connected to a router.
1167
David Shrewsbury9bac3662014-08-07 15:07:01 -04001168 The baremetal driver is a special case since all nodes are
1169 on the same shared network.
1170
Yair Fried413bf2d2014-11-19 17:07:11 +02001171 :param client: network client to create resources with.
1172 :param tenant_id: id of tenant to create resources in.
1173 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001174 :returns: network, subnet, router
1175 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04001176 if CONF.baremetal.driver_enabled:
1177 # NOTE(Shrews): This exception is for environments where tenant
1178 # credential isolation is available, but network separation is
1179 # not (the current baremetal case). Likely can be removed when
1180 # test account mgmt is reworked:
1181 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001182 if not CONF.compute.fixed_network_name:
1183 m = 'fixed_network_name must be specified in config'
1184 raise exceptions.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001185 network = self._get_network_by_name(
1186 CONF.compute.fixed_network_name)
1187 router = None
1188 subnet = None
1189 else:
John Warren94d8faf2015-09-15 12:22:24 -04001190 network = self._create_network(
1191 client=client, networks_client=networks_client,
1192 tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +03001193 router = self._get_router(client=client, tenant_id=tenant_id)
Yair Fried413bf2d2014-11-19 17:07:11 +02001194
John Warren3961acd2015-10-02 14:38:53 -04001195 subnet_kwargs = dict(network=network, client=client,
1196 subnets_client=subnets_client)
Yair Fried413bf2d2014-11-19 17:07:11 +02001197 # use explicit check because empty list is a valid option
1198 if dns_nameservers is not None:
1199 subnet_kwargs['dns_nameservers'] = dns_nameservers
1200 subnet = self._create_subnet(**subnet_kwargs)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001201 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001202 return network, subnet, router
1203
1204
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001205# power/provision states as of icehouse
1206class BaremetalPowerStates(object):
1207 """Possible power states of an Ironic node."""
1208 POWER_ON = 'power on'
1209 POWER_OFF = 'power off'
1210 REBOOT = 'rebooting'
1211 SUSPEND = 'suspended'
1212
1213
1214class BaremetalProvisionStates(object):
1215 """Possible provision states of an Ironic node."""
1216 NOSTATE = None
1217 INIT = 'initializing'
1218 ACTIVE = 'active'
1219 BUILDING = 'building'
1220 DEPLOYWAIT = 'wait call-back'
1221 DEPLOYING = 'deploying'
1222 DEPLOYFAIL = 'deploy failed'
1223 DEPLOYDONE = 'deploy complete'
1224 DELETING = 'deleting'
1225 DELETED = 'deleted'
1226 ERROR = 'error'
1227
1228
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001229class BaremetalScenarioTest(ScenarioTest):
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001230
1231 credentials = ['primary', 'admin']
1232
Adam Gandelman4a48a602014-03-20 18:23:18 -07001233 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001234 def skip_checks(cls):
1235 super(BaremetalScenarioTest, cls).skip_checks()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001236 if (not CONF.service_available.ironic or
1237 not CONF.baremetal.driver_enabled):
1238 msg = 'Ironic not available or Ironic compute driver not enabled'
1239 raise cls.skipException(msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001240
1241 @classmethod
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001242 def setup_clients(cls):
1243 super(BaremetalScenarioTest, cls).setup_clients()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001244
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001245 cls.baremetal_client = cls.admin_manager.baremetal_client
Adam Gandelman4a48a602014-03-20 18:23:18 -07001246
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001247 @classmethod
1248 def resource_setup(cls):
1249 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001250 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001251 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001252
1253 def _node_state_timeout(self, node_id, state_attr,
1254 target_states, timeout=10, interval=1):
1255 if not isinstance(target_states, list):
1256 target_states = [target_states]
1257
1258 def check_state():
1259 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001260 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001261 return True
1262 return False
1263
1264 if not tempest.test.call_until_true(
1265 check_state, timeout, interval):
1266 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1267 (node_id, state_attr, target_states))
1268 raise exceptions.TimeoutException(msg)
1269
1270 def wait_provisioning_state(self, node_id, state, timeout):
1271 self._node_state_timeout(
1272 node_id=node_id, state_attr='provision_state',
1273 target_states=state, timeout=timeout)
1274
1275 def wait_power_state(self, node_id, state):
1276 self._node_state_timeout(
1277 node_id=node_id, state_attr='power_state',
1278 target_states=state, timeout=CONF.baremetal.power_timeout)
1279
1280 def wait_node(self, instance_id):
1281 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001282
Adam Gandelman4a48a602014-03-20 18:23:18 -07001283 def _get_node():
1284 node = None
1285 try:
1286 node = self.get_node(instance_id=instance_id)
Masayuki Igawabfa07602015-01-20 18:47:17 +09001287 except lib_exc.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001288 pass
1289 return node is not None
1290
1291 if not tempest.test.call_until_true(
1292 _get_node, CONF.baremetal.association_timeout, 1):
1293 msg = ('Timed out waiting to get Ironic node by instance id %s'
1294 % instance_id)
1295 raise exceptions.TimeoutException(msg)
1296
1297 def get_node(self, node_id=None, instance_id=None):
1298 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001299 _, body = self.baremetal_client.show_node(node_id)
1300 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001301 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001302 _, body = self.baremetal_client.show_node_by_instance_uuid(
1303 instance_id)
1304 if body['nodes']:
1305 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001306
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001307 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001308 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001309 _, body = self.baremetal_client.list_node_ports(node_uuid)
1310 for port in body['ports']:
1311 _, p = self.baremetal_client.show_port(port['uuid'])
1312 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001313 return ports
1314
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001315 def add_keypair(self):
1316 self.keypair = self.create_keypair()
1317
1318 def verify_connectivity(self, ip=None):
1319 if ip:
1320 dest = self.get_remote_client(ip)
1321 else:
1322 dest = self.get_remote_client(self.instance)
1323 dest.validate_authentication()
1324
1325 def boot_instance(self):
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001326 self.instance = self.create_server(
lanoux5fc14522015-09-21 08:17:35 +00001327 key_name=self.keypair['name'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001328
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001329 self.wait_node(self.instance['id'])
1330 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001331
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001332 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001333
1334 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001335 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001336 [BaremetalProvisionStates.DEPLOYWAIT,
1337 BaremetalProvisionStates.ACTIVE],
1338 timeout=15)
1339
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001340 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001341 BaremetalProvisionStates.ACTIVE,
1342 timeout=CONF.baremetal.active_timeout)
1343
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +00001344 waiters.wait_for_server_status(self.servers_client,
1345 self.instance['id'], 'ACTIVE')
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001346 self.node = self.get_node(instance_id=self.instance['id'])
ghanshyam0f825252015-08-25 16:02:50 +09001347 self.instance = (self.servers_client.show_server(self.instance['id'])
1348 ['server'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001349
1350 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001351 self.servers_client.delete_server(self.instance['id'])
1352 self.wait_power_state(self.node['uuid'],
1353 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001354 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001355 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001356 BaremetalProvisionStates.NOSTATE,
1357 timeout=CONF.baremetal.unprovision_timeout)
1358
Adam Gandelman4a48a602014-03-20 18:23:18 -07001359
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001360class EncryptionScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001361 """Base class for encryption scenario tests"""
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001362
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001363 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001364
1365 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001366 def setup_clients(cls):
1367 super(EncryptionScenarioTest, cls).setup_clients()
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +03001368 if CONF.volume_feature_enabled.api_v1:
1369 cls.admin_volume_types_client = cls.os_adm.volume_types_client
1370 else:
1371 cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001372
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001373 def create_volume_type(self, client=None, name=None):
1374 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001375 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001376 if not name:
1377 name = 'generic'
Ken'ichi Ohmichi6ded8df2015-03-23 02:00:19 +00001378 randomized_name = data_utils.rand_name('scenario-type-' + name)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001379 LOG.debug("Creating a volume type: %s", randomized_name)
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001380 body = client.create_volume_type(
lei zhang83c4bbd2015-11-27 00:35:21 +08001381 name=randomized_name)['volume_type']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001382 self.assertIn('id', body)
1383 self.addCleanup(client.delete_volume_type, body['id'])
1384 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001385
1386 def create_encryption_type(self, client=None, type_id=None, provider=None,
1387 key_size=None, cipher=None,
1388 control_location=None):
1389 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001390 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001391 if not type_id:
1392 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001393 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001394 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001395 client.create_encryption_type(
1396 type_id, provider=provider, key_size=key_size, cipher=cipher,
John Warrend053ded2015-08-13 15:22:48 +00001397 control_location=control_location)['encryption']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001398
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001399
Masayuki Igawa0870db52015-09-18 21:08:36 +09001400class ObjectStorageScenarioTest(ScenarioTest):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001401 """Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001402
1403 Subclasses implement the tests that use the methods provided by this
1404 class.
1405 """
1406
1407 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001408 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001409 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001410 if not CONF.service_available.swift:
1411 skip_msg = ("%s skipped as swift is not available" %
1412 cls.__name__)
1413 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001414
1415 @classmethod
1416 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001417 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001418 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001419 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001420 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001421
1422 @classmethod
1423 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001424 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001425 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001426 cls.account_client = cls.os_operator.account_client
1427 cls.container_client = cls.os_operator.container_client
1428 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001429
Chris Dentde456a12014-09-10 12:41:15 +01001430 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001431 """get swift status for our user account."""
1432 self.account_client.list_account_containers()
1433 LOG.debug('Swift status information obtained successfully')
1434
Chris Dentde456a12014-09-10 12:41:15 +01001435 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001436 name = container_name or data_utils.rand_name(
1437 'swift-scenario-container')
1438 self.container_client.create_container(name)
1439 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001440 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001441 LOG.debug('Container %s created' % (name))
Chris Dent1d4313a2014-10-28 12:16:48 +00001442 self.addCleanup(self.delete_wrapper,
1443 self.container_client.delete_container,
1444 name)
Chris Dent0d494112014-08-26 13:48:30 +01001445 return name
1446
Chris Dentde456a12014-09-10 12:41:15 +01001447 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001448 self.container_client.delete_container(container_name)
1449 LOG.debug('Container %s deleted' % (container_name))
1450
Chris Dentde456a12014-09-10 12:41:15 +01001451 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001452 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1453 obj_data = data_utils.arbitrary_string()
1454 self.object_client.create_object(container_name, obj_name, obj_data)
Chris Dent1d4313a2014-10-28 12:16:48 +00001455 self.addCleanup(self.delete_wrapper,
1456 self.object_client.delete_object,
1457 container_name,
1458 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001459 return obj_name, obj_data
1460
Chris Dentde456a12014-09-10 12:41:15 +01001461 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001462 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001463 self.list_and_check_container_objects(container_name,
1464 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001465
Chris Dentde456a12014-09-10 12:41:15 +01001466 def list_and_check_container_objects(self, container_name,
1467 present_obj=None,
1468 not_present_obj=None):
Ken'ichi Ohmichic4e4f1c2015-11-17 08:16:12 +00001469 # List objects for a given container and assert which are present and
1470 # which are not.
Ghanshyam2a180b82014-06-16 13:54:22 +09001471 if present_obj is None:
1472 present_obj = []
1473 if not_present_obj is None:
1474 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001475 _, object_list = self.container_client.list_container_contents(
1476 container_name)
1477 if present_obj:
1478 for obj in present_obj:
1479 self.assertIn(obj, object_list)
1480 if not_present_obj:
1481 for obj in not_present_obj:
1482 self.assertNotIn(obj, object_list)
1483
Chris Dentde456a12014-09-10 12:41:15 +01001484 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001485 metadata_param = {'metadata_prefix': 'x-container-',
1486 'metadata': {'read': acl}}
1487 self.container_client.update_container_metadata(container_name,
1488 **metadata_param)
1489 resp, _ = self.container_client.list_container_metadata(container_name)
1490 self.assertEqual(resp['x-container-read'], acl)
1491
Chris Dentde456a12014-09-10 12:41:15 +01001492 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001493 _, obj = self.object_client.get_object(container_name, obj_name)
1494 self.assertEqual(obj, expected_data)