blob: 5b2ed70b4a2365b158abc5038f7495ce051b651e [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
Matthew Treinish96e9e882014-06-09 18:37:19 -040027import six
Matthew Treinisha83a16e2012-12-07 13:44:02 -050028
Attila Fazekas234d3e82013-02-22 16:39:49 +010029import tempest.clients
Masayuki Igawa224a8272014-02-17 15:07:43 +090030from tempest.common.utils import file_utils
Sean Dague86bd8422013-12-20 09:56:44 -050031from tempest import config
Attila Fazekas40aa3612013-01-19 22:16:38 +010032from tempest import exceptions
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040033from tempest.openstack.common import log as logging
Attila Fazekasdc216422013-01-29 15:12:14 +010034import tempest.test
Masayuki Igawa224a8272014-02-17 15:07:43 +090035from tempest.thirdparty.boto.utils import wait
Matthew Treinisha83a16e2012-12-07 13:44:02 -050036
Sean Dague86bd8422013-12-20 09:56:44 -050037CONF = config.CONF
Attila Fazekasa23f5002012-10-23 19:32:45 +020038LOG = logging.getLogger(__name__)
39
40
Attila Fazekas234d3e82013-02-22 16:39:49 +010041def decision_maker():
42 A_I_IMAGES_READY = True # ari,ami,aki
43 S3_CAN_CONNECT_ERROR = None
44 EC2_CAN_CONNECT_ERROR = None
45 secret_matcher = re.compile("[A-Za-z0-9+/]{32,}") # 40 in other system
46 id_matcher = re.compile("[A-Za-z0-9]{20,}")
47
48 def all_read(*args):
Masayuki Igawa224a8272014-02-17 15:07:43 +090049 return all(map(file_utils.have_effective_read_access, args))
Attila Fazekas234d3e82013-02-22 16:39:49 +010050
Sean Dague86bd8422013-12-20 09:56:44 -050051 materials_path = CONF.boto.s3_materials_path
52 ami_path = materials_path + os.sep + CONF.boto.ami_manifest
53 aki_path = materials_path + os.sep + CONF.boto.aki_manifest
54 ari_path = materials_path + os.sep + CONF.boto.ari_manifest
Attila Fazekas234d3e82013-02-22 16:39:49 +010055
56 A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
57 boto_logger = logging.getLogger('boto')
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040058 level = boto_logger.logger.level
Matthew Treinishc795b9e2014-06-09 17:01:10 -040059 # suppress logging for boto
60 boto_logger.logger.setLevel(orig_logging.CRITICAL)
Attila Fazekas234d3e82013-02-22 16:39:49 +010061
62 def _cred_sub_check(connection_data):
63 if not id_matcher.match(connection_data["aws_access_key_id"]):
64 raise Exception("Invalid AWS access Key")
65 if not secret_matcher.match(connection_data["aws_secret_access_key"]):
66 raise Exception("Invalid AWS secret Key")
67 raise Exception("Unknown (Authentication?) Error")
68 openstack = tempest.clients.Manager()
69 try:
Sean Dague86bd8422013-12-20 09:56:44 -050070 if urlparse.urlparse(CONF.boto.ec2_url).hostname is None:
Attila Fazekas234d3e82013-02-22 16:39:49 +010071 raise Exception("Failed to get hostname from the ec2_url")
72 ec2client = openstack.ec2api_client
73 try:
74 ec2client.get_all_regions()
75 except exception.BotoServerError as exc:
76 if exc.error_code is None:
77 raise Exception("EC2 target does not looks EC2 service")
78 _cred_sub_check(ec2client.connection_data)
79
80 except keystoneclient.exceptions.Unauthorized:
81 EC2_CAN_CONNECT_ERROR = "AWS credentials not set," +\
Matt Riedemann78b0f802015-02-09 14:06:07 -080082 " failed to get them even by keystoneclient"
Attila Fazekas234d3e82013-02-22 16:39:49 +010083 except Exception as exc:
84 EC2_CAN_CONNECT_ERROR = str(exc)
85
86 try:
Sean Dague86bd8422013-12-20 09:56:44 -050087 if urlparse.urlparse(CONF.boto.s3_url).hostname is None:
Attila Fazekas234d3e82013-02-22 16:39:49 +010088 raise Exception("Failed to get hostname from the s3_url")
89 s3client = openstack.s3_client
90 try:
91 s3client.get_bucket("^INVALID*#()@INVALID.")
92 except exception.BotoServerError as exc:
93 if exc.status == 403:
94 _cred_sub_check(s3client.connection_data)
95 except Exception as exc:
96 S3_CAN_CONNECT_ERROR = str(exc)
97 except keystoneclient.exceptions.Unauthorized:
98 S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\
Matt Riedemann78b0f802015-02-09 14:06:07 -080099 " failed to get them even by keystoneclient"
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -0400100 boto_logger.logger.setLevel(level)
Attila Fazekas234d3e82013-02-22 16:39:49 +0100101 return {'A_I_IMAGES_READY': A_I_IMAGES_READY,
102 'S3_CAN_CONNECT_ERROR': S3_CAN_CONNECT_ERROR,
103 'EC2_CAN_CONNECT_ERROR': EC2_CAN_CONNECT_ERROR}
104
105
Attila Fazekasa23f5002012-10-23 19:32:45 +0200106class BotoExceptionMatcher(object):
107 STATUS_RE = r'[45]\d\d'
108 CODE_RE = '.*' # regexp makes sense in group match
109
110 def match(self, exc):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700111 """:returns: Returns with an error string if it does not match,
112 returns with None when it matches.
Attila Fazekas55c597d2014-02-21 13:10:41 +0100113 """
Attila Fazekas40aa3612013-01-19 22:16:38 +0100114 if not isinstance(exc, exception.BotoServerError):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200115 return "%r not an BotoServerError instance" % exc
116 LOG.info("Status: %s , error_code: %s", exc.status, exc.error_code)
117 if re.match(self.STATUS_RE, str(exc.status)) is None:
118 return ("Status code (%s) does not match"
119 "the expected re pattern \"%s\""
120 % (exc.status, self.STATUS_RE))
121 if re.match(self.CODE_RE, str(exc.error_code)) is None:
122 return ("Error code (%s) does not match" +
123 "the expected re pattern \"%s\"") %\
124 (exc.error_code, self.CODE_RE)
Attila Fazekas55c597d2014-02-21 13:10:41 +0100125 return None
Attila Fazekasa23f5002012-10-23 19:32:45 +0200126
127
128class ClientError(BotoExceptionMatcher):
129 STATUS_RE = r'4\d\d'
130
131
132class ServerError(BotoExceptionMatcher):
133 STATUS_RE = r'5\d\d'
134
135
136def _add_matcher_class(error_cls, error_data, base=BotoExceptionMatcher):
137 """
138 Usable for adding an ExceptionMatcher(s) into the exception tree.
139 The not leaf elements does wildcard match
140 """
141 # in error_code just literal and '.' characters expected
llg821243b20502014-02-22 10:32:49 +0800142 if not isinstance(error_data, six.string_types):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200143 (error_code, status_code) = map(str, error_data)
144 else:
145 status_code = None
146 error_code = error_data
147 parts = error_code.split('.')
148 basematch = ""
149 num_parts = len(parts)
150 max_index = num_parts - 1
151 add_cls = error_cls
llg821243b20502014-02-22 10:32:49 +0800152 for i_part in six.moves.xrange(num_parts):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200153 part = parts[i_part]
154 leaf = i_part == max_index
155 if not leaf:
156 match = basematch + part + "[.].*"
157 else:
158 match = basematch + part
159
160 basematch += part + "[.]"
161 if not hasattr(add_cls, part):
162 cls_dict = {"CODE_RE": match}
163 if leaf and status_code is not None:
164 cls_dict["STATUS_RE"] = status_code
165 cls = type(part, (base, ), cls_dict)
166 setattr(add_cls, part, cls())
167 add_cls = cls
168 elif leaf:
169 raise LookupError("Tries to redefine an error code \"%s\"" % part)
170 else:
171 add_cls = getattr(add_cls, part)
172
173
Attila Fazekas3e381f72013-08-01 16:52:23 +0200174# TODO(afazekas): classmethod handling
Attila Fazekasa23f5002012-10-23 19:32:45 +0200175def friendly_function_name_simple(call_able):
176 name = ""
177 if hasattr(call_able, "im_class"):
178 name += call_able.im_class.__name__ + "."
179 name += call_able.__name__
180 return name
181
182
183def friendly_function_call_str(call_able, *args, **kwargs):
184 string = friendly_function_name_simple(call_able)
185 string += "(" + ", ".join(map(str, args))
186 if len(kwargs):
187 if len(args):
188 string += ", "
189 string += ", ".join("=".join(map(str, (key, value)))
Matthew Treinish1d14c542014-06-17 20:25:40 -0400190 for (key, value) in kwargs.items())
Attila Fazekasa23f5002012-10-23 19:32:45 +0200191 return string + ")"
192
193
Attila Fazekasdc216422013-01-29 15:12:14 +0100194class BotoTestCase(tempest.test.BaseTestCase):
Sean Daguef237ccb2013-01-04 15:19:14 -0500195 """Recommended to use as base class for boto related test."""
Chris Yeoh8a79b9d2013-01-18 19:32:47 +1030196
Attila Fazekasa23f5002012-10-23 19:32:45 +0200197 @classmethod
Matthew Treinishdfd7ac02015-02-09 17:47:31 -0500198 def skip_checks(cls):
199 super(BotoTestCase, cls).skip_checks()
200 if not CONF.compute_feature_enabled.ec2_api:
201 raise cls.skipException("The EC2 API is not available")
202
203 @classmethod
Emily Hugenbruche252a4a2015-02-27 15:43:12 +0000204 def setup_credentials(cls):
205 super(BotoTestCase, cls).setup_credentials()
206 cls.os = cls.get_client_manager()
207
208 @classmethod
Andrea Frittoli29fea352014-09-15 13:31:14 +0100209 def resource_setup(cls):
210 super(BotoTestCase, cls).resource_setup()
Matthew Treinishf054a9c2013-10-28 21:34:47 -0400211 cls.conclusion = decision_maker()
Attila Fazekasa23f5002012-10-23 19:32:45 +0200212 # The trash contains cleanup functions and paramaters in tuples
213 # (function, *args, **kwargs)
214 cls._resource_trash_bin = {}
215 cls._sequence = -1
216 if (hasattr(cls, "EC2") and
Attila Fazekas234d3e82013-02-22 16:39:49 +0100217 cls.conclusion['EC2_CAN_CONNECT_ERROR'] is not None):
ivan-zhu1feeb382013-01-24 10:14:39 +0800218 raise cls.skipException("EC2 " + cls.__name__ + ": " +
Attila Fazekas234d3e82013-02-22 16:39:49 +0100219 cls.conclusion['EC2_CAN_CONNECT_ERROR'])
Attila Fazekasa23f5002012-10-23 19:32:45 +0200220 if (hasattr(cls, "S3") and
Attila Fazekas234d3e82013-02-22 16:39:49 +0100221 cls.conclusion['S3_CAN_CONNECT_ERROR'] is not None):
ivan-zhu1feeb382013-01-24 10:14:39 +0800222 raise cls.skipException("S3 " + cls.__name__ + ": " +
Attila Fazekas234d3e82013-02-22 16:39:49 +0100223 cls.conclusion['S3_CAN_CONNECT_ERROR'])
Attila Fazekasa23f5002012-10-23 19:32:45 +0200224
225 @classmethod
226 def addResourceCleanUp(cls, function, *args, **kwargs):
227 """Adds CleanUp callable, used by tearDownClass.
Attila Fazekasb2902af2013-02-16 16:22:44 +0100228 Recommended to a use (deep)copy on the mutable args.
229 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200230 cls._sequence = cls._sequence + 1
231 cls._resource_trash_bin[cls._sequence] = (function, args, kwargs)
232 return cls._sequence
233
234 @classmethod
235 def cancelResourceCleanUp(cls, key):
Sean Daguef237ccb2013-01-04 15:19:14 -0500236 """Cancel Clean up request."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200237 del cls._resource_trash_bin[key]
238
Attila Fazekas3e381f72013-08-01 16:52:23 +0200239 # TODO(afazekas): Add "with" context handling
Attila Fazekasa23f5002012-10-23 19:32:45 +0200240 def assertBotoError(self, excMatcher, callableObj,
241 *args, **kwargs):
242 """Example usage:
243 self.assertBotoError(self.ec2_error_code.client.
244 InvalidKeyPair.Duplicate,
245 self.client.create_keypair,
Attila Fazekasb2902af2013-02-16 16:22:44 +0100246 key_name)
247 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200248 try:
249 callableObj(*args, **kwargs)
Attila Fazekas40aa3612013-01-19 22:16:38 +0100250 except exception.BotoServerError as exc:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200251 error_msg = excMatcher.match(exc)
252 if error_msg is not None:
253 raise self.failureException, error_msg
254 else:
255 raise self.failureException, "BotoServerError not raised"
256
257 @classmethod
Andrea Frittoli29fea352014-09-15 13:31:14 +0100258 def resource_cleanup(cls):
Attila Fazekasb2902af2013-02-16 16:22:44 +0100259 """Calls the callables added by addResourceCleanUp,
Yair Frieda039f872014-01-02 12:11:10 +0200260 when you overwrite this function don't forget to call this too.
Attila Fazekasb2902af2013-02-16 16:22:44 +0100261 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200262 fail_count = 0
263 trash_keys = sorted(cls._resource_trash_bin, reverse=True)
264 for key in trash_keys:
265 (function, pos_args, kw_args) = cls._resource_trash_bin[key]
266 try:
Yair Frieda039f872014-01-02 12:11:10 +0200267 func_name = friendly_function_call_str(function, *pos_args,
268 **kw_args)
269 LOG.debug("Cleaning up: %s" % func_name)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200270 function(*pos_args, **kw_args)
Yair Frieda039f872014-01-02 12:11:10 +0200271 except BaseException:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200272 fail_count += 1
Yair Frieda039f872014-01-02 12:11:10 +0200273 LOG.exception("Cleanup failed %s" % func_name)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200274 finally:
275 del cls._resource_trash_bin[key]
Matthew Treinish8004e8c2014-01-27 23:03:14 +0000276 cls.clear_isolated_creds()
Andrea Frittoli29fea352014-09-15 13:31:14 +0100277 super(BotoTestCase, cls).resource_cleanup()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200278 # NOTE(afazekas): let the super called even on exceptions
279 # The real exceptions already logged, if the super throws another,
280 # does not causes hidden issues
Attila Fazekasa23f5002012-10-23 19:32:45 +0200281 if fail_count:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100282 raise exceptions.TearDownException(num=fail_count)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200283
284 ec2_error_code = BotoExceptionMatcher()
285 # InsufficientInstanceCapacity can be both server and client error
286 ec2_error_code.server = ServerError()
287 ec2_error_code.client = ClientError()
288 s3_error_code = BotoExceptionMatcher()
289 s3_error_code.server = ServerError()
290 s3_error_code.client = ClientError()
291 valid_image_state = set(('available', 'pending', 'failed'))
Attila Fazekas3e381f72013-08-01 16:52:23 +0200292 # NOTE(afazekas): 'paused' is not valid status in EC2, but it does not have
Attila Fazekasc66ee652013-01-31 06:56:13 +0100293 # a good mapping, because it uses memory, but not really a running machine
Attila Fazekasa23f5002012-10-23 19:32:45 +0200294 valid_instance_state = set(('pending', 'running', 'shutting-down',
Attila Fazekasc66ee652013-01-31 06:56:13 +0100295 'terminated', 'stopping', 'stopped', 'paused'))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200296 valid_volume_status = set(('creating', 'available', 'in-use',
297 'deleting', 'deleted', 'error'))
298 valid_snapshot_status = set(('pending', 'completed', 'error'))
299
Attila Fazekas37f83042013-01-12 16:13:03 +0100300 gone_set = set(('_GONE',))
301
Attila Fazekas40aa3612013-01-19 22:16:38 +0100302 @classmethod
303 def get_lfunction_gone(cls, obj):
Sean Dague2416cf32013-04-10 08:29:07 -0400304 """If the object is instance of a well know type returns back with
Attila Fazekas40aa3612013-01-19 22:16:38 +0100305 with the correspoding function otherwise it assumes the obj itself
Sean Dague2416cf32013-04-10 08:29:07 -0400306 is the function.
307 """
Attila Fazekas40aa3612013-01-19 22:16:38 +0100308 ec = cls.ec2_error_code
309 if isinstance(obj, ec2.instance.Instance):
310 colusure_matcher = ec.client.InvalidInstanceID.NotFound
311 status_attr = "state"
312 elif isinstance(obj, ec2.image.Image):
313 colusure_matcher = ec.client.InvalidAMIID.NotFound
314 status_attr = "state"
315 elif isinstance(obj, ec2.snapshot.Snapshot):
316 colusure_matcher = ec.client.InvalidSnapshot.NotFound
317 status_attr = "status"
318 elif isinstance(obj, ec2.volume.Volume):
319 colusure_matcher = ec.client.InvalidVolume.NotFound
320 status_attr = "status"
321 else:
322 return obj
323
324 def _status():
325 try:
326 obj.update(validate=True)
327 except ValueError:
328 return "_GONE"
329 except exception.EC2ResponseError as exc:
Attila Fazekas55c597d2014-02-21 13:10:41 +0100330 if colusure_matcher.match(exc) is None:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100331 return "_GONE"
332 else:
333 raise
334 return getattr(obj, status_attr)
335
336 return _status
337
Attila Fazekas37f83042013-01-12 16:13:03 +0100338 def state_wait_gone(self, lfunction, final_set, valid_set):
339 if not isinstance(final_set, set):
340 final_set = set((final_set,))
341 final_set |= self.gone_set
Attila Fazekas40aa3612013-01-19 22:16:38 +0100342 lfunction = self.get_lfunction_gone(lfunction)
Masayuki Igawa224a8272014-02-17 15:07:43 +0900343 state = wait.state_wait(lfunction, final_set, valid_set)
Attila Fazekas37f83042013-01-12 16:13:03 +0100344 self.assertIn(state, valid_set | self.gone_set)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200345 return state
346
Attila Fazekas37f83042013-01-12 16:13:03 +0100347 def waitImageState(self, lfunction, wait_for):
348 return self.state_wait_gone(lfunction, wait_for,
349 self.valid_image_state)
350
Attila Fazekasa23f5002012-10-23 19:32:45 +0200351 def waitInstanceState(self, lfunction, wait_for):
Attila Fazekas37f83042013-01-12 16:13:03 +0100352 return self.state_wait_gone(lfunction, wait_for,
353 self.valid_instance_state)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200354
Attila Fazekasa23f5002012-10-23 19:32:45 +0200355 def waitSnapshotStatus(self, lfunction, wait_for):
Attila Fazekas37f83042013-01-12 16:13:03 +0100356 return self.state_wait_gone(lfunction, wait_for,
357 self.valid_snapshot_status)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200358
Attila Fazekas40aa3612013-01-19 22:16:38 +0100359 def waitVolumeStatus(self, lfunction, wait_for):
360 return self.state_wait_gone(lfunction, wait_for,
361 self.valid_volume_status)
362
Attila Fazekasa23f5002012-10-23 19:32:45 +0200363 def assertImageStateWait(self, lfunction, wait_for):
364 state = self.waitImageState(lfunction, wait_for)
365 self.assertIn(state, wait_for)
366
367 def assertInstanceStateWait(self, lfunction, wait_for):
368 state = self.waitInstanceState(lfunction, wait_for)
369 self.assertIn(state, wait_for)
370
371 def assertVolumeStatusWait(self, lfunction, wait_for):
372 state = self.waitVolumeStatus(lfunction, wait_for)
373 self.assertIn(state, wait_for)
374
375 def assertSnapshotStatusWait(self, lfunction, wait_for):
376 state = self.waitSnapshotStatus(lfunction, wait_for)
377 self.assertIn(state, wait_for)
378
379 def assertAddressDissasociatedWait(self, address):
380
381 def _disassociate():
382 cli = self.ec2_client
383 addresses = cli.get_all_addresses(addresses=(address.public_ip,))
384 if len(addresses) != 1:
385 return "INVALID"
386 if addresses[0].instance_id:
387 LOG.info("%s associated to %s",
388 address.public_ip,
389 addresses[0].instance_id)
390 return "ASSOCIATED"
391 return "DISASSOCIATED"
392
Masayuki Igawa224a8272014-02-17 15:07:43 +0900393 state = wait.state_wait(_disassociate, "DISASSOCIATED",
394 set(("ASSOCIATED", "DISASSOCIATED")))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200395 self.assertEqual(state, "DISASSOCIATED")
396
397 def assertAddressReleasedWait(self, address):
398
399 def _address_delete():
Attila Fazekas3e381f72013-08-01 16:52:23 +0200400 # NOTE(afazekas): the filter gives back IP
Attila Fazekasa23f5002012-10-23 19:32:45 +0200401 # even if it is not associated to my tenant
402 if (address.public_ip not in map(lambda a: a.public_ip,
403 self.ec2_client.get_all_addresses())):
404 return "DELETED"
405 return "NOTDELETED"
406
Masayuki Igawa224a8272014-02-17 15:07:43 +0900407 state = wait.state_wait(_address_delete, "DELETED")
Attila Fazekasa23f5002012-10-23 19:32:45 +0200408 self.assertEqual(state, "DELETED")
409
410 def assertReSearch(self, regexp, string):
411 if re.search(regexp, string) is None:
412 raise self.failureException("regexp: '%s' not found in '%s'" %
413 (regexp, string))
414
415 def assertNotReSearch(self, regexp, string):
416 if re.search(regexp, string) is not None:
417 raise self.failureException("regexp: '%s' found in '%s'" %
418 (regexp, string))
419
420 def assertReMatch(self, regexp, string):
421 if re.match(regexp, string) is None:
422 raise self.failureException("regexp: '%s' not matches on '%s'" %
423 (regexp, string))
424
425 def assertNotReMatch(self, regexp, string):
426 if re.match(regexp, string) is not None:
427 raise self.failureException("regexp: '%s' matches on '%s'" %
428 (regexp, string))
429
430 @classmethod
431 def destroy_bucket(cls, connection_data, bucket):
Sean Daguef237ccb2013-01-04 15:19:14 -0500432 """Destroys the bucket and its content, just for teardown."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200433 exc_num = 0
434 try:
Monty Taylorb2ca5ca2013-04-28 18:00:21 -0700435 with contextlib.closing(
436 boto.connect_s3(**connection_data)) as conn:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200437 if isinstance(bucket, basestring):
438 bucket = conn.lookup(bucket)
Attila Fazekas40aa3612013-01-19 22:16:38 +0100439 assert isinstance(bucket, s3.bucket.Bucket)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200440 for obj in bucket.list():
441 try:
442 bucket.delete_key(obj.key)
443 obj.close()
Yair Frieda039f872014-01-02 12:11:10 +0200444 except BaseException:
445 LOG.exception("Failed to delete key %s " % obj.key)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200446 exc_num += 1
447 conn.delete_bucket(bucket)
Yair Frieda039f872014-01-02 12:11:10 +0200448 except BaseException:
449 LOG.exception("Failed to destroy bucket %s " % bucket)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200450 exc_num += 1
451 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100452 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200453
454 @classmethod
455 def destroy_reservation(cls, reservation):
Sean Daguef237ccb2013-01-04 15:19:14 -0500456 """Terminate instances in a reservation, just for teardown."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200457 exc_num = 0
458
459 def _instance_state():
460 try:
461 instance.update(validate=True)
462 except ValueError:
Attila Fazekas37f83042013-01-12 16:13:03 +0100463 return "_GONE"
Attila Fazekas40aa3612013-01-19 22:16:38 +0100464 except exception.EC2ResponseError as exc:
Attila Fazekas37f83042013-01-12 16:13:03 +0100465 if cls.ec2_error_code.\
Attila Fazekas55c597d2014-02-21 13:10:41 +0100466 client.InvalidInstanceID.NotFound.match(exc) is None:
Attila Fazekas37f83042013-01-12 16:13:03 +0100467 return "_GONE"
Attila Fazekas3e381f72013-08-01 16:52:23 +0200468 # NOTE(afazekas): incorrect code,
Attila Fazekas37f83042013-01-12 16:13:03 +0100469 # but the resource must be destoreyd
470 if exc.error_code == "InstanceNotFound":
471 return "_GONE"
472
Attila Fazekasa23f5002012-10-23 19:32:45 +0200473 return instance.state
474
475 for instance in reservation.instances:
476 try:
477 instance.terminate()
Masayuki Igawa224a8272014-02-17 15:07:43 +0900478 wait.re_search_wait(_instance_state, "_GONE")
Yair Frieda039f872014-01-02 12:11:10 +0200479 except BaseException:
480 LOG.exception("Failed to terminate instance %s " % instance)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200481 exc_num += 1
482 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100483 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200484
Attila Fazekas3e381f72013-08-01 16:52:23 +0200485 # NOTE(afazekas): The incorrect ErrorCodes makes very, very difficult
Attila Fazekasa23f5002012-10-23 19:32:45 +0200486 # to write better teardown
487
488 @classmethod
489 def destroy_security_group_wait(cls, group):
490 """Delete group.
491 Use just for teardown!
492 """
Attila Fazekas3e381f72013-08-01 16:52:23 +0200493 # NOTE(afazekas): should wait/try until all related instance terminates
Attila Fazekasa23f5002012-10-23 19:32:45 +0200494 group.delete()
495
496 @classmethod
497 def destroy_volume_wait(cls, volume):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700498 """Delete volume, tries to detach first.
Attila Fazekasa23f5002012-10-23 19:32:45 +0200499 Use just for teardown!
500 """
501 exc_num = 0
502 snaps = volume.snapshots()
503 if len(snaps):
504 LOG.critical("%s Volume has %s snapshot(s)", volume.id,
Attila Fazekasfa756cb2013-02-12 10:52:42 +0100505 map(snaps.id, snaps))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200506
Attila Fazekas3e381f72013-08-01 16:52:23 +0200507 # NOTE(afazekas): detaching/attching not valid EC2 status
Attila Fazekasa23f5002012-10-23 19:32:45 +0200508 def _volume_state():
509 volume.update(validate=True)
510 try:
ghanshyam308640c2014-10-14 17:23:31 +0900511 # NOTE(gmann): Make sure volume is attached.
512 # Checking status as 'not "available"' is not enough to make
513 # sure volume is attached as it can be in "error" state
514 if volume.status == "in-use":
Attila Fazekasa23f5002012-10-23 19:32:45 +0200515 volume.detach(force=True)
Yair Frieda039f872014-01-02 12:11:10 +0200516 except BaseException:
517 LOG.exception("Failed to detach volume %s" % volume)
Attila Fazekas3e381f72013-08-01 16:52:23 +0200518 # exc_num += 1 "nonlocal" not in python2
Attila Fazekasa23f5002012-10-23 19:32:45 +0200519 return volume.status
520
521 try:
Masayuki Igawa224a8272014-02-17 15:07:43 +0900522 wait.re_search_wait(_volume_state, "available")
523 # not validates status
Attila Fazekasa23f5002012-10-23 19:32:45 +0200524 LOG.info(_volume_state())
525 volume.delete()
Yair Frieda039f872014-01-02 12:11:10 +0200526 except BaseException:
527 LOG.exception("Failed to delete volume %s" % volume)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200528 exc_num += 1
529 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100530 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200531
532 @classmethod
533 def destroy_snapshot_wait(cls, snapshot):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700534 """delete snapshot, wait until it ceases to exist."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200535 snapshot.delete()
536
537 def _update():
538 snapshot.update(validate=True)
539
Masayuki Igawa224a8272014-02-17 15:07:43 +0900540 wait.wait_exception(_update)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200541
542
543# you can specify tuples if you want to specify the status pattern
544for code in ('AddressLimitExceeded', 'AttachmentLimitExceeded', 'AuthFailure',
545 'Blocked', 'CustomerGatewayLimitExceeded', 'DependencyViolation',
546 'DiskImageSizeTooLarge', 'FilterLimitExceeded',
547 'Gateway.NotAttached', 'IdempotentParameterMismatch',
548 'IncorrectInstanceState', 'IncorrectState',
549 'InstanceLimitExceeded', 'InsufficientInstanceCapacity',
550 'InsufficientReservedInstancesCapacity',
551 'InternetGatewayLimitExceeded', 'InvalidAMIAttributeItemValue',
552 'InvalidAMIID.Malformed', 'InvalidAMIID.NotFound',
553 'InvalidAMIID.Unavailable', 'InvalidAssociationID.NotFound',
554 'InvalidAttachment.NotFound', 'InvalidConversionTaskId',
555 'InvalidCustomerGateway.DuplicateIpAddress',
556 'InvalidCustomerGatewayID.NotFound', 'InvalidDevice.InUse',
557 'InvalidDhcpOptionsID.NotFound', 'InvalidFormat',
558 'InvalidFilter', 'InvalidGatewayID.NotFound',
559 'InvalidGroup.Duplicate', 'InvalidGroupId.Malformed',
560 'InvalidGroup.InUse', 'InvalidGroup.NotFound',
561 'InvalidGroup.Reserved', 'InvalidInstanceID.Malformed',
562 'InvalidInstanceID.NotFound',
563 'InvalidInternetGatewayID.NotFound', 'InvalidIPAddress.InUse',
564 'InvalidKeyPair.Duplicate', 'InvalidKeyPair.Format',
565 'InvalidKeyPair.NotFound', 'InvalidManifest',
566 'InvalidNetworkAclEntry.NotFound',
567 'InvalidNetworkAclID.NotFound', 'InvalidParameterCombination',
568 'InvalidParameterValue', 'InvalidPermission.Duplicate',
569 'InvalidPermission.Malformed', 'InvalidReservationID.Malformed',
570 'InvalidReservationID.NotFound', 'InvalidRoute.NotFound',
571 'InvalidRouteTableID.NotFound',
572 'InvalidSecurity.RequestHasExpired',
573 'InvalidSnapshotID.Malformed', 'InvalidSnapshot.NotFound',
574 'InvalidUserID.Malformed', 'InvalidReservedInstancesId',
575 'InvalidReservedInstancesOfferingId',
576 'InvalidSubnetID.NotFound', 'InvalidVolumeID.Duplicate',
577 'InvalidVolumeID.Malformed', 'InvalidVolumeID.ZoneMismatch',
578 'InvalidVolume.NotFound', 'InvalidVpcID.NotFound',
579 'InvalidVpnConnectionID.NotFound',
580 'InvalidVpnGatewayID.NotFound',
581 'InvalidZone.NotFound', 'LegacySecurityGroup',
582 'MissingParameter', 'NetworkAclEntryAlreadyExists',
583 'NetworkAclEntryLimitExceeded', 'NetworkAclLimitExceeded',
584 'NonEBSInstance', 'PendingSnapshotLimitExceeded',
585 'PendingVerification', 'OptInRequired', 'RequestLimitExceeded',
586 'ReservedInstancesLimitExceeded', 'Resource.AlreadyAssociated',
587 'ResourceLimitExceeded', 'RouteAlreadyExists',
588 'RouteLimitExceeded', 'RouteTableLimitExceeded',
589 'RulesPerSecurityGroupLimitExceeded',
590 'SecurityGroupLimitExceeded',
591 'SecurityGroupsPerInstanceLimitExceeded',
592 'SnapshotLimitExceeded', 'SubnetLimitExceeded',
593 'UnknownParameter', 'UnsupportedOperation',
594 'VolumeLimitExceeded', 'VpcLimitExceeded',
595 'VpnConnectionLimitExceeded',
596 'VpnGatewayAttachmentLimitExceeded', 'VpnGatewayLimitExceeded'):
597 _add_matcher_class(BotoTestCase.ec2_error_code.client,
598 code, base=ClientError)
599
600for code in ('InsufficientAddressCapacity', 'InsufficientInstanceCapacity',
601 'InsufficientReservedInstanceCapacity', 'InternalError',
602 'Unavailable'):
603 _add_matcher_class(BotoTestCase.ec2_error_code.server,
604 code, base=ServerError)
605
606
607for code in (('AccessDenied', 403),
Matthew Treinish1d14c542014-06-17 20:25:40 -0400608 ('AccountProblem', 403),
609 ('AmbiguousGrantByEmailAddress', 400),
610 ('BadDigest', 400),
611 ('BucketAlreadyExists', 409),
612 ('BucketAlreadyOwnedByYou', 409),
613 ('BucketNotEmpty', 409),
614 ('CredentialsNotSupported', 400),
615 ('CrossLocationLoggingProhibited', 403),
616 ('EntityTooSmall', 400),
617 ('EntityTooLarge', 400),
618 ('ExpiredToken', 400),
619 ('IllegalVersioningConfigurationException', 400),
620 ('IncompleteBody', 400),
621 ('IncorrectNumberOfFilesInPostRequest', 400),
622 ('InlineDataTooLarge', 400),
623 ('InvalidAccessKeyId', 403),
Attila Fazekasa23f5002012-10-23 19:32:45 +0200624 'InvalidAddressingHeader',
Matthew Treinish1d14c542014-06-17 20:25:40 -0400625 ('InvalidArgument', 400),
626 ('InvalidBucketName', 400),
627 ('InvalidBucketState', 409),
628 ('InvalidDigest', 400),
629 ('InvalidLocationConstraint', 400),
630 ('InvalidPart', 400),
631 ('InvalidPartOrder', 400),
632 ('InvalidPayer', 403),
633 ('InvalidPolicyDocument', 400),
634 ('InvalidRange', 416),
635 ('InvalidRequest', 400),
636 ('InvalidSecurity', 403),
637 ('InvalidSOAPRequest', 400),
638 ('InvalidStorageClass', 400),
639 ('InvalidTargetBucketForLogging', 400),
640 ('InvalidToken', 400),
641 ('InvalidURI', 400),
642 ('KeyTooLong', 400),
643 ('MalformedACLError', 400),
644 ('MalformedPOSTRequest', 400),
645 ('MalformedXML', 400),
646 ('MaxMessageLengthExceeded', 400),
647 ('MaxPostPreDataLengthExceededError', 400),
648 ('MetadataTooLarge', 400),
649 ('MethodNotAllowed', 405),
650 ('MissingAttachment'),
651 ('MissingContentLength', 411),
652 ('MissingRequestBodyError', 400),
653 ('MissingSecurityElement', 400),
654 ('MissingSecurityHeader', 400),
655 ('NoLoggingStatusForKey', 400),
656 ('NoSuchBucket', 404),
657 ('NoSuchKey', 404),
658 ('NoSuchLifecycleConfiguration', 404),
659 ('NoSuchUpload', 404),
660 ('NoSuchVersion', 404),
661 ('NotSignedUp', 403),
662 ('NotSuchBucketPolicy', 404),
663 ('OperationAborted', 409),
664 ('PermanentRedirect', 301),
665 ('PreconditionFailed', 412),
666 ('Redirect', 307),
667 ('RequestIsNotMultiPartContent', 400),
668 ('RequestTimeout', 400),
669 ('RequestTimeTooSkewed', 403),
670 ('RequestTorrentOfBucketError', 400),
671 ('SignatureDoesNotMatch', 403),
672 ('TemporaryRedirect', 307),
673 ('TokenRefreshRequired', 400),
674 ('TooManyBuckets', 400),
675 ('UnexpectedContent', 400),
676 ('UnresolvableGrantByEmailAddress', 400),
677 ('UserKeyMustBeSpecified', 400)):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200678 _add_matcher_class(BotoTestCase.s3_error_code.client,
679 code, base=ClientError)
680
681
682for code in (('InternalError', 500),
Matthew Treinish1d14c542014-06-17 20:25:40 -0400683 ('NotImplemented', 501),
684 ('ServiceUnavailable', 503),
685 ('SlowDown', 503)):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200686 _add_matcher_class(BotoTestCase.s3_error_code.server,
687 code, base=ServerError)