blob: 93712fceab3c4283f3e69eeed702bfe5747cf96e [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
Attila Fazekasfb7552a2013-08-27 13:02:26 +020017import logging
Steve Bakerdd7c6ce2013-06-24 14:46:47 +120018import os
Sean Dague6dbc6da2013-05-08 17:49:46 -040019import subprocess
20
Sean Dague6dbc6da2013-05-08 17:49:46 -040021import netaddr
Matthew Treinish96e9e882014-06-09 18:37:19 -040022import six
Sean Dague6dbc6da2013-05-08 17:49:46 -040023
Sean Dague1937d092013-05-17 16:36:38 -040024from tempest.api.network import common as net_common
Andrea Frittoli422fbdf2014-03-20 10:05:18 +000025from tempest import auth
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000026from tempest import clients
Matt Riedemann343305f2014-05-27 09:55:03 -070027from tempest.common import debug
Matthew Treinishb86cda92013-07-29 11:22:23 -040028from tempest.common import isolated_creds
Masayuki Igawa259c1132013-10-31 17:48:44 +090029from tempest.common.utils import data_utils
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090030from tempest.common.utils.linux import remote_client
Matthew Treinish6c072292014-01-29 19:15:52 +000031from tempest import config
Giulio Fidente92f77192013-08-26 17:13:28 +020032from tempest import exceptions
Attila Fazekasfb7552a2013-08-27 13:02:26 +020033from tempest.openstack.common import log
Yair Fried1fc32a12014-08-04 09:11:30 +030034from tempest.services.network import resources as net_resources
Sean Dague6dbc6da2013-05-08 17:49:46 -040035import tempest.test
Sean Dague6dbc6da2013-05-08 17:49:46 -040036
Matthew Treinish6c072292014-01-29 19:15:52 +000037CONF = config.CONF
Sean Dague6dbc6da2013-05-08 17:49:46 -040038
Attila Fazekasfb7552a2013-08-27 13:02:26 +020039LOG = log.getLogger(__name__)
40
41# NOTE(afazekas): Workaround for the stdout logging
42LOG_nova_client = logging.getLogger('novaclient.client')
43LOG_nova_client.addHandler(log.NullHandler())
44
45LOG_cinder_client = logging.getLogger('cinderclient.client')
46LOG_cinder_client.addHandler(log.NullHandler())
Sean Dague6dbc6da2013-05-08 17:49:46 -040047
48
Andrea Frittoli2e733b52014-07-16 14:12:11 +010049class ScenarioTest(tempest.test.BaseTestCase):
Andrea Frittoli486ede72014-09-25 11:50:05 +010050 """Base class for scenario tests. Uses tempest own clients. """
Andrea Frittoli2e733b52014-07-16 14:12:11 +010051
52 @classmethod
53 def setUpClass(cls):
54 super(ScenarioTest, cls).setUpClass()
Andrea Frittoli247058f2014-07-16 16:09:22 +010055 # Using tempest client for isolated credentials as well
Andrea Frittoli2e733b52014-07-16 14:12:11 +010056 cls.isolated_creds = isolated_creds.IsolatedCreds(
Andrea Frittoliae9aca02014-09-25 11:43:11 +010057 cls.__name__, network_resources=cls.network_resources)
Andrea Frittoli2e733b52014-07-16 14:12:11 +010058 cls.manager = clients.Manager(
59 credentials=cls.credentials()
60 )
Andrea Frittoli247058f2014-07-16 16:09:22 +010061 cls.admin_manager = clients.Manager(cls.admin_credentials())
62 # Clients (in alphabetical order)
Adam Gandelmanc78c7572014-08-28 18:38:55 -070063 cls.flavors_client = cls.manager.flavors_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010064 cls.floating_ips_client = cls.manager.floating_ips_client
65 # Glance image client v1
66 cls.image_client = cls.manager.image_client
nithya-ganesan882595e2014-07-29 18:51:07 +000067 # Compute image client
68 cls.images_client = cls.manager.images_client
Andrea Frittoli247058f2014-07-16 16:09:22 +010069 cls.keypairs_client = cls.manager.keypairs_client
70 cls.networks_client = cls.admin_manager.networks_client
71 # Nova security groups client
72 cls.security_groups_client = cls.manager.security_groups_client
73 cls.servers_client = cls.manager.servers_client
74 cls.volumes_client = cls.manager.volumes_client
Joseph Lanouxeef192f2014-08-01 14:32:53 +000075 cls.snapshots_client = cls.manager.snapshots_client
Yair Fried1fc32a12014-08-04 09:11:30 +030076 cls.interface_client = cls.manager.interfaces_client
77 # Neutron network client
78 cls.network_client = cls.manager.network_client
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +090079 # Heat client
80 cls.orchestration_client = cls.manager.orchestration_client
Andrea Frittoli2e733b52014-07-16 14:12:11 +010081
82 @classmethod
83 def _get_credentials(cls, get_creds, ctype):
84 if CONF.compute.allow_tenant_isolation:
85 creds = get_creds()
86 else:
87 creds = auth.get_default_credentials(ctype)
88 return creds
89
90 @classmethod
91 def credentials(cls):
92 return cls._get_credentials(cls.isolated_creds.get_primary_creds,
93 'user')
94
Masayuki Igawaccd66592014-07-17 00:42:42 +090095 @classmethod
Yair Frieddb6c9e92014-08-06 08:53:13 +030096 def alt_credentials(cls):
97 return cls._get_credentials(cls.isolated_creds.get_alt_creds,
98 'alt_user')
99
100 @classmethod
Masayuki Igawaccd66592014-07-17 00:42:42 +0900101 def admin_credentials(cls):
102 return cls._get_credentials(cls.isolated_creds.get_admin_creds,
103 'identity_admin')
104
Andrea Frittoli247058f2014-07-16 16:09:22 +0100105 # ## Methods to handle sync and async deletes
106
107 def setUp(self):
108 super(ScenarioTest, self).setUp()
109 self.cleanup_waits = []
110 # NOTE(mtreinish) This is safe to do in setUp instead of setUp class
111 # because scenario tests in the same test class should not share
112 # resources. If resources were shared between test cases then it
113 # should be a single scenario test instead of multiples.
114
115 # NOTE(yfried): this list is cleaned at the end of test_methods and
116 # not at the end of the class
117 self.addCleanup(self._wait_for_cleanups)
118
Yair Fried1fc32a12014-08-04 09:11:30 +0300119 def delete_wrapper(self, delete_thing, *args, **kwargs):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100120 """Ignores NotFound exceptions for delete operations.
121
Yair Fried1fc32a12014-08-04 09:11:30 +0300122 @param delete_thing: delete method of a resource. method will be
123 executed as delete_thing(*args, **kwargs)
124
Andrea Frittoli247058f2014-07-16 16:09:22 +0100125 """
126 try:
127 # Tempest clients return dicts, so there is no common delete
128 # method available. Using a callable instead
Yair Fried1fc32a12014-08-04 09:11:30 +0300129 delete_thing(*args, **kwargs)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100130 except exceptions.NotFound:
131 # If the resource is already missing, mission accomplished.
132 pass
133
134 def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
Ghanshyam2a180b82014-06-16 13:54:22 +0900135 cleanup_callable, cleanup_args=None,
136 cleanup_kwargs=None, ignore_error=True):
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700137 """Adds wait for async resource deletion at the end of cleanups
Andrea Frittoli247058f2014-07-16 16:09:22 +0100138
139 @param waiter_callable: callable to wait for the resource to delete
140 @param thing_id: the id of the resource to be cleaned-up
141 @param thing_id_param: the name of the id param in the waiter
142 @param cleanup_callable: method to load pass to self.addCleanup with
143 the following *cleanup_args, **cleanup_kwargs.
144 usually a delete method.
145 """
Ghanshyam2a180b82014-06-16 13:54:22 +0900146 if cleanup_args is None:
147 cleanup_args = []
148 if cleanup_kwargs is None:
149 cleanup_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100150 self.addCleanup(cleanup_callable, *cleanup_args, **cleanup_kwargs)
151 wait_dict = {
152 'waiter_callable': waiter_callable,
153 thing_id_param: thing_id
154 }
155 self.cleanup_waits.append(wait_dict)
156
157 def _wait_for_cleanups(self):
158 """To handle async delete actions, a list of waits is added
159 which will be iterated over as the last step of clearing the
160 cleanup queue. That way all the delete calls are made up front
161 and the tests won't succeed unless the deletes are eventually
162 successful. This is the same basic approach used in the api tests to
163 limit cleanup execution time except here it is multi-resource,
164 because of the nature of the scenario tests.
165 """
166 for wait in self.cleanup_waits:
167 waiter_callable = wait.pop('waiter_callable')
168 waiter_callable(**wait)
169
170 # ## Test functions library
171 #
172 # The create_[resource] functions only return body and discard the
173 # resp part which is not used in scenario tests
174
Yair Frieddb6c9e92014-08-06 08:53:13 +0300175 def create_keypair(self, client=None):
176 if not client:
177 client = self.keypairs_client
Andrea Frittoli247058f2014-07-16 16:09:22 +0100178 name = data_utils.rand_name(self.__class__.__name__)
179 # We don't need to create a keypair by pubkey in scenario
Yair Frieddb6c9e92014-08-06 08:53:13 +0300180 resp, body = client.create_keypair(name)
181 self.addCleanup(client.delete_keypair, name)
Andrea Frittoli247058f2014-07-16 16:09:22 +0100182 return body
183
184 def create_server(self, name=None, image=None, flavor=None,
185 wait_on_boot=True, wait_on_delete=True,
Ghanshyam2a180b82014-06-16 13:54:22 +0900186 create_kwargs=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100187 """Creates VM instance.
188
189 @param image: image from which to create the instance
190 @param wait_on_boot: wait for status ACTIVE before continue
191 @param wait_on_delete: force synchronous delete on cleanup
192 @param create_kwargs: additional details for instance creation
193 @return: server dict
194 """
195 if name is None:
196 name = data_utils.rand_name(self.__class__.__name__)
197 if image is None:
198 image = CONF.compute.image_ref
199 if flavor is None:
200 flavor = CONF.compute.flavor_ref
Ghanshyam2a180b82014-06-16 13:54:22 +0900201 if create_kwargs is None:
202 create_kwargs = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100203
Andrea Frittoli247058f2014-07-16 16:09:22 +0100204 LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)",
205 name, image, flavor)
206 _, server = self.servers_client.create_server(name, image, flavor,
207 **create_kwargs)
208 if wait_on_delete:
209 self.addCleanup(self.servers_client.wait_for_server_termination,
210 server['id'])
211 self.addCleanup_with_wait(
212 waiter_callable=self.servers_client.wait_for_server_termination,
213 thing_id=server['id'], thing_id_param='server_id',
214 cleanup_callable=self.delete_wrapper,
215 cleanup_args=[self.servers_client.delete_server, server['id']])
216 if wait_on_boot:
217 self.servers_client.wait_for_server_status(server_id=server['id'],
218 status='ACTIVE')
219 # The instance retrieved on creation is missing network
220 # details, necessitating retrieval after it becomes active to
221 # ensure correct details.
222 _, server = self.servers_client.get_server(server['id'])
223 self.assertEqual(server['name'], name)
224 return server
225
226 def create_volume(self, size=1, name=None, snapshot_id=None,
227 imageRef=None, volume_type=None, wait_on_delete=True):
228 if name is None:
229 name = data_utils.rand_name(self.__class__.__name__)
230 _, volume = self.volumes_client.create_volume(
231 size=size, display_name=name, snapshot_id=snapshot_id,
232 imageRef=imageRef, volume_type=volume_type)
Matt Riedemanne85c2702014-09-10 11:50:13 -0700233
Andrea Frittoli247058f2014-07-16 16:09:22 +0100234 if wait_on_delete:
235 self.addCleanup(self.volumes_client.wait_for_resource_deletion,
236 volume['id'])
Matt Riedemanne85c2702014-09-10 11:50:13 -0700237 self.addCleanup(self.delete_wrapper,
238 self.volumes_client.delete_volume, volume['id'])
239 else:
240 self.addCleanup_with_wait(
241 waiter_callable=self.volumes_client.wait_for_resource_deletion,
242 thing_id=volume['id'], thing_id_param='id',
243 cleanup_callable=self.delete_wrapper,
244 cleanup_args=[self.volumes_client.delete_volume, volume['id']])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100245
246 self.assertEqual(name, volume['display_name'])
247 self.volumes_client.wait_for_volume_status(volume['id'], 'available')
248 # The volume retrieved on creation has a non-up-to-date status.
249 # Retrieval after it becomes active ensures correct details.
250 _, volume = self.volumes_client.get_volume(volume['id'])
251 return volume
252
Yair Fried1fc32a12014-08-04 09:11:30 +0300253 def _create_loginable_secgroup_rule(self, secgroup_id=None):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100254 _client = self.security_groups_client
255 if secgroup_id is None:
256 _, sgs = _client.list_security_groups()
257 for sg in sgs:
258 if sg['name'] == 'default':
259 secgroup_id = sg['id']
260
261 # These rules are intended to permit inbound ssh and icmp
262 # traffic from all sources, so no group_id is provided.
263 # Setting a group_id would only permit traffic from ports
264 # belonging to the same security group.
265 rulesets = [
266 {
267 # ssh
268 'ip_proto': 'tcp',
269 'from_port': 22,
270 'to_port': 22,
271 'cidr': '0.0.0.0/0',
272 },
273 {
274 # ping
275 'ip_proto': 'icmp',
276 'from_port': -1,
277 'to_port': -1,
278 'cidr': '0.0.0.0/0',
279 }
280 ]
281 rules = list()
282 for ruleset in rulesets:
283 _, sg_rule = _client.create_security_group_rule(secgroup_id,
284 **ruleset)
285 self.addCleanup(self.delete_wrapper,
286 _client.delete_security_group_rule,
287 sg_rule['id'])
288 rules.append(sg_rule)
289 return rules
290
Yair Fried1fc32a12014-08-04 09:11:30 +0300291 def _create_security_group(self):
Andrea Frittoli247058f2014-07-16 16:09:22 +0100292 # Create security group
293 sg_name = data_utils.rand_name(self.__class__.__name__)
294 sg_desc = sg_name + " description"
295 _, secgroup = self.security_groups_client.create_security_group(
296 sg_name, sg_desc)
297 self.assertEqual(secgroup['name'], sg_name)
298 self.assertEqual(secgroup['description'], sg_desc)
299 self.addCleanup(self.delete_wrapper,
300 self.security_groups_client.delete_security_group,
301 secgroup['id'])
302
303 # Add rules to the security group
Yair Fried1fc32a12014-08-04 09:11:30 +0300304 self._create_loginable_secgroup_rule(secgroup['id'])
Andrea Frittoli247058f2014-07-16 16:09:22 +0100305
306 return secgroup
307
308 def get_remote_client(self, server_or_ip, username=None, private_key=None):
309 if isinstance(server_or_ip, six.string_types):
310 ip = server_or_ip
311 else:
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700312 addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
313 ip = addr['addr']
314
Andrea Frittoli247058f2014-07-16 16:09:22 +0100315 if username is None:
316 username = CONF.scenario.ssh_user
317 if private_key is None:
318 private_key = self.keypair['private_key']
319 linux_client = remote_client.RemoteClient(ip, username,
320 pkey=private_key)
321 try:
322 linux_client.validate_authentication()
323 except exceptions.SSHTimeout:
324 LOG.exception('ssh connection to %s failed' % ip)
325 debug.log_net_debug()
326 raise
327
328 return linux_client
329
Ghanshyam2a180b82014-06-16 13:54:22 +0900330 def _image_create(self, name, fmt, path, properties=None):
331 if properties is None:
332 properties = {}
Andrea Frittoli247058f2014-07-16 16:09:22 +0100333 name = data_utils.rand_name('%s-' % name)
334 image_file = open(path, 'rb')
335 self.addCleanup(image_file.close)
336 params = {
337 'name': name,
338 'container_format': fmt,
339 'disk_format': fmt,
340 'is_public': 'False',
341 }
342 params.update(properties)
343 _, image = self.image_client.create_image(**params)
344 self.addCleanup(self.image_client.delete_image, image['id'])
345 self.assertEqual("queued", image['status'])
346 self.image_client.update_image(image['id'], data=image_file)
347 return image['id']
348
349 def glance_image_create(self):
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300350 img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
Andrea Frittoli247058f2014-07-16 16:09:22 +0100351 aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
352 ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
353 ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300354 img_container_format = CONF.scenario.img_container_format
355 img_disk_format = CONF.scenario.img_disk_format
356 LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
357 "ami: %s, ari: %s, aki: %s" %
358 (img_path, img_container_format, img_disk_format,
359 ami_img_path, ari_img_path, aki_img_path))
Andrea Frittoli247058f2014-07-16 16:09:22 +0100360 try:
361 self.image = self._image_create('scenario-img',
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300362 img_container_format,
363 img_path,
Andrea Frittoli247058f2014-07-16 16:09:22 +0100364 properties={'disk_format':
Alessandro Pilottib7c1daa2014-08-16 14:24:13 +0300365 img_disk_format})
Andrea Frittoli247058f2014-07-16 16:09:22 +0100366 except IOError:
367 LOG.debug("A qcow2 image was not found. Try to get a uec image.")
368 kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
369 ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
370 properties = {
371 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
372 }
373 self.image = self._image_create('scenario-ami', 'ami',
374 path=ami_img_path,
375 properties=properties)
376 LOG.debug("image:%s" % self.image)
377
378 def _log_console_output(self, servers=None):
Matthew Treinish42a3f3a2014-09-04 15:04:53 -0400379 if not CONF.compute_feature_enabled.console_output:
380 LOG.debug('Console output not supported, cannot log')
381 return
Andrea Frittoli247058f2014-07-16 16:09:22 +0100382 if not servers:
383 _, servers = self.servers_client.list_servers()
384 servers = servers['servers']
385 for server in servers:
386 LOG.debug('Console output for %s', server['id'])
387 LOG.debug(self.servers_client.get_console_output(server['id'],
388 length=None))
389
nithya-ganesan882595e2014-07-29 18:51:07 +0000390 def create_server_snapshot(self, server, name=None):
391 # Glance client
392 _image_client = self.image_client
393 # Compute client
394 _images_client = self.images_client
395 if name is None:
396 name = data_utils.rand_name('scenario-snapshot-')
397 LOG.debug("Creating a snapshot image for server: %s", server['name'])
398 resp, image = _images_client.create_image(server['id'], name)
399 image_id = resp['location'].split('images/')[1]
400 _image_client.wait_for_image_status(image_id, 'active')
401 self.addCleanup_with_wait(
402 waiter_callable=_image_client.wait_for_resource_deletion,
403 thing_id=image_id, thing_id_param='id',
404 cleanup_callable=self.delete_wrapper,
405 cleanup_args=[_image_client.delete_image, image_id])
406 _, snapshot_image = _image_client.get_image_meta(image_id)
407 image_name = snapshot_image['name']
408 self.assertEqual(name, image_name)
409 LOG.debug("Created snapshot image %s for server %s",
410 image_name, server['name'])
411 return snapshot_image
412
Masayuki Igawa1f0ad632014-08-05 13:36:56 +0900413 def nova_volume_attach(self):
414 # TODO(andreaf) Device should be here CONF.compute.volume_device_name
415 _, volume_attachment = self.servers_client.attach_volume(
416 self.server['id'], self.volume['id'], '/dev/vdb')
417 volume = volume_attachment['volumeAttachment']
418 self.assertEqual(self.volume['id'], volume['id'])
419 self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
420 # Refresh the volume after the attachment
421 _, self.volume = self.volumes_client.get_volume(volume['id'])
422
423 def nova_volume_detach(self):
424 self.servers_client.detach_volume(self.server['id'], self.volume['id'])
425 self.volumes_client.wait_for_volume_status(self.volume['id'],
426 'available')
427
428 _, volume = self.volumes_client.get_volume(self.volume['id'])
429 self.assertEqual('available', volume['status'])
430
Adam Gandelmanc78c7572014-08-28 18:38:55 -0700431 def rebuild_server(self, server_id, image=None,
432 preserve_ephemeral=False, wait=True,
433 rebuild_kwargs=None):
434 if image is None:
435 image = CONF.compute.image_ref
436
437 rebuild_kwargs = rebuild_kwargs or {}
438
439 LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
440 server_id, image, preserve_ephemeral)
441 self.servers_client.rebuild(server_id=server_id, image_ref=image,
442 preserve_ephemeral=preserve_ephemeral,
443 **rebuild_kwargs)
444 if wait:
445 self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
446
Aaron Rosena7df13b2014-09-23 09:45:45 -0700447 def ping_ip_address(self, ip_address, should_succeed=True):
448 cmd = ['ping', '-c1', '-w1', ip_address]
449
450 def ping():
451 proc = subprocess.Popen(cmd,
452 stdout=subprocess.PIPE,
453 stderr=subprocess.PIPE)
454 proc.communicate()
455 return (proc.returncode == 0) == should_succeed
456
457 return tempest.test.call_until_true(
458 ping, CONF.compute.ping_timeout, 1)
459
Andrea Frittoli2e733b52014-07-16 14:12:11 +0100460
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100461class NetworkScenarioTest(ScenarioTest):
Yair Fried1fc32a12014-08-04 09:11:30 +0300462 """Base class for network scenario tests.
463 This class provide helpers for network scenario tests, using the neutron
464 API. Helpers from ancestor which use the nova network API are overridden
465 with the neutron API.
466
467 This Class also enforces using Neutron instead of novanetwork.
468 Subclassed tests will be skipped if Neutron is not enabled
469
470 """
471
472 @classmethod
473 def check_preconditions(cls):
Andrea Frittoli2ddc2632014-09-25 11:03:00 +0100474 if not CONF.service_available.neutron:
475 raise cls.skipException('Neutron not available')
Yair Fried1fc32a12014-08-04 09:11:30 +0300476
477 @classmethod
478 def setUpClass(cls):
Andrea Frittoli4971fc82014-09-25 10:22:20 +0100479 super(NetworkScenarioTest, cls).setUpClass()
Yair Fried1fc32a12014-08-04 09:11:30 +0300480 cls.tenant_id = cls.manager.identity_client.tenant_id
481 cls.check_preconditions()
482
Yair Frieddb6c9e92014-08-06 08:53:13 +0300483 def _create_network(self, client=None, tenant_id=None,
484 namestart='network-smoke-'):
485 if not client:
486 client = self.network_client
487 if not tenant_id:
488 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300489 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300490 _, result = client.create_network(name=name, tenant_id=tenant_id)
491 network = net_resources.DeletableNetwork(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300492 **result['network'])
493 self.assertEqual(network.name, name)
494 self.addCleanup(self.delete_wrapper, network.delete)
495 return network
496
497 def _list_networks(self, *args, **kwargs):
498 """List networks using admin creds """
499 return self._admin_lister('networks')(*args, **kwargs)
500
501 def _list_subnets(self, *args, **kwargs):
502 """List subnets using admin creds """
503 return self._admin_lister('subnets')(*args, **kwargs)
504
505 def _list_routers(self, *args, **kwargs):
506 """List routers using admin creds """
507 return self._admin_lister('routers')(*args, **kwargs)
508
509 def _list_ports(self, *args, **kwargs):
510 """List ports using admin creds """
511 return self._admin_lister('ports')(*args, **kwargs)
512
513 def _admin_lister(self, resource_type):
514 def temp(*args, **kwargs):
515 temp_method = self.admin_manager.network_client.__getattr__(
516 'list_%s' % resource_type)
517 _, resource_list = temp_method(*args, **kwargs)
518 return resource_list[resource_type]
519 return temp
520
Yair Frieddb6c9e92014-08-06 08:53:13 +0300521 def _create_subnet(self, network, client=None, namestart='subnet-smoke',
522 **kwargs):
Yair Fried1fc32a12014-08-04 09:11:30 +0300523 """
524 Create a subnet for the given network within the cidr block
525 configured for tenant networks.
526 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300527 if not client:
528 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300529
530 def cidr_in_use(cidr, tenant_id):
531 """
532 :return True if subnet with cidr already exist in tenant
533 False else
534 """
535 cidr_in_use = self._list_subnets(tenant_id=tenant_id, cidr=cidr)
536 return len(cidr_in_use) != 0
537
538 tenant_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
539 result = None
540 # Repeatedly attempt subnet creation with sequential cidr
541 # blocks until an unallocated block is found.
542 for subnet_cidr in tenant_cidr.subnet(
543 CONF.network.tenant_network_mask_bits):
544 str_cidr = str(subnet_cidr)
545 if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
546 continue
547
548 subnet = dict(
549 name=data_utils.rand_name(namestart),
550 ip_version=4,
551 network_id=network.id,
552 tenant_id=network.tenant_id,
553 cidr=str_cidr,
554 **kwargs
555 )
556 try:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300557 _, result = client.create_subnet(**subnet)
Yair Fried1fc32a12014-08-04 09:11:30 +0300558 break
Yair Frieddb6c9e92014-08-06 08:53:13 +0300559 except exceptions.Conflict as e:
Yair Fried1fc32a12014-08-04 09:11:30 +0300560 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
561 if not is_overlapping_cidr:
562 raise
563 self.assertIsNotNone(result, 'Unable to allocate tenant network')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300564 subnet = net_resources.DeletableSubnet(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300565 **result['subnet'])
566 self.assertEqual(subnet.cidr, str_cidr)
567 self.addCleanup(self.delete_wrapper, subnet.delete)
568 return subnet
569
Yair Frieddb6c9e92014-08-06 08:53:13 +0300570 def _create_port(self, network, client=None, namestart='port-quotatest'):
571 if not client:
572 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300573 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300574 _, result = client.create_port(
Yair Fried1fc32a12014-08-04 09:11:30 +0300575 name=name,
576 network_id=network.id,
577 tenant_id=network.tenant_id)
578 self.assertIsNotNone(result, 'Unable to allocate port')
Yair Frieddb6c9e92014-08-06 08:53:13 +0300579 port = net_resources.DeletablePort(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300580 **result['port'])
581 self.addCleanup(self.delete_wrapper, port.delete)
582 return port
583
584 def _get_server_port_id(self, server, ip_addr=None):
585 ports = self._list_ports(device_id=server['id'],
586 fixed_ip=ip_addr)
587 self.assertEqual(len(ports), 1,
588 "Unable to determine which port to target.")
589 return ports[0]['id']
590
David Shrewsbury9bac3662014-08-07 15:07:01 -0400591 def _get_network_by_name(self, network_name):
592 net = self._list_networks(name=network_name)
593 return net_common.AttributeDict(net[0])
594
Yair Frieddb6c9e92014-08-06 08:53:13 +0300595 def _create_floating_ip(self, thing, external_network_id, port_id=None,
596 client=None):
597 if not client:
598 client = self.network_client
Yair Fried1fc32a12014-08-04 09:11:30 +0300599 if not port_id:
600 port_id = self._get_server_port_id(thing)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300601 _, result = client.create_floatingip(
Yair Fried1fc32a12014-08-04 09:11:30 +0300602 floating_network_id=external_network_id,
603 port_id=port_id,
604 tenant_id=thing['tenant_id']
605 )
606 floating_ip = net_resources.DeletableFloatingIp(
Yair Frieddb6c9e92014-08-06 08:53:13 +0300607 client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300608 **result['floatingip'])
609 self.addCleanup(self.delete_wrapper, floating_ip.delete)
610 return floating_ip
611
612 def _associate_floating_ip(self, floating_ip, server):
613 port_id = self._get_server_port_id(server)
614 floating_ip.update(port_id=port_id)
615 self.assertEqual(port_id, floating_ip.port_id)
616 return floating_ip
617
618 def _disassociate_floating_ip(self, floating_ip):
619 """
620 :param floating_ip: type DeletableFloatingIp
621 """
622 floating_ip.update(port_id=None)
623 self.assertIsNone(floating_ip.port_id)
624 return floating_ip
625
Yair Fried1fc32a12014-08-04 09:11:30 +0300626 def _check_vm_connectivity(self, ip_address,
627 username=None,
628 private_key=None,
629 should_connect=True):
630 """
631 :param ip_address: server to test against
632 :param username: server's ssh username
633 :param private_key: server's ssh private key to be used
634 :param should_connect: True/False indicates positive/negative test
635 positive - attempt ping and ssh
636 negative - attempt ping and fail if succeed
637
638 :raises: AssertError if the result of the connectivity check does
639 not match the value of the should_connect param
640 """
641 if should_connect:
642 msg = "Timed out waiting for %s to become reachable" % ip_address
643 else:
644 msg = "ip address %s is reachable" % ip_address
Aaron Rosena7df13b2014-09-23 09:45:45 -0700645 self.assertTrue(self.ping_ip_address(ip_address,
646 should_succeed=should_connect),
Yair Fried1fc32a12014-08-04 09:11:30 +0300647 msg=msg)
648 if should_connect:
649 # no need to check ssh for negative connectivity
650 self.get_remote_client(ip_address, username, private_key)
651
652 def _check_public_network_connectivity(self, ip_address, username,
653 private_key, should_connect=True,
654 msg=None, servers=None):
655 # The target login is assumed to have been configured for
656 # key-based authentication by cloud-init.
657 LOG.debug('checking network connections to IP %s with user: %s' %
658 (ip_address, username))
659 try:
660 self._check_vm_connectivity(ip_address,
661 username,
662 private_key,
663 should_connect=should_connect)
664 except Exception as e:
665 ex_msg = 'Public network connectivity check failed'
666 if msg:
667 ex_msg += ": " + msg
668 LOG.exception(ex_msg)
669 self._log_console_output(servers)
670 # network debug is called as part of ssh init
671 if not isinstance(e, exceptions.SSHTimeout):
672 debug.log_net_debug()
673 raise
674
675 def _check_tenant_network_connectivity(self, server,
676 username,
677 private_key,
678 should_connect=True,
679 servers_for_debug=None):
680 if not CONF.network.tenant_networks_reachable:
681 msg = 'Tenant networks not configured to be reachable.'
682 LOG.info(msg)
683 return
684 # The target login is assumed to have been configured for
685 # key-based authentication by cloud-init.
686 try:
687 for net_name, ip_addresses in server['networks'].iteritems():
688 for ip_address in ip_addresses:
689 self._check_vm_connectivity(ip_address,
690 username,
691 private_key,
692 should_connect=should_connect)
693 except Exception as e:
694 LOG.exception('Tenant network connectivity check failed')
695 self._log_console_output(servers_for_debug)
696 # network debug is called as part of ssh init
697 if not isinstance(e, exceptions.SSHTimeout):
698 debug.log_net_debug()
699 raise
700
701 def _check_remote_connectivity(self, source, dest, should_succeed=True):
702 """
703 check ping server via source ssh connection
704
705 :param source: RemoteClient: an ssh connection from which to ping
706 :param dest: and IP to ping against
707 :param should_succeed: boolean should ping succeed or not
708 :returns: boolean -- should_succeed == ping
709 :returns: ping is false if ping failed
710 """
711 def ping_remote():
712 try:
713 source.ping_host(dest)
714 except exceptions.SSHExecCommandFailed:
715 LOG.warn('Failed to ping IP: %s via a ssh connection from: %s.'
716 % (dest, source.ssh_client.host))
717 return not should_succeed
718 return should_succeed
719
720 return tempest.test.call_until_true(ping_remote,
721 CONF.compute.ping_timeout,
722 1)
723
Yair Frieddb6c9e92014-08-06 08:53:13 +0300724 def _create_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300725 namestart='secgroup-smoke'):
726 if client is None:
727 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300728 if tenant_id is None:
729 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300730 secgroup = self._create_empty_security_group(namestart=namestart,
731 client=client,
732 tenant_id=tenant_id)
733
734 # Add rules to the security group
735 rules = self._create_loginable_secgroup_rule(secgroup=secgroup)
736 for rule in rules:
737 self.assertEqual(tenant_id, rule.tenant_id)
738 self.assertEqual(secgroup.id, rule.security_group_id)
739 return secgroup
740
Yair Frieddb6c9e92014-08-06 08:53:13 +0300741 def _create_empty_security_group(self, client=None, tenant_id=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300742 namestart='secgroup-smoke'):
743 """Create a security group without rules.
744
745 Default rules will be created:
746 - IPv4 egress to any
747 - IPv6 egress to any
748
749 :param tenant_id: secgroup will be created in this tenant
750 :returns: DeletableSecurityGroup -- containing the secgroup created
751 """
752 if client is None:
753 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300754 if not tenant_id:
755 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300756 sg_name = data_utils.rand_name(namestart)
757 sg_desc = sg_name + " description"
758 sg_dict = dict(name=sg_name,
759 description=sg_desc)
760 sg_dict['tenant_id'] = tenant_id
761 _, result = client.create_security_group(**sg_dict)
762 secgroup = net_resources.DeletableSecurityGroup(
763 client=client,
764 **result['security_group']
765 )
766 self.assertEqual(secgroup.name, sg_name)
767 self.assertEqual(tenant_id, secgroup.tenant_id)
768 self.assertEqual(secgroup.description, sg_desc)
769 self.addCleanup(self.delete_wrapper, secgroup.delete)
770 return secgroup
771
Yair Frieddb6c9e92014-08-06 08:53:13 +0300772 def _default_security_group(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300773 """Get default secgroup for given tenant_id.
774
775 :returns: DeletableSecurityGroup -- default secgroup for given tenant
776 """
777 if client is None:
778 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300779 if not tenant_id:
780 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300781 sgs = [
782 sg for sg in client.list_security_groups().values()[0]
783 if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
784 ]
785 msg = "No default security group for tenant %s." % (tenant_id)
786 self.assertTrue(len(sgs) > 0, msg)
Yair Fried1fc32a12014-08-04 09:11:30 +0300787 return net_resources.DeletableSecurityGroup(client=client,
788 **sgs[0])
789
Yair Frieddb6c9e92014-08-06 08:53:13 +0300790 def _create_security_group_rule(self, secgroup=None, client=None,
Yair Fried1fc32a12014-08-04 09:11:30 +0300791 tenant_id=None, **kwargs):
792 """Create a rule from a dictionary of rule parameters.
793
794 Create a rule in a secgroup. if secgroup not defined will search for
795 default secgroup in tenant_id.
796
797 :param secgroup: type DeletableSecurityGroup.
Yair Fried1fc32a12014-08-04 09:11:30 +0300798 :param tenant_id: if secgroup not passed -- the tenant in which to
799 search for default secgroup
800 :param kwargs: a dictionary containing rule parameters:
801 for example, to allow incoming ssh:
802 rule = {
803 direction: 'ingress'
804 protocol:'tcp',
805 port_range_min: 22,
806 port_range_max: 22
807 }
808 """
809 if client is None:
810 client = self.network_client
Yair Frieddb6c9e92014-08-06 08:53:13 +0300811 if not tenant_id:
812 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300813 if secgroup is None:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300814 secgroup = self._default_security_group(client=client,
815 tenant_id=tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300816
817 ruleset = dict(security_group_id=secgroup.id,
818 tenant_id=secgroup.tenant_id)
819 ruleset.update(kwargs)
820
821 _, sg_rule = client.create_security_group_rule(**ruleset)
822 sg_rule = net_resources.DeletableSecurityGroupRule(
823 client=client,
824 **sg_rule['security_group_rule']
825 )
826 self.addCleanup(self.delete_wrapper, sg_rule.delete)
827 self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
828 self.assertEqual(secgroup.id, sg_rule.security_group_id)
829
830 return sg_rule
831
832 def _create_loginable_secgroup_rule(self, client=None, secgroup=None):
833 """These rules are intended to permit inbound ssh and icmp
834 traffic from all sources, so no group_id is provided.
835 Setting a group_id would only permit traffic from ports
836 belonging to the same security group.
837 """
838
839 if client is None:
840 client = self.network_client
841 rules = []
842 rulesets = [
843 dict(
844 # ssh
845 protocol='tcp',
846 port_range_min=22,
847 port_range_max=22,
848 ),
849 dict(
850 # ping
851 protocol='icmp',
852 )
853 ]
854 for ruleset in rulesets:
855 for r_direction in ['ingress', 'egress']:
856 ruleset['direction'] = r_direction
857 try:
858 sg_rule = self._create_security_group_rule(
859 client=client, secgroup=secgroup, **ruleset)
860 except exceptions.Conflict as ex:
861 # if rule already exist - skip rule and continue
862 msg = 'Security group rule already exists'
863 if msg not in ex._error_string:
864 raise ex
865 else:
866 self.assertEqual(r_direction, sg_rule.direction)
867 rules.append(sg_rule)
868
869 return rules
870
Miguel Lavalle02ba8cd2014-09-01 19:23:22 -0500871 def _create_pool(self, lb_method, protocol, subnet_id):
872 """Wrapper utility that returns a test pool."""
873 client = self.network_client
874 name = data_utils.rand_name('pool')
875 _, resp_pool = client.create_pool(protocol=protocol, name=name,
876 subnet_id=subnet_id,
877 lb_method=lb_method)
878 pool = net_resources.DeletablePool(client=client, **resp_pool['pool'])
879 self.assertEqual(pool['name'], name)
880 self.addCleanup(self.delete_wrapper, pool.delete)
881 return pool
882
883 def _create_member(self, address, protocol_port, pool_id):
884 """Wrapper utility that returns a test member."""
885 client = self.network_client
886 _, resp_member = client.create_member(protocol_port=protocol_port,
887 pool_id=pool_id,
888 address=address)
889 member = net_resources.DeletableMember(client=client,
890 **resp_member['member'])
891 self.addCleanup(self.delete_wrapper, member.delete)
892 return member
893
894 def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
895 """Wrapper utility that returns a test vip."""
896 client = self.network_client
897 name = data_utils.rand_name('vip')
898 _, resp_vip = client.create_vip(protocol=protocol, name=name,
899 subnet_id=subnet_id, pool_id=pool_id,
900 protocol_port=protocol_port)
901 vip = net_resources.DeletableVip(client=client, **resp_vip['vip'])
902 self.assertEqual(vip['name'], name)
903 self.addCleanup(self.delete_wrapper, vip.delete)
904 return vip
905
Yair Fried1fc32a12014-08-04 09:11:30 +0300906 def _ssh_to_server(self, server, private_key):
907 ssh_login = CONF.compute.image_ssh_user
908 return self.get_remote_client(server,
909 username=ssh_login,
910 private_key=private_key)
911
Yair Frieddb6c9e92014-08-06 08:53:13 +0300912 def _get_router(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300913 """Retrieve a router for the given tenant id.
914
915 If a public router has been configured, it will be returned.
916
917 If a public router has not been configured, but a public
918 network has, a tenant router will be created and returned that
919 routes traffic to the public network.
920 """
Yair Frieddb6c9e92014-08-06 08:53:13 +0300921 if not client:
922 client = self.network_client
923 if not tenant_id:
924 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300925 router_id = CONF.network.public_router_id
926 network_id = CONF.network.public_network_id
927 if router_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300928 result = client.show_router(router_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300929 return net_resources.AttributeDict(**result['router'])
930 elif network_id:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300931 router = self._create_router(client, tenant_id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300932 router.set_gateway(network_id)
933 return router
934 else:
935 raise Exception("Neither of 'public_router_id' or "
936 "'public_network_id' has been defined.")
937
Yair Frieddb6c9e92014-08-06 08:53:13 +0300938 def _create_router(self, client=None, tenant_id=None,
939 namestart='router-smoke'):
940 if not client:
941 client = self.network_client
942 if not tenant_id:
943 tenant_id = client.rest_client.tenant_id
Yair Fried1fc32a12014-08-04 09:11:30 +0300944 name = data_utils.rand_name(namestart)
Yair Frieddb6c9e92014-08-06 08:53:13 +0300945 _, result = client.create_router(name=name,
946 admin_state_up=True,
947 tenant_id=tenant_id)
948 router = net_resources.DeletableRouter(client=client,
Yair Fried1fc32a12014-08-04 09:11:30 +0300949 **result['router'])
950 self.assertEqual(router.name, name)
951 self.addCleanup(self.delete_wrapper, router.delete)
952 return router
953
Yair Frieddb6c9e92014-08-06 08:53:13 +0300954 def create_networks(self, client=None, tenant_id=None):
Yair Fried1fc32a12014-08-04 09:11:30 +0300955 """Create a network with a subnet connected to a router.
956
David Shrewsbury9bac3662014-08-07 15:07:01 -0400957 The baremetal driver is a special case since all nodes are
958 on the same shared network.
959
Yair Fried1fc32a12014-08-04 09:11:30 +0300960 :returns: network, subnet, router
961 """
David Shrewsbury9bac3662014-08-07 15:07:01 -0400962 if CONF.baremetal.driver_enabled:
963 # NOTE(Shrews): This exception is for environments where tenant
964 # credential isolation is available, but network separation is
965 # not (the current baremetal case). Likely can be removed when
966 # test account mgmt is reworked:
967 # https://blueprints.launchpad.net/tempest/+spec/test-accounts
968 network = self._get_network_by_name(
969 CONF.compute.fixed_network_name)
970 router = None
971 subnet = None
972 else:
Yair Frieddb6c9e92014-08-06 08:53:13 +0300973 network = self._create_network(client=client, tenant_id=tenant_id)
974 router = self._get_router(client=client, tenant_id=tenant_id)
975 subnet = self._create_subnet(network=network, client=client)
David Shrewsbury9bac3662014-08-07 15:07:01 -0400976 subnet.add_to_router(router.id)
Yair Fried1fc32a12014-08-04 09:11:30 +0300977 return network, subnet, router
978
979
David Shrewsbury06f7f8a2014-05-20 13:55:57 -0400980# power/provision states as of icehouse
981class BaremetalPowerStates(object):
982 """Possible power states of an Ironic node."""
983 POWER_ON = 'power on'
984 POWER_OFF = 'power off'
985 REBOOT = 'rebooting'
986 SUSPEND = 'suspended'
987
988
989class BaremetalProvisionStates(object):
990 """Possible provision states of an Ironic node."""
991 NOSTATE = None
992 INIT = 'initializing'
993 ACTIVE = 'active'
994 BUILDING = 'building'
995 DEPLOYWAIT = 'wait call-back'
996 DEPLOYING = 'deploying'
997 DEPLOYFAIL = 'deploy failed'
998 DEPLOYDONE = 'deploy complete'
999 DELETING = 'deleting'
1000 DELETED = 'deleted'
1001 ERROR = 'error'
1002
1003
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001004class BaremetalScenarioTest(ScenarioTest):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001005 @classmethod
1006 def setUpClass(cls):
1007 super(BaremetalScenarioTest, cls).setUpClass()
1008
1009 if (not CONF.service_available.ironic or
1010 not CONF.baremetal.driver_enabled):
1011 msg = 'Ironic not available or Ironic compute driver not enabled'
1012 raise cls.skipException(msg)
1013
1014 # use an admin client manager for baremetal client
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001015 manager = clients.Manager(
1016 credentials=cls.admin_credentials()
1017 )
Adam Gandelman4a48a602014-03-20 18:23:18 -07001018 cls.baremetal_client = manager.baremetal_client
1019
1020 # allow any issues obtaining the node list to raise early
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001021 cls.baremetal_client.list_nodes()
Adam Gandelman4a48a602014-03-20 18:23:18 -07001022
1023 def _node_state_timeout(self, node_id, state_attr,
1024 target_states, timeout=10, interval=1):
1025 if not isinstance(target_states, list):
1026 target_states = [target_states]
1027
1028 def check_state():
1029 node = self.get_node(node_id=node_id)
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001030 if node.get(state_attr) in target_states:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001031 return True
1032 return False
1033
1034 if not tempest.test.call_until_true(
1035 check_state, timeout, interval):
1036 msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
1037 (node_id, state_attr, target_states))
1038 raise exceptions.TimeoutException(msg)
1039
1040 def wait_provisioning_state(self, node_id, state, timeout):
1041 self._node_state_timeout(
1042 node_id=node_id, state_attr='provision_state',
1043 target_states=state, timeout=timeout)
1044
1045 def wait_power_state(self, node_id, state):
1046 self._node_state_timeout(
1047 node_id=node_id, state_attr='power_state',
1048 target_states=state, timeout=CONF.baremetal.power_timeout)
1049
1050 def wait_node(self, instance_id):
1051 """Waits for a node to be associated with instance_id."""
Zhi Kun Liu4a8d1ea2014-04-15 22:08:21 -05001052
Adam Gandelman4a48a602014-03-20 18:23:18 -07001053 def _get_node():
1054 node = None
1055 try:
1056 node = self.get_node(instance_id=instance_id)
Andrea Frittoli2ddc2632014-09-25 11:03:00 +01001057 except exceptions.NotFound:
Adam Gandelman4a48a602014-03-20 18:23:18 -07001058 pass
1059 return node is not None
1060
1061 if not tempest.test.call_until_true(
1062 _get_node, CONF.baremetal.association_timeout, 1):
1063 msg = ('Timed out waiting to get Ironic node by instance id %s'
1064 % instance_id)
1065 raise exceptions.TimeoutException(msg)
1066
1067 def get_node(self, node_id=None, instance_id=None):
1068 if node_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001069 _, body = self.baremetal_client.show_node(node_id)
1070 return body
Adam Gandelman4a48a602014-03-20 18:23:18 -07001071 elif instance_id:
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001072 _, body = self.baremetal_client.show_node_by_instance_uuid(
1073 instance_id)
1074 if body['nodes']:
1075 return body['nodes'][0]
Adam Gandelman4a48a602014-03-20 18:23:18 -07001076
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001077 def get_ports(self, node_uuid):
Adam Gandelman4a48a602014-03-20 18:23:18 -07001078 ports = []
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001079 _, body = self.baremetal_client.list_node_ports(node_uuid)
1080 for port in body['ports']:
1081 _, p = self.baremetal_client.show_port(port['uuid'])
1082 ports.append(p)
Adam Gandelman4a48a602014-03-20 18:23:18 -07001083 return ports
1084
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001085 def add_keypair(self):
1086 self.keypair = self.create_keypair()
1087
1088 def verify_connectivity(self, ip=None):
1089 if ip:
1090 dest = self.get_remote_client(ip)
1091 else:
1092 dest = self.get_remote_client(self.instance)
1093 dest.validate_authentication()
1094
1095 def boot_instance(self):
1096 create_kwargs = {
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001097 'key_name': self.keypair['name']
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001098 }
1099 self.instance = self.create_server(
Matthew Treinishb7144eb2013-12-13 22:57:35 +00001100 wait_on_boot=False, create_kwargs=create_kwargs)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001101
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001102 self.wait_node(self.instance['id'])
1103 self.node = self.get_node(instance_id=self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001104
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001105 self.wait_power_state(self.node['uuid'], BaremetalPowerStates.POWER_ON)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001106
1107 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001108 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001109 [BaremetalProvisionStates.DEPLOYWAIT,
1110 BaremetalProvisionStates.ACTIVE],
1111 timeout=15)
1112
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001113 self.wait_provisioning_state(self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001114 BaremetalProvisionStates.ACTIVE,
1115 timeout=CONF.baremetal.active_timeout)
1116
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001117 self.servers_client.wait_for_server_status(self.instance['id'],
1118 'ACTIVE')
1119 self.node = self.get_node(instance_id=self.instance['id'])
1120 _, self.instance = self.servers_client.get_server(self.instance['id'])
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001121
1122 def terminate_instance(self):
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001123 self.servers_client.delete_server(self.instance['id'])
1124 self.wait_power_state(self.node['uuid'],
1125 BaremetalPowerStates.POWER_OFF)
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001126 self.wait_provisioning_state(
Adam Gandelmanc78c7572014-08-28 18:38:55 -07001127 self.node['uuid'],
David Shrewsbury06f7f8a2014-05-20 13:55:57 -04001128 BaremetalProvisionStates.NOSTATE,
1129 timeout=CONF.baremetal.unprovision_timeout)
1130
Adam Gandelman4a48a602014-03-20 18:23:18 -07001131
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001132class EncryptionScenarioTest(ScenarioTest):
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001133 """
1134 Base class for encryption scenario tests
1135 """
1136
1137 @classmethod
1138 def setUpClass(cls):
1139 super(EncryptionScenarioTest, cls).setUpClass()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001140 cls.admin_volume_types_client = cls.admin_manager.volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001141
1142 def _wait_for_volume_status(self, status):
1143 self.status_timeout(
1144 self.volume_client.volumes, self.volume.id, status)
1145
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001146 def nova_boot(self):
1147 self.keypair = self.create_keypair()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001148 create_kwargs = {'key_name': self.keypair['name']}
1149 self.server = self.create_server(image=self.image,
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001150 create_kwargs=create_kwargs)
1151
1152 def create_volume_type(self, client=None, name=None):
1153 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001154 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001155 if not name:
1156 name = 'generic'
1157 randomized_name = data_utils.rand_name('scenario-type-' + name + '-')
1158 LOG.debug("Creating a volume type: %s", randomized_name)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001159 _, body = client.create_volume_type(
1160 randomized_name)
1161 self.assertIn('id', body)
1162 self.addCleanup(client.delete_volume_type, body['id'])
1163 return body
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001164
1165 def create_encryption_type(self, client=None, type_id=None, provider=None,
1166 key_size=None, cipher=None,
1167 control_location=None):
1168 if not client:
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001169 client = self.admin_volume_types_client
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001170 if not type_id:
1171 volume_type = self.create_volume_type()
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001172 type_id = volume_type['id']
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001173 LOG.debug("Creating an encryption type for volume type: %s", type_id)
Masayuki Igawa1f0ad632014-08-05 13:36:56 +09001174 client.create_encryption_type(
1175 type_id, provider=provider, key_size=key_size, cipher=cipher,
1176 control_location=control_location)
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001177
Kaitlin Farr366a51f2014-04-21 12:43:54 -04001178
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001179class OrchestrationScenarioTest(ScenarioTest):
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001180 """
1181 Base class for orchestration scenario tests
1182 """
1183
1184 @classmethod
Matt Riedemann11c5b642013-08-24 08:45:38 -07001185 def setUpClass(cls):
1186 super(OrchestrationScenarioTest, cls).setUpClass()
Matthew Treinish6c072292014-01-29 19:15:52 +00001187 if not CONF.service_available.heat:
Matt Riedemann11c5b642013-08-24 08:45:38 -07001188 raise cls.skipException("Heat support is required")
1189
1190 @classmethod
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001191 def credentials(cls):
Andrea Frittoli422fbdf2014-03-20 10:05:18 +00001192 admin_creds = auth.get_default_credentials('identity_admin')
1193 creds = auth.get_default_credentials('user')
1194 admin_creds.tenant_name = creds.tenant_name
1195 return admin_creds
Steve Bakerdd7c6ce2013-06-24 14:46:47 +12001196
1197 def _load_template(self, base_file, file_name):
1198 filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
1199 file_name)
1200 with open(filepath) as f:
1201 return f.read()
1202
1203 @classmethod
1204 def _stack_rand_name(cls):
Masayuki Igawa259c1132013-10-31 17:48:44 +09001205 return data_utils.rand_name(cls.__name__ + '-')
Steve Baker80252da2013-09-25 13:29:10 +12001206
1207 @classmethod
1208 def _get_default_network(cls):
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001209 _, networks = cls.networks_client.list_networks()
1210 for net in networks:
1211 if net['label'] == CONF.compute.fixed_network_name:
Steve Baker80252da2013-09-25 13:29:10 +12001212 return net
Steve Baker22c16602014-05-05 13:34:19 +12001213
1214 @staticmethod
1215 def _stack_output(stack, output_key):
1216 """Return a stack output value for a given key."""
Masayuki Igawabc6fe8d2014-08-29 16:50:01 +09001217 return next((o['output_value'] for o in stack['outputs']
Steve Baker22c16602014-05-05 13:34:19 +12001218 if o['output_key'] == output_key), None)
1219
Chris Dent0d494112014-08-26 13:48:30 +01001220
1221class SwiftScenarioTest(ScenarioTest):
1222 """
1223 Provide harness to do Swift scenario tests.
1224
1225 Subclasses implement the tests that use the methods provided by this
1226 class.
1227 """
1228
1229 @classmethod
1230 def setUpClass(cls):
1231 cls.set_network_resources()
1232 super(SwiftScenarioTest, cls).setUpClass()
1233 if not CONF.service_available.swift:
1234 skip_msg = ("%s skipped as swift is not available" %
1235 cls.__name__)
1236 raise cls.skipException(skip_msg)
1237 # Clients for Swift
1238 cls.account_client = cls.manager.account_client
1239 cls.container_client = cls.manager.container_client
1240 cls.object_client = cls.manager.object_client
1241
Chris Dentde456a12014-09-10 12:41:15 +01001242 def get_swift_stat(self):
Chris Dent0d494112014-08-26 13:48:30 +01001243 """get swift status for our user account."""
1244 self.account_client.list_account_containers()
1245 LOG.debug('Swift status information obtained successfully')
1246
Chris Dentde456a12014-09-10 12:41:15 +01001247 def create_container(self, container_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001248 name = container_name or data_utils.rand_name(
1249 'swift-scenario-container')
1250 self.container_client.create_container(name)
1251 # look for the container to assure it is created
Chris Dentde456a12014-09-10 12:41:15 +01001252 self.list_and_check_container_objects(name)
Chris Dent0d494112014-08-26 13:48:30 +01001253 LOG.debug('Container %s created' % (name))
1254 return name
1255
Chris Dentde456a12014-09-10 12:41:15 +01001256 def delete_container(self, container_name):
Chris Dent0d494112014-08-26 13:48:30 +01001257 self.container_client.delete_container(container_name)
1258 LOG.debug('Container %s deleted' % (container_name))
1259
Chris Dentde456a12014-09-10 12:41:15 +01001260 def upload_object_to_container(self, container_name, obj_name=None):
Chris Dent0d494112014-08-26 13:48:30 +01001261 obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
1262 obj_data = data_utils.arbitrary_string()
1263 self.object_client.create_object(container_name, obj_name, obj_data)
1264 return obj_name, obj_data
1265
Chris Dentde456a12014-09-10 12:41:15 +01001266 def delete_object(self, container_name, filename):
Chris Dent0d494112014-08-26 13:48:30 +01001267 self.object_client.delete_object(container_name, filename)
Chris Dentde456a12014-09-10 12:41:15 +01001268 self.list_and_check_container_objects(container_name,
1269 not_present_obj=[filename])
Chris Dent0d494112014-08-26 13:48:30 +01001270
Chris Dentde456a12014-09-10 12:41:15 +01001271 def list_and_check_container_objects(self, container_name,
1272 present_obj=None,
1273 not_present_obj=None):
Chris Dent0d494112014-08-26 13:48:30 +01001274 """
1275 List objects for a given container and assert which are present and
1276 which are not.
1277 """
Ghanshyam2a180b82014-06-16 13:54:22 +09001278 if present_obj is None:
1279 present_obj = []
1280 if not_present_obj is None:
1281 not_present_obj = []
Chris Dent0d494112014-08-26 13:48:30 +01001282 _, object_list = self.container_client.list_container_contents(
1283 container_name)
1284 if present_obj:
1285 for obj in present_obj:
1286 self.assertIn(obj, object_list)
1287 if not_present_obj:
1288 for obj in not_present_obj:
1289 self.assertNotIn(obj, object_list)
1290
Chris Dentde456a12014-09-10 12:41:15 +01001291 def change_container_acl(self, container_name, acl):
Chris Dent0d494112014-08-26 13:48:30 +01001292 metadata_param = {'metadata_prefix': 'x-container-',
1293 'metadata': {'read': acl}}
1294 self.container_client.update_container_metadata(container_name,
1295 **metadata_param)
1296 resp, _ = self.container_client.list_container_metadata(container_name)
1297 self.assertEqual(resp['x-container-read'], acl)
1298
Chris Dentde456a12014-09-10 12:41:15 +01001299 def download_and_verify(self, container_name, obj_name, expected_data):
Chris Dent0d494112014-08-26 13:48:30 +01001300 _, obj = self.object_client.get_object(container_name, obj_name)
1301 self.assertEqual(obj, expected_data)