blob: 043b230b04b078056c00a157f887660b24e90dec [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Attila Fazekasa23f5002012-10-23 19:32:45 +02002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Monty Taylorb2ca5ca2013-04-28 18:00:21 -070016import contextlib
Mitsuhiko Yamazaki46818aa2013-04-18 17:49:17 +090017import logging as orig_logging
Attila Fazekas234d3e82013-02-22 16:39:49 +010018import os
Matthew Treinisha83a16e2012-12-07 13:44:02 -050019import re
Attila Fazekas234d3e82013-02-22 16:39:49 +010020import urlparse
Attila Fazekasa23f5002012-10-23 19:32:45 +020021
Matthew Treinisha83a16e2012-12-07 13:44:02 -050022import boto
Attila Fazekas40aa3612013-01-19 22:16:38 +010023from boto import ec2
24from boto import exception
25from boto import s3
Attila Fazekas234d3e82013-02-22 16:39:49 +010026import keystoneclient.exceptions
Doug Hellmann583ce2c2015-03-11 14:55:46 +000027from oslo_log import log as logging
Matthew Treinish96e9e882014-06-09 18:37:19 -040028import six
Matthew Treinisha83a16e2012-12-07 13:44:02 -050029
Andrea Frittoli32d74992015-03-06 17:01:07 +000030from tempest_lib import exceptions as lib_exc
31
Attila Fazekas234d3e82013-02-22 16:39:49 +010032import tempest.clients
Masayuki Igawa224a8272014-02-17 15:07:43 +090033from tempest.common.utils import file_utils
Sean Dague86bd8422013-12-20 09:56:44 -050034from tempest import config
Attila Fazekas40aa3612013-01-19 22:16:38 +010035from tempest import exceptions
Attila Fazekasdc216422013-01-29 15:12:14 +010036import tempest.test
Masayuki Igawa224a8272014-02-17 15:07:43 +090037from tempest.thirdparty.boto.utils import wait
Matthew Treinisha83a16e2012-12-07 13:44:02 -050038
Sean Dague86bd8422013-12-20 09:56:44 -050039CONF = config.CONF
Attila Fazekasa23f5002012-10-23 19:32:45 +020040LOG = logging.getLogger(__name__)
41
42
Attila Fazekas234d3e82013-02-22 16:39:49 +010043def decision_maker():
44 A_I_IMAGES_READY = True # ari,ami,aki
45 S3_CAN_CONNECT_ERROR = None
46 EC2_CAN_CONNECT_ERROR = None
47 secret_matcher = re.compile("[A-Za-z0-9+/]{32,}") # 40 in other system
48 id_matcher = re.compile("[A-Za-z0-9]{20,}")
49
50 def all_read(*args):
Masayuki Igawa224a8272014-02-17 15:07:43 +090051 return all(map(file_utils.have_effective_read_access, args))
Attila Fazekas234d3e82013-02-22 16:39:49 +010052
Sean Dague86bd8422013-12-20 09:56:44 -050053 materials_path = CONF.boto.s3_materials_path
54 ami_path = materials_path + os.sep + CONF.boto.ami_manifest
55 aki_path = materials_path + os.sep + CONF.boto.aki_manifest
56 ari_path = materials_path + os.sep + CONF.boto.ari_manifest
Attila Fazekas234d3e82013-02-22 16:39:49 +010057
58 A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
59 boto_logger = logging.getLogger('boto')
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040060 level = boto_logger.logger.level
Matthew Treinishc795b9e2014-06-09 17:01:10 -040061 # suppress logging for boto
62 boto_logger.logger.setLevel(orig_logging.CRITICAL)
Attila Fazekas234d3e82013-02-22 16:39:49 +010063
64 def _cred_sub_check(connection_data):
65 if not id_matcher.match(connection_data["aws_access_key_id"]):
66 raise Exception("Invalid AWS access Key")
67 if not secret_matcher.match(connection_data["aws_secret_access_key"]):
68 raise Exception("Invalid AWS secret Key")
69 raise Exception("Unknown (Authentication?) Error")
Andrea Frittoli32d74992015-03-06 17:01:07 +000070 # NOTE(andreaf) Setting up an extra manager here is redundant,
71 # and should be removed.
Attila Fazekas234d3e82013-02-22 16:39:49 +010072 openstack = tempest.clients.Manager()
73 try:
Sean Dague86bd8422013-12-20 09:56:44 -050074 if urlparse.urlparse(CONF.boto.ec2_url).hostname is None:
Attila Fazekas234d3e82013-02-22 16:39:49 +010075 raise Exception("Failed to get hostname from the ec2_url")
76 ec2client = openstack.ec2api_client
77 try:
78 ec2client.get_all_regions()
79 except exception.BotoServerError as exc:
80 if exc.error_code is None:
81 raise Exception("EC2 target does not looks EC2 service")
82 _cred_sub_check(ec2client.connection_data)
83
Andrea Frittoli32d74992015-03-06 17:01:07 +000084 except lib_exc.Unauthorized:
Attila Fazekas234d3e82013-02-22 16:39:49 +010085 EC2_CAN_CONNECT_ERROR = "AWS credentials not set," +\
Matt Riedemann78b0f802015-02-09 14:06:07 -080086 " failed to get them even by keystoneclient"
Attila Fazekas234d3e82013-02-22 16:39:49 +010087 except Exception as exc:
88 EC2_CAN_CONNECT_ERROR = str(exc)
89
90 try:
Sean Dague86bd8422013-12-20 09:56:44 -050091 if urlparse.urlparse(CONF.boto.s3_url).hostname is None:
Attila Fazekas234d3e82013-02-22 16:39:49 +010092 raise Exception("Failed to get hostname from the s3_url")
93 s3client = openstack.s3_client
94 try:
95 s3client.get_bucket("^INVALID*#()@INVALID.")
96 except exception.BotoServerError as exc:
97 if exc.status == 403:
98 _cred_sub_check(s3client.connection_data)
99 except Exception as exc:
100 S3_CAN_CONNECT_ERROR = str(exc)
101 except keystoneclient.exceptions.Unauthorized:
102 S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\
Matt Riedemann78b0f802015-02-09 14:06:07 -0800103 " failed to get them even by keystoneclient"
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -0400104 boto_logger.logger.setLevel(level)
Attila Fazekas234d3e82013-02-22 16:39:49 +0100105 return {'A_I_IMAGES_READY': A_I_IMAGES_READY,
106 'S3_CAN_CONNECT_ERROR': S3_CAN_CONNECT_ERROR,
107 'EC2_CAN_CONNECT_ERROR': EC2_CAN_CONNECT_ERROR}
108
109
Attila Fazekasa23f5002012-10-23 19:32:45 +0200110class BotoExceptionMatcher(object):
111 STATUS_RE = r'[45]\d\d'
112 CODE_RE = '.*' # regexp makes sense in group match
113
114 def match(self, exc):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700115 """:returns: Returns with an error string if it does not match,
116 returns with None when it matches.
Attila Fazekas55c597d2014-02-21 13:10:41 +0100117 """
Attila Fazekas40aa3612013-01-19 22:16:38 +0100118 if not isinstance(exc, exception.BotoServerError):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200119 return "%r not an BotoServerError instance" % exc
120 LOG.info("Status: %s , error_code: %s", exc.status, exc.error_code)
121 if re.match(self.STATUS_RE, str(exc.status)) is None:
122 return ("Status code (%s) does not match"
123 "the expected re pattern \"%s\""
124 % (exc.status, self.STATUS_RE))
125 if re.match(self.CODE_RE, str(exc.error_code)) is None:
126 return ("Error code (%s) does not match" +
127 "the expected re pattern \"%s\"") %\
128 (exc.error_code, self.CODE_RE)
Attila Fazekas55c597d2014-02-21 13:10:41 +0100129 return None
Attila Fazekasa23f5002012-10-23 19:32:45 +0200130
131
132class ClientError(BotoExceptionMatcher):
133 STATUS_RE = r'4\d\d'
134
135
136class ServerError(BotoExceptionMatcher):
137 STATUS_RE = r'5\d\d'
138
139
140def _add_matcher_class(error_cls, error_data, base=BotoExceptionMatcher):
141 """
142 Usable for adding an ExceptionMatcher(s) into the exception tree.
143 The not leaf elements does wildcard match
144 """
145 # in error_code just literal and '.' characters expected
llg821243b20502014-02-22 10:32:49 +0800146 if not isinstance(error_data, six.string_types):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200147 (error_code, status_code) = map(str, error_data)
148 else:
149 status_code = None
150 error_code = error_data
151 parts = error_code.split('.')
152 basematch = ""
153 num_parts = len(parts)
154 max_index = num_parts - 1
155 add_cls = error_cls
llg821243b20502014-02-22 10:32:49 +0800156 for i_part in six.moves.xrange(num_parts):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200157 part = parts[i_part]
158 leaf = i_part == max_index
159 if not leaf:
160 match = basematch + part + "[.].*"
161 else:
162 match = basematch + part
163
164 basematch += part + "[.]"
165 if not hasattr(add_cls, part):
166 cls_dict = {"CODE_RE": match}
167 if leaf and status_code is not None:
168 cls_dict["STATUS_RE"] = status_code
169 cls = type(part, (base, ), cls_dict)
170 setattr(add_cls, part, cls())
171 add_cls = cls
172 elif leaf:
173 raise LookupError("Tries to redefine an error code \"%s\"" % part)
174 else:
175 add_cls = getattr(add_cls, part)
176
177
Attila Fazekas3e381f72013-08-01 16:52:23 +0200178# TODO(afazekas): classmethod handling
Attila Fazekasa23f5002012-10-23 19:32:45 +0200179def friendly_function_name_simple(call_able):
180 name = ""
181 if hasattr(call_able, "im_class"):
182 name += call_able.im_class.__name__ + "."
183 name += call_able.__name__
184 return name
185
186
187def friendly_function_call_str(call_able, *args, **kwargs):
188 string = friendly_function_name_simple(call_able)
189 string += "(" + ", ".join(map(str, args))
190 if len(kwargs):
191 if len(args):
192 string += ", "
193 string += ", ".join("=".join(map(str, (key, value)))
Matthew Treinish1d14c542014-06-17 20:25:40 -0400194 for (key, value) in kwargs.items())
Attila Fazekasa23f5002012-10-23 19:32:45 +0200195 return string + ")"
196
197
Attila Fazekasdc216422013-01-29 15:12:14 +0100198class BotoTestCase(tempest.test.BaseTestCase):
Sean Daguef237ccb2013-01-04 15:19:14 -0500199 """Recommended to use as base class for boto related test."""
Chris Yeoh8a79b9d2013-01-18 19:32:47 +1030200
Attila Fazekasa23f5002012-10-23 19:32:45 +0200201 @classmethod
Matthew Treinishdfd7ac02015-02-09 17:47:31 -0500202 def skip_checks(cls):
203 super(BotoTestCase, cls).skip_checks()
204 if not CONF.compute_feature_enabled.ec2_api:
205 raise cls.skipException("The EC2 API is not available")
206
207 @classmethod
Emily Hugenbruche252a4a2015-02-27 15:43:12 +0000208 def setup_credentials(cls):
209 super(BotoTestCase, cls).setup_credentials()
210 cls.os = cls.get_client_manager()
211
212 @classmethod
Andrea Frittoli29fea352014-09-15 13:31:14 +0100213 def resource_setup(cls):
214 super(BotoTestCase, cls).resource_setup()
Matthew Treinishf054a9c2013-10-28 21:34:47 -0400215 cls.conclusion = decision_maker()
Attila Fazekasa23f5002012-10-23 19:32:45 +0200216 # The trash contains cleanup functions and paramaters in tuples
217 # (function, *args, **kwargs)
218 cls._resource_trash_bin = {}
219 cls._sequence = -1
220 if (hasattr(cls, "EC2") and
Attila Fazekas234d3e82013-02-22 16:39:49 +0100221 cls.conclusion['EC2_CAN_CONNECT_ERROR'] is not None):
ivan-zhu1feeb382013-01-24 10:14:39 +0800222 raise cls.skipException("EC2 " + cls.__name__ + ": " +
Attila Fazekas234d3e82013-02-22 16:39:49 +0100223 cls.conclusion['EC2_CAN_CONNECT_ERROR'])
Attila Fazekasa23f5002012-10-23 19:32:45 +0200224 if (hasattr(cls, "S3") and
Attila Fazekas234d3e82013-02-22 16:39:49 +0100225 cls.conclusion['S3_CAN_CONNECT_ERROR'] is not None):
ivan-zhu1feeb382013-01-24 10:14:39 +0800226 raise cls.skipException("S3 " + cls.__name__ + ": " +
Attila Fazekas234d3e82013-02-22 16:39:49 +0100227 cls.conclusion['S3_CAN_CONNECT_ERROR'])
Attila Fazekasa23f5002012-10-23 19:32:45 +0200228
229 @classmethod
230 def addResourceCleanUp(cls, function, *args, **kwargs):
231 """Adds CleanUp callable, used by tearDownClass.
Attila Fazekasb2902af2013-02-16 16:22:44 +0100232 Recommended to a use (deep)copy on the mutable args.
233 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200234 cls._sequence = cls._sequence + 1
235 cls._resource_trash_bin[cls._sequence] = (function, args, kwargs)
236 return cls._sequence
237
238 @classmethod
239 def cancelResourceCleanUp(cls, key):
Sean Daguef237ccb2013-01-04 15:19:14 -0500240 """Cancel Clean up request."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200241 del cls._resource_trash_bin[key]
242
Attila Fazekas3e381f72013-08-01 16:52:23 +0200243 # TODO(afazekas): Add "with" context handling
Attila Fazekasa23f5002012-10-23 19:32:45 +0200244 def assertBotoError(self, excMatcher, callableObj,
245 *args, **kwargs):
246 """Example usage:
247 self.assertBotoError(self.ec2_error_code.client.
248 InvalidKeyPair.Duplicate,
249 self.client.create_keypair,
Attila Fazekasb2902af2013-02-16 16:22:44 +0100250 key_name)
251 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200252 try:
253 callableObj(*args, **kwargs)
Attila Fazekas40aa3612013-01-19 22:16:38 +0100254 except exception.BotoServerError as exc:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200255 error_msg = excMatcher.match(exc)
256 if error_msg is not None:
257 raise self.failureException, error_msg
258 else:
259 raise self.failureException, "BotoServerError not raised"
260
261 @classmethod
Andrea Frittoli29fea352014-09-15 13:31:14 +0100262 def resource_cleanup(cls):
Attila Fazekasb2902af2013-02-16 16:22:44 +0100263 """Calls the callables added by addResourceCleanUp,
Yair Frieda039f872014-01-02 12:11:10 +0200264 when you overwrite this function don't forget to call this too.
Attila Fazekasb2902af2013-02-16 16:22:44 +0100265 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200266 fail_count = 0
267 trash_keys = sorted(cls._resource_trash_bin, reverse=True)
268 for key in trash_keys:
269 (function, pos_args, kw_args) = cls._resource_trash_bin[key]
270 try:
Yair Frieda039f872014-01-02 12:11:10 +0200271 func_name = friendly_function_call_str(function, *pos_args,
272 **kw_args)
273 LOG.debug("Cleaning up: %s" % func_name)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200274 function(*pos_args, **kw_args)
Yair Frieda039f872014-01-02 12:11:10 +0200275 except BaseException:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200276 fail_count += 1
Yair Frieda039f872014-01-02 12:11:10 +0200277 LOG.exception("Cleanup failed %s" % func_name)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200278 finally:
279 del cls._resource_trash_bin[key]
Matthew Treinish8004e8c2014-01-27 23:03:14 +0000280 cls.clear_isolated_creds()
Andrea Frittoli29fea352014-09-15 13:31:14 +0100281 super(BotoTestCase, cls).resource_cleanup()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200282 # NOTE(afazekas): let the super called even on exceptions
283 # The real exceptions already logged, if the super throws another,
284 # does not causes hidden issues
Attila Fazekasa23f5002012-10-23 19:32:45 +0200285 if fail_count:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100286 raise exceptions.TearDownException(num=fail_count)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200287
288 ec2_error_code = BotoExceptionMatcher()
289 # InsufficientInstanceCapacity can be both server and client error
290 ec2_error_code.server = ServerError()
291 ec2_error_code.client = ClientError()
292 s3_error_code = BotoExceptionMatcher()
293 s3_error_code.server = ServerError()
294 s3_error_code.client = ClientError()
295 valid_image_state = set(('available', 'pending', 'failed'))
Attila Fazekas3e381f72013-08-01 16:52:23 +0200296 # NOTE(afazekas): 'paused' is not valid status in EC2, but it does not have
Attila Fazekasc66ee652013-01-31 06:56:13 +0100297 # a good mapping, because it uses memory, but not really a running machine
Attila Fazekasa23f5002012-10-23 19:32:45 +0200298 valid_instance_state = set(('pending', 'running', 'shutting-down',
Attila Fazekasc66ee652013-01-31 06:56:13 +0100299 'terminated', 'stopping', 'stopped', 'paused'))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200300 valid_volume_status = set(('creating', 'available', 'in-use',
301 'deleting', 'deleted', 'error'))
302 valid_snapshot_status = set(('pending', 'completed', 'error'))
303
Attila Fazekas37f83042013-01-12 16:13:03 +0100304 gone_set = set(('_GONE',))
305
Attila Fazekas40aa3612013-01-19 22:16:38 +0100306 @classmethod
307 def get_lfunction_gone(cls, obj):
Sean Dague2416cf32013-04-10 08:29:07 -0400308 """If the object is instance of a well know type returns back with
Attila Fazekas40aa3612013-01-19 22:16:38 +0100309 with the correspoding function otherwise it assumes the obj itself
Sean Dague2416cf32013-04-10 08:29:07 -0400310 is the function.
311 """
Attila Fazekas40aa3612013-01-19 22:16:38 +0100312 ec = cls.ec2_error_code
313 if isinstance(obj, ec2.instance.Instance):
314 colusure_matcher = ec.client.InvalidInstanceID.NotFound
315 status_attr = "state"
316 elif isinstance(obj, ec2.image.Image):
317 colusure_matcher = ec.client.InvalidAMIID.NotFound
318 status_attr = "state"
319 elif isinstance(obj, ec2.snapshot.Snapshot):
320 colusure_matcher = ec.client.InvalidSnapshot.NotFound
321 status_attr = "status"
322 elif isinstance(obj, ec2.volume.Volume):
323 colusure_matcher = ec.client.InvalidVolume.NotFound
324 status_attr = "status"
325 else:
326 return obj
327
328 def _status():
329 try:
330 obj.update(validate=True)
331 except ValueError:
332 return "_GONE"
333 except exception.EC2ResponseError as exc:
Attila Fazekas55c597d2014-02-21 13:10:41 +0100334 if colusure_matcher.match(exc) is None:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100335 return "_GONE"
336 else:
337 raise
338 return getattr(obj, status_attr)
339
340 return _status
341
Attila Fazekas37f83042013-01-12 16:13:03 +0100342 def state_wait_gone(self, lfunction, final_set, valid_set):
343 if not isinstance(final_set, set):
344 final_set = set((final_set,))
345 final_set |= self.gone_set
Attila Fazekas40aa3612013-01-19 22:16:38 +0100346 lfunction = self.get_lfunction_gone(lfunction)
Masayuki Igawa224a8272014-02-17 15:07:43 +0900347 state = wait.state_wait(lfunction, final_set, valid_set)
Attila Fazekas37f83042013-01-12 16:13:03 +0100348 self.assertIn(state, valid_set | self.gone_set)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200349 return state
350
Attila Fazekas37f83042013-01-12 16:13:03 +0100351 def waitImageState(self, lfunction, wait_for):
352 return self.state_wait_gone(lfunction, wait_for,
353 self.valid_image_state)
354
Attila Fazekasa23f5002012-10-23 19:32:45 +0200355 def waitInstanceState(self, lfunction, wait_for):
Attila Fazekas37f83042013-01-12 16:13:03 +0100356 return self.state_wait_gone(lfunction, wait_for,
357 self.valid_instance_state)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200358
Attila Fazekasa23f5002012-10-23 19:32:45 +0200359 def waitSnapshotStatus(self, lfunction, wait_for):
Attila Fazekas37f83042013-01-12 16:13:03 +0100360 return self.state_wait_gone(lfunction, wait_for,
361 self.valid_snapshot_status)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200362
Attila Fazekas40aa3612013-01-19 22:16:38 +0100363 def waitVolumeStatus(self, lfunction, wait_for):
364 return self.state_wait_gone(lfunction, wait_for,
365 self.valid_volume_status)
366
Attila Fazekasa23f5002012-10-23 19:32:45 +0200367 def assertImageStateWait(self, lfunction, wait_for):
368 state = self.waitImageState(lfunction, wait_for)
369 self.assertIn(state, wait_for)
370
371 def assertInstanceStateWait(self, lfunction, wait_for):
372 state = self.waitInstanceState(lfunction, wait_for)
373 self.assertIn(state, wait_for)
374
375 def assertVolumeStatusWait(self, lfunction, wait_for):
376 state = self.waitVolumeStatus(lfunction, wait_for)
377 self.assertIn(state, wait_for)
378
379 def assertSnapshotStatusWait(self, lfunction, wait_for):
380 state = self.waitSnapshotStatus(lfunction, wait_for)
381 self.assertIn(state, wait_for)
382
383 def assertAddressDissasociatedWait(self, address):
384
385 def _disassociate():
386 cli = self.ec2_client
387 addresses = cli.get_all_addresses(addresses=(address.public_ip,))
388 if len(addresses) != 1:
389 return "INVALID"
390 if addresses[0].instance_id:
391 LOG.info("%s associated to %s",
392 address.public_ip,
393 addresses[0].instance_id)
394 return "ASSOCIATED"
395 return "DISASSOCIATED"
396
Masayuki Igawa224a8272014-02-17 15:07:43 +0900397 state = wait.state_wait(_disassociate, "DISASSOCIATED",
398 set(("ASSOCIATED", "DISASSOCIATED")))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200399 self.assertEqual(state, "DISASSOCIATED")
400
401 def assertAddressReleasedWait(self, address):
402
403 def _address_delete():
Attila Fazekas3e381f72013-08-01 16:52:23 +0200404 # NOTE(afazekas): the filter gives back IP
Attila Fazekasa23f5002012-10-23 19:32:45 +0200405 # even if it is not associated to my tenant
406 if (address.public_ip not in map(lambda a: a.public_ip,
407 self.ec2_client.get_all_addresses())):
408 return "DELETED"
409 return "NOTDELETED"
410
Masayuki Igawa224a8272014-02-17 15:07:43 +0900411 state = wait.state_wait(_address_delete, "DELETED")
Attila Fazekasa23f5002012-10-23 19:32:45 +0200412 self.assertEqual(state, "DELETED")
413
414 def assertReSearch(self, regexp, string):
415 if re.search(regexp, string) is None:
416 raise self.failureException("regexp: '%s' not found in '%s'" %
417 (regexp, string))
418
419 def assertNotReSearch(self, regexp, string):
420 if re.search(regexp, string) is not None:
421 raise self.failureException("regexp: '%s' found in '%s'" %
422 (regexp, string))
423
424 def assertReMatch(self, regexp, string):
425 if re.match(regexp, string) is None:
426 raise self.failureException("regexp: '%s' not matches on '%s'" %
427 (regexp, string))
428
429 def assertNotReMatch(self, regexp, string):
430 if re.match(regexp, string) is not None:
431 raise self.failureException("regexp: '%s' matches on '%s'" %
432 (regexp, string))
433
434 @classmethod
435 def destroy_bucket(cls, connection_data, bucket):
Sean Daguef237ccb2013-01-04 15:19:14 -0500436 """Destroys the bucket and its content, just for teardown."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200437 exc_num = 0
438 try:
Monty Taylorb2ca5ca2013-04-28 18:00:21 -0700439 with contextlib.closing(
440 boto.connect_s3(**connection_data)) as conn:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200441 if isinstance(bucket, basestring):
442 bucket = conn.lookup(bucket)
Attila Fazekas40aa3612013-01-19 22:16:38 +0100443 assert isinstance(bucket, s3.bucket.Bucket)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200444 for obj in bucket.list():
445 try:
446 bucket.delete_key(obj.key)
447 obj.close()
Yair Frieda039f872014-01-02 12:11:10 +0200448 except BaseException:
449 LOG.exception("Failed to delete key %s " % obj.key)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200450 exc_num += 1
451 conn.delete_bucket(bucket)
Yair Frieda039f872014-01-02 12:11:10 +0200452 except BaseException:
453 LOG.exception("Failed to destroy bucket %s " % bucket)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200454 exc_num += 1
455 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100456 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200457
458 @classmethod
459 def destroy_reservation(cls, reservation):
Sean Daguef237ccb2013-01-04 15:19:14 -0500460 """Terminate instances in a reservation, just for teardown."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200461 exc_num = 0
462
463 def _instance_state():
464 try:
465 instance.update(validate=True)
466 except ValueError:
Attila Fazekas37f83042013-01-12 16:13:03 +0100467 return "_GONE"
Attila Fazekas40aa3612013-01-19 22:16:38 +0100468 except exception.EC2ResponseError as exc:
Attila Fazekas37f83042013-01-12 16:13:03 +0100469 if cls.ec2_error_code.\
Attila Fazekas55c597d2014-02-21 13:10:41 +0100470 client.InvalidInstanceID.NotFound.match(exc) is None:
Attila Fazekas37f83042013-01-12 16:13:03 +0100471 return "_GONE"
Attila Fazekas3e381f72013-08-01 16:52:23 +0200472 # NOTE(afazekas): incorrect code,
Attila Fazekas37f83042013-01-12 16:13:03 +0100473 # but the resource must be destoreyd
474 if exc.error_code == "InstanceNotFound":
475 return "_GONE"
476
Attila Fazekasa23f5002012-10-23 19:32:45 +0200477 return instance.state
478
479 for instance in reservation.instances:
480 try:
481 instance.terminate()
Masayuki Igawa224a8272014-02-17 15:07:43 +0900482 wait.re_search_wait(_instance_state, "_GONE")
Yair Frieda039f872014-01-02 12:11:10 +0200483 except BaseException:
484 LOG.exception("Failed to terminate instance %s " % instance)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200485 exc_num += 1
486 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100487 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200488
Attila Fazekas3e381f72013-08-01 16:52:23 +0200489 # NOTE(afazekas): The incorrect ErrorCodes makes very, very difficult
Attila Fazekasa23f5002012-10-23 19:32:45 +0200490 # to write better teardown
491
492 @classmethod
493 def destroy_security_group_wait(cls, group):
494 """Delete group.
495 Use just for teardown!
496 """
Attila Fazekas3e381f72013-08-01 16:52:23 +0200497 # NOTE(afazekas): should wait/try until all related instance terminates
Attila Fazekasa23f5002012-10-23 19:32:45 +0200498 group.delete()
499
500 @classmethod
501 def destroy_volume_wait(cls, volume):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700502 """Delete volume, tries to detach first.
Attila Fazekasa23f5002012-10-23 19:32:45 +0200503 Use just for teardown!
504 """
505 exc_num = 0
506 snaps = volume.snapshots()
507 if len(snaps):
508 LOG.critical("%s Volume has %s snapshot(s)", volume.id,
Attila Fazekasfa756cb2013-02-12 10:52:42 +0100509 map(snaps.id, snaps))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200510
Attila Fazekas3e381f72013-08-01 16:52:23 +0200511 # NOTE(afazekas): detaching/attching not valid EC2 status
Attila Fazekasa23f5002012-10-23 19:32:45 +0200512 def _volume_state():
513 volume.update(validate=True)
514 try:
ghanshyam308640c2014-10-14 17:23:31 +0900515 # NOTE(gmann): Make sure volume is attached.
516 # Checking status as 'not "available"' is not enough to make
517 # sure volume is attached as it can be in "error" state
518 if volume.status == "in-use":
Attila Fazekasa23f5002012-10-23 19:32:45 +0200519 volume.detach(force=True)
Yair Frieda039f872014-01-02 12:11:10 +0200520 except BaseException:
521 LOG.exception("Failed to detach volume %s" % volume)
Attila Fazekas3e381f72013-08-01 16:52:23 +0200522 # exc_num += 1 "nonlocal" not in python2
Attila Fazekasa23f5002012-10-23 19:32:45 +0200523 return volume.status
524
525 try:
Masayuki Igawa224a8272014-02-17 15:07:43 +0900526 wait.re_search_wait(_volume_state, "available")
527 # not validates status
Attila Fazekasa23f5002012-10-23 19:32:45 +0200528 LOG.info(_volume_state())
529 volume.delete()
Yair Frieda039f872014-01-02 12:11:10 +0200530 except BaseException:
531 LOG.exception("Failed to delete volume %s" % volume)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200532 exc_num += 1
533 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100534 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200535
536 @classmethod
537 def destroy_snapshot_wait(cls, snapshot):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700538 """delete snapshot, wait until it ceases to exist."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200539 snapshot.delete()
540
541 def _update():
542 snapshot.update(validate=True)
543
Masayuki Igawa224a8272014-02-17 15:07:43 +0900544 wait.wait_exception(_update)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200545
546
547# you can specify tuples if you want to specify the status pattern
548for code in ('AddressLimitExceeded', 'AttachmentLimitExceeded', 'AuthFailure',
549 'Blocked', 'CustomerGatewayLimitExceeded', 'DependencyViolation',
550 'DiskImageSizeTooLarge', 'FilterLimitExceeded',
551 'Gateway.NotAttached', 'IdempotentParameterMismatch',
552 'IncorrectInstanceState', 'IncorrectState',
553 'InstanceLimitExceeded', 'InsufficientInstanceCapacity',
554 'InsufficientReservedInstancesCapacity',
555 'InternetGatewayLimitExceeded', 'InvalidAMIAttributeItemValue',
556 'InvalidAMIID.Malformed', 'InvalidAMIID.NotFound',
557 'InvalidAMIID.Unavailable', 'InvalidAssociationID.NotFound',
558 'InvalidAttachment.NotFound', 'InvalidConversionTaskId',
559 'InvalidCustomerGateway.DuplicateIpAddress',
560 'InvalidCustomerGatewayID.NotFound', 'InvalidDevice.InUse',
561 'InvalidDhcpOptionsID.NotFound', 'InvalidFormat',
562 'InvalidFilter', 'InvalidGatewayID.NotFound',
563 'InvalidGroup.Duplicate', 'InvalidGroupId.Malformed',
564 'InvalidGroup.InUse', 'InvalidGroup.NotFound',
565 'InvalidGroup.Reserved', 'InvalidInstanceID.Malformed',
566 'InvalidInstanceID.NotFound',
567 'InvalidInternetGatewayID.NotFound', 'InvalidIPAddress.InUse',
568 'InvalidKeyPair.Duplicate', 'InvalidKeyPair.Format',
569 'InvalidKeyPair.NotFound', 'InvalidManifest',
570 'InvalidNetworkAclEntry.NotFound',
571 'InvalidNetworkAclID.NotFound', 'InvalidParameterCombination',
572 'InvalidParameterValue', 'InvalidPermission.Duplicate',
573 'InvalidPermission.Malformed', 'InvalidReservationID.Malformed',
574 'InvalidReservationID.NotFound', 'InvalidRoute.NotFound',
575 'InvalidRouteTableID.NotFound',
576 'InvalidSecurity.RequestHasExpired',
577 'InvalidSnapshotID.Malformed', 'InvalidSnapshot.NotFound',
578 'InvalidUserID.Malformed', 'InvalidReservedInstancesId',
579 'InvalidReservedInstancesOfferingId',
580 'InvalidSubnetID.NotFound', 'InvalidVolumeID.Duplicate',
581 'InvalidVolumeID.Malformed', 'InvalidVolumeID.ZoneMismatch',
582 'InvalidVolume.NotFound', 'InvalidVpcID.NotFound',
583 'InvalidVpnConnectionID.NotFound',
584 'InvalidVpnGatewayID.NotFound',
585 'InvalidZone.NotFound', 'LegacySecurityGroup',
586 'MissingParameter', 'NetworkAclEntryAlreadyExists',
587 'NetworkAclEntryLimitExceeded', 'NetworkAclLimitExceeded',
588 'NonEBSInstance', 'PendingSnapshotLimitExceeded',
589 'PendingVerification', 'OptInRequired', 'RequestLimitExceeded',
590 'ReservedInstancesLimitExceeded', 'Resource.AlreadyAssociated',
591 'ResourceLimitExceeded', 'RouteAlreadyExists',
592 'RouteLimitExceeded', 'RouteTableLimitExceeded',
593 'RulesPerSecurityGroupLimitExceeded',
594 'SecurityGroupLimitExceeded',
595 'SecurityGroupsPerInstanceLimitExceeded',
596 'SnapshotLimitExceeded', 'SubnetLimitExceeded',
597 'UnknownParameter', 'UnsupportedOperation',
598 'VolumeLimitExceeded', 'VpcLimitExceeded',
599 'VpnConnectionLimitExceeded',
600 'VpnGatewayAttachmentLimitExceeded', 'VpnGatewayLimitExceeded'):
601 _add_matcher_class(BotoTestCase.ec2_error_code.client,
602 code, base=ClientError)
603
604for code in ('InsufficientAddressCapacity', 'InsufficientInstanceCapacity',
605 'InsufficientReservedInstanceCapacity', 'InternalError',
606 'Unavailable'):
607 _add_matcher_class(BotoTestCase.ec2_error_code.server,
608 code, base=ServerError)
609
610
611for code in (('AccessDenied', 403),
Matthew Treinish1d14c542014-06-17 20:25:40 -0400612 ('AccountProblem', 403),
613 ('AmbiguousGrantByEmailAddress', 400),
614 ('BadDigest', 400),
615 ('BucketAlreadyExists', 409),
616 ('BucketAlreadyOwnedByYou', 409),
617 ('BucketNotEmpty', 409),
618 ('CredentialsNotSupported', 400),
619 ('CrossLocationLoggingProhibited', 403),
620 ('EntityTooSmall', 400),
621 ('EntityTooLarge', 400),
622 ('ExpiredToken', 400),
623 ('IllegalVersioningConfigurationException', 400),
624 ('IncompleteBody', 400),
625 ('IncorrectNumberOfFilesInPostRequest', 400),
626 ('InlineDataTooLarge', 400),
627 ('InvalidAccessKeyId', 403),
Attila Fazekasa23f5002012-10-23 19:32:45 +0200628 'InvalidAddressingHeader',
Matthew Treinish1d14c542014-06-17 20:25:40 -0400629 ('InvalidArgument', 400),
630 ('InvalidBucketName', 400),
631 ('InvalidBucketState', 409),
632 ('InvalidDigest', 400),
633 ('InvalidLocationConstraint', 400),
634 ('InvalidPart', 400),
635 ('InvalidPartOrder', 400),
636 ('InvalidPayer', 403),
637 ('InvalidPolicyDocument', 400),
638 ('InvalidRange', 416),
639 ('InvalidRequest', 400),
640 ('InvalidSecurity', 403),
641 ('InvalidSOAPRequest', 400),
642 ('InvalidStorageClass', 400),
643 ('InvalidTargetBucketForLogging', 400),
644 ('InvalidToken', 400),
645 ('InvalidURI', 400),
646 ('KeyTooLong', 400),
647 ('MalformedACLError', 400),
648 ('MalformedPOSTRequest', 400),
649 ('MalformedXML', 400),
650 ('MaxMessageLengthExceeded', 400),
651 ('MaxPostPreDataLengthExceededError', 400),
652 ('MetadataTooLarge', 400),
653 ('MethodNotAllowed', 405),
654 ('MissingAttachment'),
655 ('MissingContentLength', 411),
656 ('MissingRequestBodyError', 400),
657 ('MissingSecurityElement', 400),
658 ('MissingSecurityHeader', 400),
659 ('NoLoggingStatusForKey', 400),
660 ('NoSuchBucket', 404),
661 ('NoSuchKey', 404),
662 ('NoSuchLifecycleConfiguration', 404),
663 ('NoSuchUpload', 404),
664 ('NoSuchVersion', 404),
665 ('NotSignedUp', 403),
666 ('NotSuchBucketPolicy', 404),
667 ('OperationAborted', 409),
668 ('PermanentRedirect', 301),
669 ('PreconditionFailed', 412),
670 ('Redirect', 307),
671 ('RequestIsNotMultiPartContent', 400),
672 ('RequestTimeout', 400),
673 ('RequestTimeTooSkewed', 403),
674 ('RequestTorrentOfBucketError', 400),
675 ('SignatureDoesNotMatch', 403),
676 ('TemporaryRedirect', 307),
677 ('TokenRefreshRequired', 400),
678 ('TooManyBuckets', 400),
679 ('UnexpectedContent', 400),
680 ('UnresolvableGrantByEmailAddress', 400),
681 ('UserKeyMustBeSpecified', 400)):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200682 _add_matcher_class(BotoTestCase.s3_error_code.client,
683 code, base=ClientError)
684
685
686for code in (('InternalError', 500),
Matthew Treinish1d14c542014-06-17 20:25:40 -0400687 ('NotImplemented', 501),
688 ('ServiceUnavailable', 503),
689 ('SlowDown', 503)):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200690 _add_matcher_class(BotoTestCase.s3_error_code.server,
691 code, base=ServerError)