blob: fb80a48805b4e65c1df3dc936fe07010f5690764 [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
Rohan Kanade9ce97df2013-12-10 18:59:35 +053026from tempest.common import fixed_network
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
Andrea Frittoli247058f2014-07-16 16:09:22 +010050 cls.floating_ips_client = cls.manager.floating_ips_client
51 # Glance image client v1
52 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000053 # Compute image client
54 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010055 cls.keypairs_client = cls.manager.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010056 # Nova security groups client
57 cls.security_groups_client = cls.manager.security_groups_client
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +000058 cls.security_group_rules_client = (
59 cls.manager.security_group_rules_client)
Andrea Frittoli247058f2014-07-16 16:09:22 +010060 cls.servers_client = cls.manager.servers_client
Yair Fried1fc32a12014-08-04 09:11:30 +030061 cls.interface_client = cls.manager.interfaces_client
62 # Neutron network client
63 cls.network_client = cls.manager.network_client
John Warren94d8faf2015-09-15 12:22:24 -040064 cls.networks_client = cls.manager.networks_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090065 # Heat client
66 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010067
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +030068 if CONF.volume_feature_enabled.api_v1:
69 cls.volumes_client = cls.manager.volumes_client
70 cls.snapshots_client = cls.manager.snapshots_client
71 else:
72 cls.volumes_client = cls.manager.volumes_v2_client
73 cls.snapshots_client = cls.manager.snapshots_v2_client
74
Andrea Frittoli247058f2014-07-16 16:09:22 +010075 # ## Methods to handle sync and async deletes
76
77 def setUp(self):
78 super(ScenarioTest, self).setUp()
79 self.cleanup_waits = []
80 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
81 # because scenario tests in the same test class should not share
82 # resources. If resources were shared between test cases then it
83 # should be a single scenario test instead of multiples.
84
85 # NOTE(yfried): this list is cleaned at the end of test_methods and
86 # not at the end of the class
87 self.addCleanup(self._wait_for_cleanups)
88
Yair Fried1fc32a12014-08-04 09:11:30 +030089 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +010090 """Ignores NotFound exceptions for delete operations.
91
Yair Fried1fc32a12014-08-04 09:11:30 +030092 @param delete_thing: delete method of a resource. method will be
93 executed as delete_thing(*args, **kwargs)
94
Andrea Frittoli247058f2014-07-16 16:09:22 +010095 """
96 try:
97 # Tempest clients return dicts, so there is no common delete
98 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +030099 delete_thing(*args, **kwargs)
Masayuki Igawabfa07602015-01-20 18:47:17 +0900100 except lib_exc.NotFound:
Andrea Frittoli247058f2014-07-16 16:09:22 +0100101 # If the resource is already missing, mission accomplished.
102 pass
103
104 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900105 cleanup_callable, cleanup_args=None,
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000106 cleanup_kwargs=None, waiter_client=None):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700107 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100108
109 @param waiter_callable: callable to wait for the resource to delete
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000110 with the following waiter_client if specified.
Andrea Frittoli247058f2014-07-16 16:09:22 +0100111 @param thing_id: the id of the resource to be cleaned-up
112 @param thing_id_param: the name of the id param in the waiter
113 @param cleanup_callable: method to load pass to self.addCleanup with
114 the following *cleanup_args, **cleanup_kwargs.
115 usually a delete method.
116 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900117 if cleanup_args is None:
118 cleanup_args = []
119 if cleanup_kwargs is None:
120 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100121 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
122 wait_dict = {
123 'waiter_callable': waiter_callable,
124 thing_id_param: thing_id
125 }
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000126 if waiter_client:
127 wait_dict['client'] = waiter_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100128 self.cleanup_waits.append(wait_dict)
129
130 def _wait_for_cleanups(self):
131 """To handle async delete actions, a list of waits is added
132 which will be iterated over as the last step of clearing the
133 cleanup queue. That way all the delete calls are made up front
134 and the tests won't succeed unless the deletes are eventually
135 successful. This is the same basic approach used in the api tests to
136 limit cleanup execution time except here it is multi-resource,
137 because of the nature of the scenario tests.
138 """
139 for wait in self.cleanup_waits:
140 waiter_callable = wait.pop('waiter_callable')
141 waiter_callable(**wait)
142
143 # ## Test functions library
144 #
145 # The create_[resource] functions only return body and discard the
146 # resp part which is not used in scenario tests
147
Yair Frieddb6c9e92014-08-06 08:53:13 +0300148 def create_keypair(self, client=None):
149 if not client:
150 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100151 name = data_utils.rand_name(self.__class__.__name__)
152 # We don't need to create a keypair by pubkey in scenario
Ken'ichi Ohmichie364bce2015-07-17 10:27:59 +0000153 body = client.create_keypair(name=name)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300154 self.addCleanup(client.delete_keypair, name)
ghanshyamdee01f22015-08-17 11:41:47 +0900155 return body['keypair']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100156
157 def create_server(self, name=None, image=None, flavor=None,
158 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900159 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100160 """Creates VM instance.
161
162 @param image: image from which to create the instance
163 @param wait_on_boot: wait for status ACTIVE before continue
164 @param wait_on_delete: force synchronous delete on cleanup
165 @param create_kwargs: additional details for instance creation
166 @return: server dict
167 """
168 if name is None:
169 name = data_utils.rand_name(self.__class__.__name__)
170 if image is None:
171 image = CONF.compute.image_ref
172 if flavor is None:
173 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900174 if create_kwargs is None:
175 create_kwargs = {}
Rohan Kanade9ce97df2013-12-10 18:59:35 +0530176 network = self.get_tenant_network()
Matthew Treinish4bbc1992015-04-07 11:13:40 -0400177 create_kwargs = fixed_network.set_networks_kwarg(network,
178 create_kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100179
Andrea Frittoli247058f2014-07-16 16:09:22 +0100180 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
181 name, image, flavor)
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +0000182 server = self.servers_client.create_server(name=name, imageRef=image,
183 flavorRef=flavor,
ghanshyam0f825252015-08-25 16:02:50 +0900184 **create_kwargs)['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100185 if wait_on_delete:
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000186 self.addCleanup(waiters.wait_for_server_termination,
187 self.servers_client,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100188 server['id'])
189 self.addCleanup_with_wait(
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000190 waiter_callable=waiters.wait_for_server_termination,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100191 thing_id=server['id'], thing_id_param='server_id',
192 cleanup_callable=self.delete_wrapper,
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000193 cleanup_args=[self.servers_client.delete_server, server['id']],
194 waiter_client=self.servers_client)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100195 if wait_on_boot:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000196 waiters.wait_for_server_status(self.servers_client,
197 server_id=server['id'],
198 status='ACTIVE')
Andrea Frittoli247058f2014-07-16 16:09:22 +0100199 # The instance retrieved on creation is missing network
200 # details, necessitating retrieval after it becomes active to
201 # ensure correct details.
ghanshyam0f825252015-08-25 16:02:50 +0900202 server = self.servers_client.show_server(server['id'])['server']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100203 self.assertEqual(server['name'], name)
204 return server
205
Markus Zoeller3d2a21c2015-02-27 12:04:22 +0100206 def create_volume(self, size=None, name=None, snapshot_id=None,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100207 imageRef=None, volume_type=None, wait_on_delete=True):
208 if name is None:
209 name = data_utils.rand_name(self.__class__.__name__)
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000210 volume = self.volumes_client.create_volume(
Andrea Frittoli247058f2014-07-16 16:09:22 +0100211 size=size, display_name=name, snapshot_id=snapshot_id,
John Warren6177c9e2015-08-19 20:00:17 +0000212 imageRef=imageRef, volume_type=volume_type)['volume']
Matt Riedemanne85c2702014-09-10 11:50:13 -0700213
Andrea Frittoli247058f2014-07-16 16:09:22 +0100214 if wait_on_delete:
215 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
216 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700217 self.addCleanup(self.delete_wrapper,
218 self.volumes_client.delete_volume, volume['id'])
219 else:
220 self.addCleanup_with_wait(
221 waiter_callable=self.volumes_client.wait_for_resource_deletion,
222 thing_id=volume['id'], thing_id_param='id',
223 cleanup_callable=self.delete_wrapper,
224 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100225
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +0300226 # NOTE(e0ne): Cinder API v2 uses name instead of display_name
227 if 'display_name' in volume:
228 self.assertEqual(name, volume['display_name'])
229 else:
230 self.assertEqual(name, volume['name'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100231 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
232 # The volume retrieved on creation has a non-up-to-date status.
233 # Retrieval after it becomes active ensures correct details.
John Warren6177c9e2015-08-19 20:00:17 +0000234 volume = self.volumes_client.show_volume(volume['id'])['volume']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100235 return volume
236
Yair Fried1fc32a12014-08-04 09:11:30 +0300237 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100238 _client = self.security_groups_client
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000239 _client_rules = self.security_group_rules_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100240 if secgroup_id is None:
ghanshyamb610b772015-08-24 17:29:38 +0900241 sgs = _client.list_security_groups()['security_groups']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100242 for sg in sgs:
243 if sg['name'] == 'default':
244 secgroup_id = sg['id']
245
246 # These rules are intended to permit inbound ssh and icmp
247 # traffic from all sources, so no group_id is provided.
248 # Setting a group_id would only permit traffic from ports
249 # belonging to the same security group.
250 rulesets = [
251 {
252 # ssh
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000253 'ip_protocol': 'tcp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100254 'from_port': 22,
255 'to_port': 22,
256 'cidr': '0.0.0.0/0',
257 },
258 {
259 # ping
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000260 'ip_protocol': 'icmp',
Andrea Frittoli247058f2014-07-16 16:09:22 +0100261 'from_port': -1,
262 'to_port': -1,
263 'cidr': '0.0.0.0/0',
264 }
265 ]
266 rules = list()
267 for ruleset in rulesets:
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000268 sg_rule = _client_rules.create_security_group_rule(
ghanshyam0a5e1232015-08-24 16:59:59 +0900269 parent_group_id=secgroup_id, **ruleset)['security_group_rule']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100270 self.addCleanup(self.delete_wrapper,
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000271 _client_rules.delete_security_group_rule,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100272 sg_rule['id'])
273 rules.append(sg_rule)
274 return rules
275
Yair Fried1fc32a12014-08-04 09:11:30 +0300276 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100277 # Create security group
278 sg_name = data_utils.rand_name(self.__class__.__name__)
279 sg_desc = sg_name + " description"
David Kranz9964b4e2015-02-06 15:45:29 -0500280 secgroup = self.security_groups_client.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900281 name=sg_name, description=sg_desc)['security_group']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100282 self.assertEqual(secgroup['name'], sg_name)
283 self.assertEqual(secgroup['description'], sg_desc)
284 self.addCleanup(self.delete_wrapper,
285 self.security_groups_client.delete_security_group,
286 secgroup['id'])
287
288 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300289 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100290
291 return secgroup
292
JordanP3fe2dc32014-11-17 13:06:01 +0100293 def get_remote_client(self, server_or_ip, username=None, private_key=None,
294 log_console_of_servers=None):
295 """Get a SSH client to a remote server
296
297 @param server_or_ip a server object as returned by Tempest compute
298 client or an IP address to connect to
299 @param username name of the Linux account on the remote server
300 @param private_key the SSH private key to use
301 @param log_console_of_servers a list of server objects. Each server
302 in the list will have its console printed in the logs in case the
303 SSH connection failed to be established
304 @return a RemoteClient object
305 """
Andrea Frittoli247058f2014-07-16 16:09:22 +0100306 if isinstance(server_or_ip, six.string_types):
307 ip = server_or_ip
308 else:
Andrew Boik4a3daf12015-03-27 01:59:31 -0400309 addrs = server_or_ip['addresses'][CONF.compute.network_for_ssh]
310 try:
311 ip = (addr['addr'] for addr in addrs if
312 netaddr.valid_ipv4(addr['addr'])).next()
313 except StopIteration:
314 raise lib_exc.NotFound("No IPv4 addresses to use for SSH to "
315 "remote server.")
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700316
Andrea Frittoli247058f2014-07-16 16:09:22 +0100317 if username is None:
318 username = CONF.scenario.ssh_user
wantwatering896300c2015-03-27 15:17:42 +0800319 # Set this with 'keypair' or others to log in with keypair or
320 # username/password.
321 if CONF.compute.ssh_auth_method == 'keypair':
322 password = None
323 if private_key is None:
324 private_key = self.keypair['private_key']
325 else:
326 password = CONF.compute.image_ssh_password
327 private_key = None
Andrea Frittoli247058f2014-07-16 16:09:22 +0100328 linux_client = remote_client.RemoteClient(ip, username,
wantwatering896300c2015-03-27 15:17:42 +0800329 pkey=private_key,
330 password=password)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100331 try:
332 linux_client.validate_authentication()
Matt Riedemann5f0ac522015-05-21 09:16:24 -0700333 except Exception as e:
334 message = ('Initializing SSH connection to %(ip)s failed. '
335 'Error: %(error)s' % {'ip': ip, 'error': e})
336 caller = misc_utils.find_test_caller()
337 if caller:
338 message = '(%s) %s' % (caller, message)
339 LOG.exception(message)
Marc Kodererb06db502015-04-30 09:31:27 +0200340 # If we don't explicitly set for which servers we want to
JordanP3fe2dc32014-11-17 13:06:01 +0100341 # log the console output then all the servers will be logged.
342 # See the definition of _log_console_output()
343 self._log_console_output(log_console_of_servers)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100344 raise
345
346 return linux_client
347
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000348 def _image_create(self, name, fmt, path,
349 disk_format=None, properties=None):
Ghanshyam2a180b82014-06-16 13:54:22 +0900350 if properties is None:
351 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100352 name = data_utils.rand_name('%s-' % name)
353 image_file = open(path, 'rb')
354 self.addCleanup(image_file.close)
355 params = {
356 'name': name,
357 'container_format': fmt,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000358 'disk_format': disk_format or fmt,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100359 'is_public': 'False',
360 }
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000361 params['properties'] = properties
John Warren66207252015-07-31 15:51:02 -0400362 image = self.image_client.create_image(**params)['image']
Andrea Frittoli247058f2014-07-16 16:09:22 +0100363 self.addCleanup(self.image_client.delete_image, image['id'])
364 self.assertEqual("queued", image['status'])
365 self.image_client.update_image(image['id'], data=image_file)
366 return image['id']
367
368 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300369 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100370 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
371 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
372 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300373 img_container_format = CONF.scenario.img_container_format
374 img_disk_format = CONF.scenario.img_disk_format
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000375 img_properties = CONF.scenario.img_properties
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300376 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000377 "properties: %s, ami: %s, ari: %s, aki: %s" %
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300378 (img_path, img_container_format, img_disk_format,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000379 img_properties, ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100380 try:
381 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300382 img_container_format,
383 img_path,
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000384 disk_format=img_disk_format,
385 properties=img_properties)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100386 except IOError:
387 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
388 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
389 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
Evgeny Antyshev7ba0d5f2015-04-28 13:18:07 +0000390 properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100391 self.image = self._image_create('scenario-ami', 'ami',
392 path=ami_img_path,
393 properties=properties)
394 LOG.debug("image:%s" % self.image)
395
396 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400397 if not CONF.compute_feature_enabled.console_output:
398 LOG.debug('Console output not supported, cannot log')
399 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100400 if not servers:
David Kranzae99b9a2015-02-16 13:37:01 -0500401 servers = self.servers_client.list_servers()
Andrea Frittoli247058f2014-07-16 16:09:22 +0100402 servers = servers['servers']
403 for server in servers:
Brant Knudson566c5712014-09-24 20:04:50 -0500404 console_output = self.servers_client.get_console_output(
ghanshyam0f825252015-08-25 16:02:50 +0900405 server['id'], length=None)['output']
David Kranzae99b9a2015-02-16 13:37:01 -0500406 LOG.debug('Console output for %s\nbody=\n%s',
407 server['id'], console_output)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100408
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000409 def _log_net_info(self, exc):
410 # network debug is called as part of ssh init
Andrey Pavlov64723762015-04-29 06:24:58 +0300411 if not isinstance(exc, lib_exc.SSHTimeout):
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000412 LOG.debug('Network information on a devstack host')
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000413
nithya-ganesan882595e2014-07-29 18:51:07 +0000414 def create_server_snapshot(self, server, name=None):
415 # Glance client
416 _image_client = self.image_client
417 # Compute client
418 _images_client = self.images_client
419 if name is None:
Ken'ichi Ohmichi6ded8df2015-03-23 02:00:19 +0000420 name = data_utils.rand_name('scenario-snapshot')
nithya-ganesan882595e2014-07-29 18:51:07 +0000421 LOG.debug("Creating a snapshot image for server: %s", server['name'])
Ken'ichi Ohmichi28f18672015-07-17 10:00:38 +0000422 image = _images_client.create_image(server['id'], name=name)
David Kranza5299eb2015-01-15 17:24:05 -0500423 image_id = image.response['location'].split('images/')[1]
nithya-ganesan882595e2014-07-29 18:51:07 +0000424 _image_client.wait_for_image_status(image_id, 'active')
425 self.addCleanup_with_wait(
426 waiter_callable=_image_client.wait_for_resource_deletion,
427 thing_id=image_id, thing_id_param='id',
428 cleanup_callable=self.delete_wrapper,
429 cleanup_args=[_image_client.delete_image, image_id])
David Kranz34f18782015-01-06 13:43:55 -0500430 snapshot_image = _image_client.get_image_meta(image_id)
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300431
432 bdm = snapshot_image.get('properties', {}).get('block_device_mapping')
433 if bdm:
434 bdm = json.loads(bdm)
435 if bdm and 'snapshot_id' in bdm[0]:
436 snapshot_id = bdm[0]['snapshot_id']
437 self.addCleanup(
438 self.snapshots_client.wait_for_resource_deletion,
439 snapshot_id)
440 self.addCleanup(
441 self.delete_wrapper, self.snapshots_client.delete_snapshot,
442 snapshot_id)
Andrey Pavlovd35957b2015-08-27 20:12:15 +0300443 self.snapshots_client.wait_for_snapshot_status(snapshot_id,
444 'available')
Andrey Pavlovc8bd4b12015-08-17 10:20:17 +0300445
nithya-ganesan882595e2014-07-29 18:51:07 +0000446 image_name = snapshot_image['name']
447 self.assertEqual(name, image_name)
448 LOG.debug("Created snapshot image %s for server %s",
449 image_name, server['name'])
450 return snapshot_image
451
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900452 def nova_volume_attach(self):
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000453 volume = self.servers_client.attach_volume(
Ken'ichi Ohmichidfc88de2015-08-13 05:12:20 +0000454 self.server['id'], volumeId=self.volume['id'], device='/dev/%s'
ghanshyam0f825252015-08-25 16:02:50 +0900455 % CONF.compute.volume_device_name)['volumeAttachment']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900456 self.assertEqual(self.volume['id'], volume['id'])
457 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
458 # Refresh the volume after the attachment
John Warren6177c9e2015-08-19 20:00:17 +0000459 self.volume = self.volumes_client.show_volume(volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900460
461 def nova_volume_detach(self):
462 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
463 self.volumes_client.wait_for_volume_status(self.volume['id'],
464 'available')
465
John Warren6177c9e2015-08-19 20:00:17 +0000466 volume = self.volumes_client.show_volume(self.volume['id'])['volume']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900467 self.assertEqual('available', volume['status'])
468
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700469 def rebuild_server(self, server_id, image=None,
470 preserve_ephemeral=False, wait=True,
471 rebuild_kwargs=None):
472 if image is None:
473 image = CONF.compute.image_ref
474
475 rebuild_kwargs = rebuild_kwargs or {}
476
477 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
478 server_id, image, preserve_ephemeral)
Ken'ichi Ohmichi5271b0f2015-08-10 07:53:27 +0000479 self.servers_client.rebuild_server(
480 server_id=server_id, image_ref=image,
481 preserve_ephemeral=preserve_ephemeral,
482 **rebuild_kwargs)
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700483 if wait:
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000484 waiters.wait_for_server_status(self.servers_client,
485 server_id, 'ACTIVE')
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700486
Steven Hardyda2a8352014-10-02 12:52:20 +0100487 def ping_ip_address(self, ip_address, should_succeed=True,
488 ping_timeout=None):
489 timeout = ping_timeout or CONF.compute.ping_timeout
Aaron Rosena7df13b2014-09-23 09:45:45 -0700490 cmd = ['ping', '-c1', '-w1', ip_address]
491
492 def ping():
493 proc = subprocess.Popen(cmd,
494 stdout=subprocess.PIPE,
495 stderr=subprocess.PIPE)
496 proc.communicate()
Shuquan Huang753629e2015-07-20 08:52:29 +0000497
Aaron Rosena7df13b2014-09-23 09:45:45 -0700498 return (proc.returncode == 0) == should_succeed
499
Shuquan Huang753629e2015-07-20 08:52:29 +0000500 caller = misc_utils.find_test_caller()
501 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
502 ' expected result is %(should_succeed)s' % {
503 'caller': caller, 'ip': ip_address, 'timeout': timeout,
504 'should_succeed':
505 'reachable' if should_succeed else 'unreachable'
506 })
507 result = tempest.test.call_until_true(ping, timeout, 1)
508 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
509 'ping result is %(result)s' % {
510 'caller': caller, 'ip': ip_address, 'timeout': timeout,
511 'result': 'expected' if result else 'unexpected'
512 })
513 return result
Aaron Rosena7df13b2014-09-23 09:45:45 -0700514
Yair Friedae0e73d2014-11-24 11:56:26 +0200515 def check_vm_connectivity(self, ip_address,
516 username=None,
517 private_key=None,
518 should_connect=True):
519 """
520 :param ip_address: server to test against
521 :param username: server's ssh username
522 :param private_key: server's ssh private key to be used
523 :param should_connect: True/False indicates positive/negative test
524 positive - attempt ping and ssh
525 negative - attempt ping and fail if succeed
526
527 :raises: AssertError if the result of the connectivity check does
528 not match the value of the should_connect param
529 """
530 if should_connect:
531 msg = "Timed out waiting for %s to become reachable" % ip_address
532 else:
533 msg = "ip address %s is reachable" % ip_address
534 self.assertTrue(self.ping_ip_address(ip_address,
535 should_succeed=should_connect),
536 msg=msg)
537 if should_connect:
538 # no need to check ssh for negative connectivity
539 self.get_remote_client(ip_address, username, private_key)
540
541 def check_public_network_connectivity(self, ip_address, username,
542 private_key, should_connect=True,
543 msg=None, servers=None):
544 # The target login is assumed to have been configured for
545 # key-based authentication by cloud-init.
546 LOG.debug('checking network connections to IP %s with user: %s' %
547 (ip_address, username))
548 try:
549 self.check_vm_connectivity(ip_address,
550 username,
551 private_key,
552 should_connect=should_connect)
Matthew Treinish53483132014-12-09 18:50:06 -0500553 except Exception:
Yair Friedae0e73d2014-11-24 11:56:26 +0200554 ex_msg = 'Public network connectivity check failed'
555 if msg:
556 ex_msg += ": " + msg
557 LOG.exception(ex_msg)
558 self._log_console_output(servers)
Yair Friedae0e73d2014-11-24 11:56:26 +0200559 raise
560
561 def create_floating_ip(self, thing, pool_name=None):
562 """Creates a floating IP and associates to a server using
563 Nova clients
564 """
565
ghanshyam9a3a9a22015-08-18 17:03:55 +0900566 floating_ip = (self.floating_ips_client.create_floating_ip(pool_name)
567 ['floating_ip'])
Yair Friedae0e73d2014-11-24 11:56:26 +0200568 self.addCleanup(self.delete_wrapper,
569 self.floating_ips_client.delete_floating_ip,
570 floating_ip['id'])
571 self.floating_ips_client.associate_floating_ip_to_server(
572 floating_ip['ip'], thing['id'])
573 return floating_ip
574
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300575 def create_timestamp(self, server_or_ip, dev_name=None, mount_path='/mnt'):
576 ssh_client = self.get_remote_client(server_or_ip)
577 if dev_name is not None:
578 ssh_client.make_fs(dev_name)
Matt Riedemann076685a2015-09-30 14:38:16 -0700579 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300580 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
581 ssh_client.exec_command(cmd_timestamp)
582 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
583 % mount_path)
584 if dev_name is not None:
585 ssh_client.umount(mount_path)
586 return timestamp
587
588 def get_timestamp(self, server_or_ip, dev_name=None, mount_path='/mnt'):
589 ssh_client = self.get_remote_client(server_or_ip)
590 if dev_name is not None:
Matt Riedemann076685a2015-09-30 14:38:16 -0700591 ssh_client.mount(dev_name, mount_path)
Alexander Gubanovabd154c2015-09-23 23:24:06 +0300592 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
593 % mount_path)
594 if dev_name is not None:
595 ssh_client.umount(mount_path)
596 return timestamp
597
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100598
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100599class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300600 """Base class for network scenario tests.
601 This class provide helpers for network scenario tests, using the neutron
602 API. Helpers from ancestor which use the nova network API are overridden
603 with the neutron API.
604
605 This Class also enforces using Neutron instead of novanetwork.
606 Subclassed tests will be skipped if Neutron is not enabled
607
608 """
609
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000610 credentials = ['primary', 'admin']
611
Yair Fried1fc32a12014-08-04 09:11:30 +0300612 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +0000613 def skip_checks(cls):
614 super(NetworkScenarioTest, cls).skip_checks()
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100615 if not CONF.service_available.neutron:
616 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300617
618 @classmethod
Andrea Frittoliac20b5e2014-09-15 13:31:14 +0100619 def resource_setup(cls):
620 super(NetworkScenarioTest, cls).resource_setup()
Yair Fried1fc32a12014-08-04 09:11:30 +0300621 cls.tenant_id = cls.manager.identity_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300622
John Warren94d8faf2015-09-15 12:22:24 -0400623 def _create_network(self, client=None, networks_client=None,
624 tenant_id=None, namestart='network-smoke-'):
Yair Frieddb6c9e92014-08-06 08:53:13 +0300625 if not client:
626 client = self.network_client
John Warren94d8faf2015-09-15 12:22:24 -0400627 if not networks_client:
628 networks_client = self.networks_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300629 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000630 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300631 name = data_utils.rand_name(namestart)
John Warren94d8faf2015-09-15 12:22:24 -0400632 result = networks_client.create_network(name=name, tenant_id=tenant_id)
633 network = net_resources.DeletableNetwork(
634 networks_client=networks_client, **result['network'])
Yair Fried1fc32a12014-08-04 09:11:30 +0300635 self.assertEqual(network.name, name)
636 self.addCleanup(self.delete_wrapper, network.delete)
637 return network
638
639 def _list_networks(self, *args, **kwargs):
640 """List networks using admin creds """
John Warren94d8faf2015-09-15 12:22:24 -0400641 networks_list = self.admin_manager.networks_client.list_networks(
ghanshyam2f7cc022015-06-26 18:18:11 +0900642 *args, **kwargs)
643 return networks_list['networks']
Yair Fried1fc32a12014-08-04 09:11:30 +0300644
645 def _list_subnets(self, *args, **kwargs):
646 """List subnets using admin creds """
ghanshyam2f7cc022015-06-26 18:18:11 +0900647 subnets_list = self.admin_manager.network_client.list_subnets(
648 *args, **kwargs)
649 return subnets_list['subnets']
Yair Fried1fc32a12014-08-04 09:11:30 +0300650
651 def _list_routers(self, *args, **kwargs):
652 """List routers using admin creds """
ghanshyam2f7cc022015-06-26 18:18:11 +0900653 routers_list = self.admin_manager.network_client.list_routers(
654 *args, **kwargs)
655 return routers_list['routers']
Yair Fried1fc32a12014-08-04 09:11:30 +0300656
657 def _list_ports(self, *args, **kwargs):
658 """List ports using admin creds """
ghanshyam2f7cc022015-06-26 18:18:11 +0900659 ports_list = self.admin_manager.network_client.list_ports(
660 *args, **kwargs)
661 return ports_list['ports']
Yair Fried1fc32a12014-08-04 09:11:30 +0300662
Yair Fried564d89d2015-08-06 17:02:12 +0300663 def _list_agents(self, *args, **kwargs):
664 """List agents using admin creds """
665 agents_list = self.admin_manager.network_client.list_agents(
666 *args, **kwargs)
667 return agents_list['agents']
668
Yair Frieddb6c9e92014-08-06 08:53:13 +0300669 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
670 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300671 """
672 Create a subnet for the given network within the cidr block
673 configured for tenant networks.
674 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300675 if not client:
676 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300677
678 def cidr_in_use(cidr, tenant_id):
679 """
680 :return True if subnet with cidr already exist in tenant
681 False else
682 """
683 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
684 return len(cidr_in_use) != 0
685
Kirill Shileev14113572014-11-21 16:58:02 +0300686 ip_version = kwargs.pop('ip_version', 4)
687
688 if ip_version == 6:
689 tenant_cidr = netaddr.IPNetwork(
690 CONF.network.tenant_network_v6_cidr)
691 num_bits = CONF.network.tenant_network_v6_mask_bits
692 else:
693 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
694 num_bits = CONF.network.tenant_network_mask_bits
695
Yair Fried1fc32a12014-08-04 09:11:30 +0300696 result = None
Kirill Shileev14113572014-11-21 16:58:02 +0300697 str_cidr = None
Yair Fried1fc32a12014-08-04 09:11:30 +0300698 # Repeatedly attempt subnet creation with sequential cidr
699 # blocks until an unallocated block is found.
Kirill Shileev14113572014-11-21 16:58:02 +0300700 for subnet_cidr in tenant_cidr.subnet(num_bits):
Yair Fried1fc32a12014-08-04 09:11:30 +0300701 str_cidr = str(subnet_cidr)
702 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
703 continue
704
705 subnet = dict(
706 name=data_utils.rand_name(namestart),
Yair Fried1fc32a12014-08-04 09:11:30 +0300707 network_id=network.id,
708 tenant_id=network.tenant_id,
709 cidr=str_cidr,
Kirill Shileev14113572014-11-21 16:58:02 +0300710 ip_version=ip_version,
Yair Fried1fc32a12014-08-04 09:11:30 +0300711 **kwargs
712 )
713 try:
David Kranz34e88122014-12-11 15:24:05 -0500714 result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300715 break
Masayuki Igawad9388762015-01-20 14:56:42 +0900716 except lib_exc.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300717 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
718 if not is_overlapping_cidr:
719 raise
720 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300721 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300722 **result['subnet'])
723 self.assertEqual(subnet.cidr, str_cidr)
724 self.addCleanup(self.delete_wrapper, subnet.delete)
725 return subnet
726
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200727 def _create_port(self, network_id, client=None, namestart='port-quotatest',
728 **kwargs):
Yair Frieddb6c9e92014-08-06 08:53:13 +0300729 if not client:
730 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300731 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -0500732 result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300733 name=name,
Itzik Brown2ca01cd2014-12-08 12:58:20 +0200734 network_id=network_id,
735 **kwargs)
Yair Fried1fc32a12014-08-04 09:11:30 +0300736 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300737 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300738 **result['port'])
739 self.addCleanup(self.delete_wrapper, port.delete)
740 return port
741
Kirill Shileev14113572014-11-21 16:58:02 +0300742 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300743 ports = self._list_ports(device_id=server['id'],
744 fixed_ip=ip_addr)
Kirill Shileev14113572014-11-21 16:58:02 +0300745 # it might happen here that this port has more then one ip address
746 # as in case of dual stack- when this port is created on 2 subnets
Daniel Mellado9e3e1062015-08-06 18:07:05 +0200747 port_map = [(p["id"], fxip["ip_address"])
748 for p in ports
749 for fxip in p["fixed_ips"]
750 if netaddr.valid_ipv4(fxip["ip_address"])]
751
752 self.assertEqual(len(port_map), 1,
753 "Found multiple IPv4 addresses: %s. "
754 "Unable to determine which port to target."
755 % port_map)
756 return port_map[0]
Yair Fried1fc32a12014-08-04 09:11:30 +0300757
David Shrewsbury9bac3662014-08-07 15:07:01 -0400758 def _get_network_by_name(self, network_name):
759 net = self._list_networks(name=network_name)
Adam Gandelman878a5fd2015-03-30 14:33:36 -0700760 self.assertNotEqual(len(net), 0,
761 "Unable to get network by name: %s" % network_name)
Yair Fried8186f812014-09-28 09:39:39 +0300762 return net_resources.AttributeDict(net[0])
David Shrewsbury9bac3662014-08-07 15:07:01 -0400763
Yair Friedae0e73d2014-11-24 11:56:26 +0200764 def create_floating_ip(self, thing, external_network_id=None,
765 port_id=None, client=None):
766 """Creates a floating IP and associates to a resource/port using
767 Neutron client
768 """
769 if not external_network_id:
770 external_network_id = CONF.network.public_network_id
Yair Frieddb6c9e92014-08-06 08:53:13 +0300771 if not client:
772 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300773 if not port_id:
Kirill Shileev14113572014-11-21 16:58:02 +0300774 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
775 else:
776 ip4 = None
David Kranz34e88122014-12-11 15:24:05 -0500777 result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300778 floating_network_id=external_network_id,
779 port_id=port_id,
Kirill Shileev14113572014-11-21 16:58:02 +0300780 tenant_id=thing['tenant_id'],
781 fixed_ip_address=ip4
Yair Fried1fc32a12014-08-04 09:11:30 +0300782 )
783 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300784 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300785 **result['floatingip'])
786 self.addCleanup(self.delete_wrapper, floating_ip.delete)
787 return floating_ip
788
789 def _associate_floating_ip(self, floating_ip, server):
Kirill Shileev14113572014-11-21 16:58:02 +0300790 port_id, _ = self._get_server_port_id_and_ip4(server)
Yair Fried1fc32a12014-08-04 09:11:30 +0300791 floating_ip.update(port_id=port_id)
792 self.assertEqual(port_id, floating_ip.port_id)
793 return floating_ip
794
795 def _disassociate_floating_ip(self, floating_ip):
796 """
797 :param floating_ip: type DeletableFloatingIp
798 """
799 floating_ip.update(port_id=None)
800 self.assertIsNone(floating_ip.port_id)
801 return floating_ip
802
Yair Fried45f92952014-06-26 05:19:19 +0300803 def check_floating_ip_status(self, floating_ip, status):
Carl Baldwina754e2d2014-10-23 22:47:41 +0000804 """Verifies floatingip reaches the given status
Yair Fried45f92952014-06-26 05:19:19 +0300805
806 :param floating_ip: net_resources.DeletableFloatingIp floating IP to
807 to check status
808 :param status: target status
809 :raises: AssertionError if status doesn't match
810 """
Carl Baldwina754e2d2014-10-23 22:47:41 +0000811 def refresh():
812 floating_ip.refresh()
813 return status == floating_ip.status
814
815 tempest.test.call_until_true(refresh,
816 CONF.network.build_timeout,
817 CONF.network.build_interval)
Yair Fried45f92952014-06-26 05:19:19 +0300818 self.assertEqual(status, floating_ip.status,
819 message="FloatingIP: {fp} is at status: {cst}. "
820 "failed to reach status: {st}"
821 .format(fp=floating_ip, cst=floating_ip.status,
822 st=status))
823 LOG.info("FloatingIP: {fp} is at status: {st}"
824 .format(fp=floating_ip, st=status))
825
Yair Fried1fc32a12014-08-04 09:11:30 +0300826 def _check_tenant_network_connectivity(self, server,
827 username,
828 private_key,
829 should_connect=True,
830 servers_for_debug=None):
831 if not CONF.network.tenant_networks_reachable:
832 msg = 'Tenant networks not configured to be reachable.'
833 LOG.info(msg)
834 return
835 # The target login is assumed to have been configured for
836 # key-based authentication by cloud-init.
837 try:
Matthew Treinish71426682015-04-23 11:19:38 -0400838 for net_name, ip_addresses in six.iteritems(server['addresses']):
Yair Fried1fc32a12014-08-04 09:11:30 +0300839 for ip_address in ip_addresses:
ghanshyam807211c2014-12-18 13:21:22 +0900840 self.check_vm_connectivity(ip_address['addr'],
Yair Friedae0e73d2014-11-24 11:56:26 +0200841 username,
842 private_key,
843 should_connect=should_connect)
Yair Fried1fc32a12014-08-04 09:11:30 +0300844 except Exception as e:
845 LOG.exception('Tenant network connectivity check failed')
846 self._log_console_output(servers_for_debug)
Ken'ichi Ohmichi6e201f52014-10-01 04:21:39 +0000847 self._log_net_info(e)
Yair Fried1fc32a12014-08-04 09:11:30 +0300848 raise
849
850 def _check_remote_connectivity(self, source, dest, should_succeed=True):
851 """
852 check ping server via source ssh connection
853
854 :param source: RemoteClient: an ssh connection from which to ping
855 :param dest: and IP to ping against
856 :param should_succeed: boolean should ping succeed or not
857 :returns: boolean -- should_succeed == ping
858 :returns: ping is false if ping failed
859 """
860 def ping_remote():
861 try:
862 source.ping_host(dest)
Andrey Pavlov64723762015-04-29 06:24:58 +0300863 except lib_exc.SSHExecCommandFailed:
Yair Fried1fc32a12014-08-04 09:11:30 +0300864 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
865 % (dest, source.ssh_client.host))
866 return not should_succeed
867 return should_succeed
868
869 return tempest.test.call_until_true(ping_remote,
870 CONF.compute.ping_timeout,
871 1)
872
Yair Frieddb6c9e92014-08-06 08:53:13 +0300873 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300874 namestart='secgroup-smoke'):
875 if client is None:
876 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300877 if tenant_id is None:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000878 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300879 secgroup = self._create_empty_security_group(namestart=namestart,
880 client=client,
881 tenant_id=tenant_id)
882
883 # Add rules to the security group
ghanshyam38890b52015-01-21 15:24:18 +0900884 rules = self._create_loginable_secgroup_rule(client=client,
885 secgroup=secgroup)
Yair Fried1fc32a12014-08-04 09:11:30 +0300886 for rule in rules:
887 self.assertEqual(tenant_id, rule.tenant_id)
888 self.assertEqual(secgroup.id, rule.security_group_id)
889 return secgroup
890
Yair Frieddb6c9e92014-08-06 08:53:13 +0300891 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300892 namestart='secgroup-smoke'):
893 """Create a security group without rules.
894
895 Default rules will be created:
896 - IPv4 egress to any
897 - IPv6 egress to any
898
899 :param tenant_id: secgroup will be created in this tenant
900 :returns: DeletableSecurityGroup -- containing the secgroup created
901 """
902 if client is None:
903 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300904 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000905 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300906 sg_name = data_utils.rand_name(namestart)
907 sg_desc = sg_name + " description"
908 sg_dict = dict(name=sg_name,
909 description=sg_desc)
910 sg_dict['tenant_id'] = tenant_id
David Kranz34e88122014-12-11 15:24:05 -0500911 result = client.create_security_group(**sg_dict)
Yair Fried1fc32a12014-08-04 09:11:30 +0300912 secgroup = net_resources.DeletableSecurityGroup(
913 client=client,
914 **result['security_group']
915 )
916 self.assertEqual(secgroup.name, sg_name)
917 self.assertEqual(tenant_id, secgroup.tenant_id)
918 self.assertEqual(secgroup.description, sg_desc)
919 self.addCleanup(self.delete_wrapper, secgroup.delete)
920 return secgroup
921
Yair Frieddb6c9e92014-08-06 08:53:13 +0300922 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300923 """Get default secgroup for given tenant_id.
924
925 :returns: DeletableSecurityGroup -- default secgroup for given tenant
926 """
927 if client is None:
928 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300929 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000930 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300931 sgs = [
932 sg for sg in client.list_security_groups().values()[0]
933 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
934 ]
935 msg = "No default security group for tenant %s." % (tenant_id)
936 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300937 return net_resources.DeletableSecurityGroup(client=client,
938 **sgs[0])
939
Yair Frieddb6c9e92014-08-06 08:53:13 +0300940 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300941 tenant_id=None, **kwargs):
942 """Create a rule from a dictionary of rule parameters.
943
944 Create a rule in a secgroup. if secgroup not defined will search for
945 default secgroup in tenant_id.
946
947 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300948 :param tenant_id: if secgroup not passed -- the tenant in which to
949 search for default secgroup
950 :param kwargs: a dictionary containing rule parameters:
951 for example, to allow incoming ssh:
952 rule = {
953 direction: 'ingress'
954 protocol:'tcp',
955 port_range_min: 22,
956 port_range_max: 22
957 }
958 """
959 if client is None:
960 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300961 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +0000962 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300963 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300964 secgroup = self._default_security_group(client=client,
965 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300966
967 ruleset = dict(security_group_id=secgroup.id,
968 tenant_id=secgroup.tenant_id)
969 ruleset.update(kwargs)
970
David Kranz34e88122014-12-11 15:24:05 -0500971 sg_rule = client.create_security_group_rule(**ruleset)
Yair Fried1fc32a12014-08-04 09:11:30 +0300972 sg_rule = net_resources.DeletableSecurityGroupRule(
973 client=client,
974 **sg_rule['security_group_rule']
975 )
976 self.addCleanup(self.delete_wrapper, sg_rule.delete)
977 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
978 self.assertEqual(secgroup.id, sg_rule.security_group_id)
979
980 return sg_rule
981
982 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
983 """These rules are intended to permit inbound ssh and icmp
984 traffic from all sources, so no group_id is provided.
985 Setting a group_id would only permit traffic from ports
986 belonging to the same security group.
987 """
988
989 if client is None:
990 client = self.network_client
991 rules = []
992 rulesets = [
993 dict(
994 # ssh
995 protocol='tcp',
996 port_range_min=22,
997 port_range_max=22,
998 ),
999 dict(
1000 # ping
1001 protocol='icmp',
Andreas Scheuring887ca8e2015-02-03 17:56:12 +01001002 ),
1003 dict(
1004 # ipv6-icmp for ping6
1005 protocol='icmp',
1006 ethertype='IPv6',
Yair Fried1fc32a12014-08-04 09:11:30 +03001007 )
1008 ]
1009 for ruleset in rulesets:
1010 for r_direction in ['ingress', 'egress']:
1011 ruleset['direction'] = r_direction
1012 try:
1013 sg_rule = self._create_security_group_rule(
1014 client=client, secgroup=secgroup, **ruleset)
Masayuki Igawad9388762015-01-20 14:56:42 +09001015 except lib_exc.Conflict as ex:
Yair Fried1fc32a12014-08-04 09:11:30 +03001016 # if rule already exist - skip rule and continue
1017 msg = 'Security group rule already exists'
1018 if msg not in ex._error_string:
1019 raise ex
1020 else:
1021 self.assertEqual(r_direction, sg_rule.direction)
1022 rules.append(sg_rule)
1023
1024 return rules
1025
1026 def _ssh_to_server(self, server, private_key):
1027 ssh_login = CONF.compute.image_ssh_user
1028 return self.get_remote_client(server,
1029 username=ssh_login,
1030 private_key=private_key)
1031
Yair Frieddb6c9e92014-08-06 08:53:13 +03001032 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001033 """Retrieve a router for the given tenant id.
1034
1035 If a public router has been configured, it will be returned.
1036
1037 If a public router has not been configured, but a public
1038 network has, a tenant router will be created and returned that
1039 routes traffic to the public network.
1040 """
Yair Frieddb6c9e92014-08-06 08:53:13 +03001041 if not client:
1042 client = self.network_client
1043 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001044 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001045 router_id = CONF.network.public_router_id
1046 network_id = CONF.network.public_network_id
1047 if router_id:
David Kranzca4c7e72015-05-27 11:39:19 -04001048 body = client.show_router(router_id)
Sergey Shnaidman5e9c8fd2014-09-30 18:31:43 +04001049 return net_resources.AttributeDict(**body['router'])
Yair Fried1fc32a12014-08-04 09:11:30 +03001050 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +03001051 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001052 router.set_gateway(network_id)
1053 return router
1054 else:
1055 raise Exception("Neither of 'public_router_id' or "
1056 "'public_network_id' has been defined.")
1057
Yair Frieddb6c9e92014-08-06 08:53:13 +03001058 def _create_router(self, client=None, tenant_id=None,
1059 namestart='router-smoke'):
1060 if not client:
1061 client = self.network_client
1062 if not tenant_id:
Ken'ichi Ohmichi88f12c12014-12-24 01:02:58 +00001063 tenant_id = client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +03001064 name = data_utils.rand_name(namestart)
David Kranz34e88122014-12-11 15:24:05 -05001065 result = client.create_router(name=name,
1066 admin_state_up=True,
1067 tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +03001068 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +03001069 **result['router'])
1070 self.assertEqual(router.name, name)
1071 self.addCleanup(self.delete_wrapper, router.delete)
1072 return router
1073
Alok Maurya6384bbb2014-07-13 06:44:29 -07001074 def _update_router_admin_state(self, router, admin_state_up):
1075 router.update(admin_state_up=admin_state_up)
1076 self.assertEqual(admin_state_up, router.admin_state_up)
1077
John Warren94d8faf2015-09-15 12:22:24 -04001078 def create_networks(self, client=None, networks_client=None,
1079 tenant_id=None, dns_nameservers=None):
Yair Fried1fc32a12014-08-04 09:11:30 +03001080 """Create a network with a subnet connected to a router.
1081
David Shrewsbury9bac3662014-08-07 15:07:01 -04001082 The baremetal driver is a special case since all nodes are
1083 on the same shared network.
1084
Yair Fried413bf2d2014-11-19 17:07:11 +02001085 :param client: network client to create resources with.
1086 :param tenant_id: id of tenant to create resources in.
1087 :param dns_nameservers: list of dns servers to send to subnet.
Yair Fried1fc32a12014-08-04 09:11:30 +03001088 :returns: network, subnet, router
1089 """
David Shrewsbury9bac3662014-08-07 15:07:01 -04001090 if CONF.baremetal.driver_enabled:
1091 # NOTE(Shrews): This exception is for environments where tenant
1092 # credential isolation is available, but network separation is
1093 # not (the current baremetal case). Likely can be removed when
1094 # test account mgmt is reworked:
1095 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
Adam Gandelman878a5fd2015-03-30 14:33:36 -07001096 if not CONF.compute.fixed_network_name:
1097 m = 'fixed_network_name must be specified in config'
1098 raise exceptions.InvalidConfiguration(m)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001099 network = self._get_network_by_name(
1100 CONF.compute.fixed_network_name)
1101 router = None
1102 subnet = None
1103 else:
John Warren94d8faf2015-09-15 12:22:24 -04001104 network = self._create_network(
1105 client=client, networks_client=networks_client,
1106 tenant_id=tenant_id)
Yair Frieddb6c9e92014-08-06 08:53:13 +03001107 router = self._get_router(client=client, tenant_id=tenant_id)
Yair Fried413bf2d2014-11-19 17:07:11 +02001108
1109 subnet_kwargs = dict(network=network, client=client)
1110 # use explicit check because empty list is a valid option
1111 if dns_nameservers is not None:
1112 subnet_kwargs['dns_nameservers'] = dns_nameservers
1113 subnet = self._create_subnet(**subnet_kwargs)
David Shrewsbury9bac3662014-08-07 15:07:01 -04001114 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +03001115 return network, subnet, router
1116
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001117 def create_server(self, name=None, image=None, flavor=None,
1118 wait_on_boot=True, wait_on_delete=True,
John Warren94d8faf2015-09-15 12:22:24 -04001119 network_client=None, networks_client=None,
1120 create_kwargs=None):
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +00001121 if network_client is None:
1122 network_client = self.network_client
John Warren94d8faf2015-09-15 12:22:24 -04001123 if networks_client is None:
1124 networks_client = self.networks_client
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +00001125
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001126 vnic_type = CONF.network.port_vnic_type
1127
1128 # If vnic_type is configured create port for
1129 # every network
1130 if vnic_type:
1131 ports = []
1132 networks = []
1133 create_port_body = {'binding:vnic_type': vnic_type,
1134 'namestart': 'port-smoke'}
1135 if create_kwargs:
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001136 # Convert security group names to security group ids
1137 # to pass to create_port
1138 if create_kwargs.get('security_groups'):
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +00001139 security_groups = network_client.list_security_groups(
1140 ).get('security_groups')
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001141 sec_dict = dict([(s['name'], s['id'])
1142 for s in security_groups])
1143
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +00001144 sec_groups_names = [s['name'] for s in create_kwargs.get(
1145 'security_groups')]
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001146 security_groups_ids = [sec_dict[s]
1147 for s in sec_groups_names]
1148
1149 if security_groups_ids:
1150 create_port_body[
1151 'security_groups'] = security_groups_ids
1152 networks = create_kwargs.get('networks')
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +00001153
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001154 # If there are no networks passed to us we look up
1155 # for the tenant's private networks and create a port
1156 # if there is only one private network. The same behaviour
1157 # as we would expect when passing the call to the clients
1158 # with no networks
1159 if not networks:
John Warren94d8faf2015-09-15 12:22:24 -04001160 networks = networks_client.list_networks(filters={
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001161 'router:external': False})
1162 self.assertEqual(1, len(networks),
1163 "There is more than one"
1164 " network for the tenant")
1165 for net in networks:
1166 net_id = net['uuid']
1167 port = self._create_port(network_id=net_id,
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +00001168 client=network_client,
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001169 **create_port_body)
1170 ports.append({'port': port.id})
1171 if ports:
1172 create_kwargs['networks'] = ports
Shuquan Huangb5c8beb2015-08-05 14:14:01 +00001173 self.ports = ports
Itzik Brown2ca01cd2014-12-08 12:58:20 +02001174
1175 return super(NetworkScenarioTest, self).create_server(
1176 name=name, image=image, flavor=flavor,
1177 wait_on_boot=wait_on_boot, wait_on_delete=wait_on_delete,
1178 create_kwargs=create_kwargs)
1179
Yair Fried1fc32a12014-08-04 09:11:30 +03001180
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001181# power/provision states as of icehouse
1182class BaremetalPowerStates(object):
1183 """Possible power states of an Ironic node."""
1184 POWER_ON = 'power on'
1185 POWER_OFF = 'power off'
1186 REBOOT = 'rebooting'
1187 SUSPEND = 'suspended'
1188
1189
1190class BaremetalProvisionStates(object):
1191 """Possible provision states of an Ironic node."""
1192 NOSTATE = None
1193 INIT = 'initializing'
1194 ACTIVE = 'active'
1195 BUILDING = 'building'
1196 DEPLOYWAIT = 'wait call-back'
1197 DEPLOYING = 'deploying'
1198 DEPLOYFAIL = 'deploy failed'
1199 DEPLOYDONE = 'deploy complete'
1200 DELETING = 'deleting'
1201 DELETED = 'deleted'
1202 ERROR = 'error'
1203
1204
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001205class BaremetalScenarioTest(ScenarioTest):
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001206
1207 credentials = ['primary', 'admin']
1208
Adam Gandelman4a48a602014-03-20 18:23:18 -07001209 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001210 def skip_checks(cls):
1211 super(BaremetalScenarioTest, cls).skip_checks()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001212 if (not CONF.service_available.ironic or
1213 not CONF.baremetal.driver_enabled):
1214 msg = 'Ironic not available or Ironic compute driver not enabled'
1215 raise cls.skipException(msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001216
1217 @classmethod
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001218 def setup_clients(cls):
1219 super(BaremetalScenarioTest, cls).setup_clients()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001220
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001221 cls.baremetal_client = cls.admin_manager.baremetal_client
Adam Gandelman4a48a602014-03-20 18:23:18 -07001222
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001223 @classmethod
1224 def resource_setup(cls):
1225 super(BaremetalScenarioTest, cls).resource_setup()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001226 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001227 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001228
1229 def _node_state_timeout(self, node_id, state_attr,
1230 target_states, timeout=10, interval=1):
1231 if not isinstance(target_states, list):
1232 target_states = [target_states]
1233
1234 def check_state():
1235 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001236 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001237 return True
1238 return False
1239
1240 if not tempest.test.call_until_true(
1241 check_state, timeout, interval):
1242 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1243 (node_id, state_attr, target_states))
1244 raise exceptions.TimeoutException(msg)
1245
1246 def wait_provisioning_state(self, node_id, state, timeout):
1247 self._node_state_timeout(
1248 node_id=node_id, state_attr='provision_state',
1249 target_states=state, timeout=timeout)
1250
1251 def wait_power_state(self, node_id, state):
1252 self._node_state_timeout(
1253 node_id=node_id, state_attr='power_state',
1254 target_states=state, timeout=CONF.baremetal.power_timeout)
1255
1256 def wait_node(self, instance_id):
1257 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001258
Adam Gandelman4a48a602014-03-20 18:23:18 -07001259 def _get_node():
1260 node = None
1261 try:
1262 node = self.get_node(instance_id=instance_id)
Masayuki Igawabfa07602015-01-20 18:47:17 +09001263 except lib_exc.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001264 pass
1265 return node is not None
1266
1267 if not tempest.test.call_until_true(
1268 _get_node, CONF.baremetal.association_timeout, 1):
1269 msg = ('Timed out waiting to get Ironic node by instance id %s'
1270 % instance_id)
1271 raise exceptions.TimeoutException(msg)
1272
1273 def get_node(self, node_id=None, instance_id=None):
1274 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001275 _, body = self.baremetal_client.show_node(node_id)
1276 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001277 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001278 _, body = self.baremetal_client.show_node_by_instance_uuid(
1279 instance_id)
1280 if body['nodes']:
1281 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001282
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001283 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001284 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001285 _, body = self.baremetal_client.list_node_ports(node_uuid)
1286 for port in body['ports']:
1287 _, p = self.baremetal_client.show_port(port['uuid'])
1288 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001289 return ports
1290
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001291 def add_keypair(self):
1292 self.keypair = self.create_keypair()
1293
1294 def verify_connectivity(self, ip=None):
1295 if ip:
1296 dest = self.get_remote_client(ip)
1297 else:
1298 dest = self.get_remote_client(self.instance)
1299 dest.validate_authentication()
1300
1301 def boot_instance(self):
1302 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001303 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001304 }
1305 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001306 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001307
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001308 self.wait_node(self.instance['id'])
1309 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001310
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001311 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001312
1313 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001314 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001315 [BaremetalProvisionStates.DEPLOYWAIT,
1316 BaremetalProvisionStates.ACTIVE],
1317 timeout=15)
1318
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001319 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001320 BaremetalProvisionStates.ACTIVE,
1321 timeout=CONF.baremetal.active_timeout)
1322
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +00001323 waiters.wait_for_server_status(self.servers_client,
1324 self.instance['id'], 'ACTIVE')
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001325 self.node = self.get_node(instance_id=self.instance['id'])
ghanshyam0f825252015-08-25 16:02:50 +09001326 self.instance = (self.servers_client.show_server(self.instance['id'])
1327 ['server'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001328
1329 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001330 self.servers_client.delete_server(self.instance['id'])
1331 self.wait_power_state(self.node['uuid'],
1332 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001333 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001334 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001335 BaremetalProvisionStates.NOSTATE,
1336 timeout=CONF.baremetal.unprovision_timeout)
1337
Adam Gandelman4a48a602014-03-20 18:23:18 -07001338
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001339class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001340 """
1341 Base class for encryption scenario tests
1342 """
1343
Andrea Frittolib21de6c2015-02-06 20:12:38 +00001344 credentials = ['primary', 'admin']
David Kranz4cc852b2015-03-09 14:57:11 -04001345
1346 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001347 def setup_clients(cls):
1348 super(EncryptionScenarioTest, cls).setup_clients()
Ivan Kolodyazhnybcfc32e2015-08-06 13:31:36 +03001349 if CONF.volume_feature_enabled.api_v1:
1350 cls.admin_volume_types_client = cls.os_adm.volume_types_client
1351 else:
1352 cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001353
1354 def _wait_for_volume_status(self, status):
1355 self.status_timeout(
1356 self.volume_client.volumes, self.volume.id, status)
1357
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001358 def nova_boot(self):
1359 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001360 create_kwargs = {'key_name': self.keypair['name']}
1361 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001362 create_kwargs=create_kwargs)
1363
1364 def create_volume_type(self, client=None, name=None):
1365 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001366 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001367 if not name:
1368 name = 'generic'
Ken'ichi Ohmichi6ded8df2015-03-23 02:00:19 +00001369 randomized_name = data_utils.rand_name('scenario-type-' + name)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001370 LOG.debug("Creating a volume type: %s", randomized_name)
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001371 body = client.create_volume_type(
John Warrend053ded2015-08-13 15:22:48 +00001372 randomized_name)['volume_type']
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001373 self.assertIn('id', body)
1374 self.addCleanup(client.delete_volume_type, body['id'])
1375 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001376
1377 def create_encryption_type(self, client=None, type_id=None, provider=None,
1378 key_size=None, cipher=None,
1379 control_location=None):
1380 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001381 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001382 if not type_id:
1383 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001384 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001385 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001386 client.create_encryption_type(
1387 type_id, provider=provider, key_size=key_size, cipher=cipher,
John Warrend053ded2015-08-13 15:22:48 +00001388 control_location=control_location)['encryption']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001389
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001390
Masayuki Igawa0870db52015-09-18 21:08:36 +09001391class ObjectStorageScenarioTest(ScenarioTest):
Chris Dent0d494112014-08-26 13:48:30 +01001392 """
Masayuki Igawa0870db52015-09-18 21:08:36 +09001393 Provide harness to do Object Storage scenario tests.
Chris Dent0d494112014-08-26 13:48:30 +01001394
1395 Subclasses implement the tests that use the methods provided by this
1396 class.
1397 """
1398
1399 @classmethod
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001400 def skip_checks(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001401 super(ObjectStorageScenarioTest, cls).skip_checks()
Chris Dent0d494112014-08-26 13:48:30 +01001402 if not CONF.service_available.swift:
1403 skip_msg = ("%s skipped as swift is not available" %
1404 cls.__name__)
1405 raise cls.skipException(skip_msg)
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001406
1407 @classmethod
1408 def setup_credentials(cls):
Masayuki Igawa60ea6c52014-10-15 17:32:14 +09001409 cls.set_network_resources()
Masayuki Igawa0870db52015-09-18 21:08:36 +09001410 super(ObjectStorageScenarioTest, cls).setup_credentials()
Matthew Treinish4a596932015-03-06 20:37:01 -05001411 operator_role = CONF.object_storage.operator_role
Andrea Frittoli (andreaf)737fac92015-05-12 16:14:35 +01001412 cls.os_operator = cls.get_client_manager(roles=[operator_role])
Emily Hugenbruch5e2d2a22015-02-25 21:35:45 +00001413
1414 @classmethod
1415 def setup_clients(cls):
Masayuki Igawa0870db52015-09-18 21:08:36 +09001416 super(ObjectStorageScenarioTest, cls).setup_clients()
Chris Dent0d494112014-08-26 13:48:30 +01001417 # Clients for Swift
Matthew Treinish8f268292015-02-24 20:01:36 -05001418 cls.account_client = cls.os_operator.account_client
1419 cls.container_client = cls.os_operator.container_client
1420 cls.object_client = cls.os_operator.object_client
Chris Dent0d494112014-08-26 13:48:30 +01001421
Chris Dentde456a12014-09-10 12:41:15 +01001422 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001423 """get swift status for our user account."""
1424 self.account_client.list_account_containers()
1425 LOG.debug('Swift status information obtained successfully')
1426
Chris Dentde456a12014-09-10 12:41:15 +01001427 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001428 name = container_name or data_utils.rand_name(
1429 'swift-scenario-container')
1430 self.container_client.create_container(name)
1431 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001432 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001433 LOG.debug('Container %s created' % (name))
Chris Dent1d4313a2014-10-28 12:16:48 +00001434 self.addCleanup(self.delete_wrapper,
1435 self.container_client.delete_container,
1436 name)
Chris Dent0d494112014-08-26 13:48:30 +01001437 return name
1438
Chris Dentde456a12014-09-10 12:41:15 +01001439 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001440 self.container_client.delete_container(container_name)
1441 LOG.debug('Container %s deleted' % (container_name))
1442
Chris Dentde456a12014-09-10 12:41:15 +01001443 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001444 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1445 obj_data = data_utils.arbitrary_string()
1446 self.object_client.create_object(container_name, obj_name, obj_data)
Chris Dent1d4313a2014-10-28 12:16:48 +00001447 self.addCleanup(self.delete_wrapper,
1448 self.object_client.delete_object,
1449 container_name,
1450 obj_name)
Chris Dent0d494112014-08-26 13:48:30 +01001451 return obj_name, obj_data
1452
Chris Dentde456a12014-09-10 12:41:15 +01001453 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001454 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001455 self.list_and_check_container_objects(container_name,
1456 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001457
Chris Dentde456a12014-09-10 12:41:15 +01001458 def list_and_check_container_objects(self, container_name,
1459 present_obj=None,
1460 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001461 """
1462 List objects for a given container and assert which are present and
1463 which are not.
1464 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001465 if present_obj is None:
1466 present_obj = []
1467 if not_present_obj is None:
1468 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001469 _, object_list = self.container_client.list_container_contents(
1470 container_name)
1471 if present_obj:
1472 for obj in present_obj:
1473 self.assertIn(obj, object_list)
1474 if not_present_obj:
1475 for obj in not_present_obj:
1476 self.assertNotIn(obj, object_list)
1477
Chris Dentde456a12014-09-10 12:41:15 +01001478 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001479 metadata_param = {'metadata_prefix': 'x-container-',
1480 'metadata': {'read': acl}}
1481 self.container_client.update_container_metadata(container_name,
1482 **metadata_param)
1483 resp, _ = self.container_client.list_container_metadata(container_name)
1484 self.assertEqual(resp['x-container-read'], acl)
1485
Chris Dentde456a12014-09-10 12:41:15 +01001486 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001487 _, obj = self.object_client.get_object(container_name, obj_name)
1488 self.assertEqual(obj, expected_data)