blob: 2aef9109e179608ed5ee3b549e7b603bdbe1708b [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
Doug Hellmann583ce2c2015-03-11 14:55:46 +000026from oslo_log import log as logging
Matthew Treinish96e9e882014-06-09 18:37:19 -040027import six
Matthew Treinisha83a16e2012-12-07 13:44:02 -050028
Andrea Frittoli32d74992015-03-06 17:01:07 +000029from tempest_lib import exceptions as lib_exc
30
Attila Fazekas234d3e82013-02-22 16:39:49 +010031import tempest.clients
Masayuki Igawa224a8272014-02-17 15:07:43 +090032from tempest.common.utils import file_utils
Sean Dague86bd8422013-12-20 09:56:44 -050033from tempest import config
Attila Fazekas40aa3612013-01-19 22:16:38 +010034from tempest import exceptions
Attila Fazekasdc216422013-01-29 15:12:14 +010035import tempest.test
Masayuki Igawa224a8272014-02-17 15:07:43 +090036from tempest.thirdparty.boto.utils import wait
Matthew Treinisha83a16e2012-12-07 13:44:02 -050037
Sean Dague86bd8422013-12-20 09:56:44 -050038CONF = config.CONF
Attila Fazekasa23f5002012-10-23 19:32:45 +020039LOG = logging.getLogger(__name__)
40
41
Attila Fazekas234d3e82013-02-22 16:39:49 +010042def decision_maker():
43 A_I_IMAGES_READY = True # ari,ami,aki
44 S3_CAN_CONNECT_ERROR = None
45 EC2_CAN_CONNECT_ERROR = None
46 secret_matcher = re.compile("[A-Za-z0-9+/]{32,}") # 40 in other system
47 id_matcher = re.compile("[A-Za-z0-9]{20,}")
48
49 def all_read(*args):
Masayuki Igawa224a8272014-02-17 15:07:43 +090050 return all(map(file_utils.have_effective_read_access, args))
Attila Fazekas234d3e82013-02-22 16:39:49 +010051
Sean Dague86bd8422013-12-20 09:56:44 -050052 materials_path = CONF.boto.s3_materials_path
53 ami_path = materials_path + os.sep + CONF.boto.ami_manifest
54 aki_path = materials_path + os.sep + CONF.boto.aki_manifest
55 ari_path = materials_path + os.sep + CONF.boto.ari_manifest
Attila Fazekas234d3e82013-02-22 16:39:49 +010056
57 A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
58 boto_logger = logging.getLogger('boto')
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -040059 level = boto_logger.logger.level
Matthew Treinishc795b9e2014-06-09 17:01:10 -040060 # suppress logging for boto
61 boto_logger.logger.setLevel(orig_logging.CRITICAL)
Attila Fazekas234d3e82013-02-22 16:39:49 +010062
63 def _cred_sub_check(connection_data):
64 if not id_matcher.match(connection_data["aws_access_key_id"]):
65 raise Exception("Invalid AWS access Key")
66 if not secret_matcher.match(connection_data["aws_secret_access_key"]):
67 raise Exception("Invalid AWS secret Key")
68 raise Exception("Unknown (Authentication?) Error")
Andrea Frittoli32d74992015-03-06 17:01:07 +000069 # NOTE(andreaf) Setting up an extra manager here is redundant,
70 # and should be removed.
Attila Fazekas234d3e82013-02-22 16:39:49 +010071 openstack = tempest.clients.Manager()
72 try:
Sean Dague86bd8422013-12-20 09:56:44 -050073 if urlparse.urlparse(CONF.boto.ec2_url).hostname is None:
Attila Fazekas234d3e82013-02-22 16:39:49 +010074 raise Exception("Failed to get hostname from the ec2_url")
75 ec2client = openstack.ec2api_client
76 try:
77 ec2client.get_all_regions()
78 except exception.BotoServerError as exc:
79 if exc.error_code is None:
80 raise Exception("EC2 target does not looks EC2 service")
81 _cred_sub_check(ec2client.connection_data)
82
Andrea Frittoli32d74992015-03-06 17:01:07 +000083 except lib_exc.Unauthorized:
Attila Fazekas234d3e82013-02-22 16:39:49 +010084 EC2_CAN_CONNECT_ERROR = "AWS credentials not set," +\
Attila Fazekas62aebc42015-03-12 07:38:19 +010085 " also failed to get it from keystone"
Attila Fazekas234d3e82013-02-22 16:39:49 +010086 except Exception as exc:
87 EC2_CAN_CONNECT_ERROR = str(exc)
88
89 try:
Sean Dague86bd8422013-12-20 09:56:44 -050090 if urlparse.urlparse(CONF.boto.s3_url).hostname is None:
Attila Fazekas234d3e82013-02-22 16:39:49 +010091 raise Exception("Failed to get hostname from the s3_url")
92 s3client = openstack.s3_client
93 try:
94 s3client.get_bucket("^INVALID*#()@INVALID.")
95 except exception.BotoServerError as exc:
96 if exc.status == 403:
97 _cred_sub_check(s3client.connection_data)
98 except Exception as exc:
99 S3_CAN_CONNECT_ERROR = str(exc)
Attila Fazekas62aebc42015-03-12 07:38:19 +0100100 except lib_exc.Unauthorized:
Attila Fazekas234d3e82013-02-22 16:39:49 +0100101 S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\
Matt Riedemann78b0f802015-02-09 14:06:07 -0800102 " failed to get them even by keystoneclient"
Matthew Treinishf4a9b0f2013-07-26 16:58:26 -0400103 boto_logger.logger.setLevel(level)
Attila Fazekas234d3e82013-02-22 16:39:49 +0100104 return {'A_I_IMAGES_READY': A_I_IMAGES_READY,
105 'S3_CAN_CONNECT_ERROR': S3_CAN_CONNECT_ERROR,
106 'EC2_CAN_CONNECT_ERROR': EC2_CAN_CONNECT_ERROR}
107
108
Attila Fazekasa23f5002012-10-23 19:32:45 +0200109class BotoExceptionMatcher(object):
110 STATUS_RE = r'[45]\d\d'
111 CODE_RE = '.*' # regexp makes sense in group match
112
113 def match(self, exc):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700114 """:returns: Returns with an error string if it does not match,
115 returns with None when it matches.
Attila Fazekas55c597d2014-02-21 13:10:41 +0100116 """
Attila Fazekas40aa3612013-01-19 22:16:38 +0100117 if not isinstance(exc, exception.BotoServerError):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200118 return "%r not an BotoServerError instance" % exc
119 LOG.info("Status: %s , error_code: %s", exc.status, exc.error_code)
120 if re.match(self.STATUS_RE, str(exc.status)) is None:
121 return ("Status code (%s) does not match"
122 "the expected re pattern \"%s\""
123 % (exc.status, self.STATUS_RE))
124 if re.match(self.CODE_RE, str(exc.error_code)) is None:
125 return ("Error code (%s) does not match" +
126 "the expected re pattern \"%s\"") %\
127 (exc.error_code, self.CODE_RE)
Attila Fazekas55c597d2014-02-21 13:10:41 +0100128 return None
Attila Fazekasa23f5002012-10-23 19:32:45 +0200129
130
131class ClientError(BotoExceptionMatcher):
132 STATUS_RE = r'4\d\d'
133
134
135class ServerError(BotoExceptionMatcher):
136 STATUS_RE = r'5\d\d'
137
138
139def _add_matcher_class(error_cls, error_data, base=BotoExceptionMatcher):
140 """
141 Usable for adding an ExceptionMatcher(s) into the exception tree.
142 The not leaf elements does wildcard match
143 """
144 # in error_code just literal and '.' characters expected
llg821243b20502014-02-22 10:32:49 +0800145 if not isinstance(error_data, six.string_types):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200146 (error_code, status_code) = map(str, error_data)
147 else:
148 status_code = None
149 error_code = error_data
150 parts = error_code.split('.')
151 basematch = ""
152 num_parts = len(parts)
153 max_index = num_parts - 1
154 add_cls = error_cls
llg821243b20502014-02-22 10:32:49 +0800155 for i_part in six.moves.xrange(num_parts):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200156 part = parts[i_part]
157 leaf = i_part == max_index
158 if not leaf:
159 match = basematch + part + "[.].*"
160 else:
161 match = basematch + part
162
163 basematch += part + "[.]"
164 if not hasattr(add_cls, part):
165 cls_dict = {"CODE_RE": match}
166 if leaf and status_code is not None:
167 cls_dict["STATUS_RE"] = status_code
168 cls = type(part, (base, ), cls_dict)
169 setattr(add_cls, part, cls())
170 add_cls = cls
171 elif leaf:
172 raise LookupError("Tries to redefine an error code \"%s\"" % part)
173 else:
174 add_cls = getattr(add_cls, part)
175
176
Attila Fazekas3e381f72013-08-01 16:52:23 +0200177# TODO(afazekas): classmethod handling
Attila Fazekasa23f5002012-10-23 19:32:45 +0200178def friendly_function_name_simple(call_able):
179 name = ""
180 if hasattr(call_able, "im_class"):
181 name += call_able.im_class.__name__ + "."
182 name += call_able.__name__
183 return name
184
185
186def friendly_function_call_str(call_able, *args, **kwargs):
187 string = friendly_function_name_simple(call_able)
188 string += "(" + ", ".join(map(str, args))
189 if len(kwargs):
190 if len(args):
191 string += ", "
192 string += ", ".join("=".join(map(str, (key, value)))
Matthew Treinish1d14c542014-06-17 20:25:40 -0400193 for (key, value) in kwargs.items())
Attila Fazekasa23f5002012-10-23 19:32:45 +0200194 return string + ")"
195
196
Attila Fazekasdc216422013-01-29 15:12:14 +0100197class BotoTestCase(tempest.test.BaseTestCase):
Sean Daguef237ccb2013-01-04 15:19:14 -0500198 """Recommended to use as base class for boto related test."""
Chris Yeoh8a79b9d2013-01-18 19:32:47 +1030199
Andrea Frittolib21de6c2015-02-06 20:12:38 +0000200 credentials = ['primary']
201
Attila Fazekasa23f5002012-10-23 19:32:45 +0200202 @classmethod
Matthew Treinishdfd7ac02015-02-09 17:47:31 -0500203 def skip_checks(cls):
204 super(BotoTestCase, cls).skip_checks()
205 if not CONF.compute_feature_enabled.ec2_api:
206 raise cls.skipException("The EC2 API is not available")
Andrea Frittoli046953c2015-03-12 11:20:04 +0000207 if not CONF.identity_feature_enabled.api_v2 or \
208 not CONF.identity.auth_version == 'v2':
209 raise cls.skipException("Identity v2 is not available")
Matthew Treinishdfd7ac02015-02-09 17:47:31 -0500210
211 @classmethod
Andrea Frittoli29fea352014-09-15 13:31:14 +0100212 def resource_setup(cls):
213 super(BotoTestCase, cls).resource_setup()
Matthew Treinishf054a9c2013-10-28 21:34:47 -0400214 cls.conclusion = decision_maker()
Attila Fazekasa23f5002012-10-23 19:32:45 +0200215 # The trash contains cleanup functions and paramaters in tuples
216 # (function, *args, **kwargs)
217 cls._resource_trash_bin = {}
218 cls._sequence = -1
219 if (hasattr(cls, "EC2") and
Attila Fazekas234d3e82013-02-22 16:39:49 +0100220 cls.conclusion['EC2_CAN_CONNECT_ERROR'] is not None):
ivan-zhu1feeb382013-01-24 10:14:39 +0800221 raise cls.skipException("EC2 " + cls.__name__ + ": " +
Attila Fazekas234d3e82013-02-22 16:39:49 +0100222 cls.conclusion['EC2_CAN_CONNECT_ERROR'])
Attila Fazekasa23f5002012-10-23 19:32:45 +0200223 if (hasattr(cls, "S3") and
Attila Fazekas234d3e82013-02-22 16:39:49 +0100224 cls.conclusion['S3_CAN_CONNECT_ERROR'] is not None):
ivan-zhu1feeb382013-01-24 10:14:39 +0800225 raise cls.skipException("S3 " + cls.__name__ + ": " +
Attila Fazekas234d3e82013-02-22 16:39:49 +0100226 cls.conclusion['S3_CAN_CONNECT_ERROR'])
Attila Fazekasa23f5002012-10-23 19:32:45 +0200227
228 @classmethod
229 def addResourceCleanUp(cls, function, *args, **kwargs):
230 """Adds CleanUp callable, used by tearDownClass.
Attila Fazekasb2902af2013-02-16 16:22:44 +0100231 Recommended to a use (deep)copy on the mutable args.
232 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200233 cls._sequence = cls._sequence + 1
234 cls._resource_trash_bin[cls._sequence] = (function, args, kwargs)
235 return cls._sequence
236
237 @classmethod
238 def cancelResourceCleanUp(cls, key):
Sean Daguef237ccb2013-01-04 15:19:14 -0500239 """Cancel Clean up request."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200240 del cls._resource_trash_bin[key]
241
Attila Fazekas3e381f72013-08-01 16:52:23 +0200242 # TODO(afazekas): Add "with" context handling
Attila Fazekasa23f5002012-10-23 19:32:45 +0200243 def assertBotoError(self, excMatcher, callableObj,
244 *args, **kwargs):
245 """Example usage:
246 self.assertBotoError(self.ec2_error_code.client.
247 InvalidKeyPair.Duplicate,
248 self.client.create_keypair,
Attila Fazekasb2902af2013-02-16 16:22:44 +0100249 key_name)
250 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200251 try:
252 callableObj(*args, **kwargs)
Attila Fazekas40aa3612013-01-19 22:16:38 +0100253 except exception.BotoServerError as exc:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200254 error_msg = excMatcher.match(exc)
255 if error_msg is not None:
256 raise self.failureException, error_msg
257 else:
258 raise self.failureException, "BotoServerError not raised"
259
260 @classmethod
Andrea Frittoli29fea352014-09-15 13:31:14 +0100261 def resource_cleanup(cls):
Attila Fazekasb2902af2013-02-16 16:22:44 +0100262 """Calls the callables added by addResourceCleanUp,
Yair Frieda039f872014-01-02 12:11:10 +0200263 when you overwrite this function don't forget to call this too.
Attila Fazekasb2902af2013-02-16 16:22:44 +0100264 """
Attila Fazekasa23f5002012-10-23 19:32:45 +0200265 fail_count = 0
266 trash_keys = sorted(cls._resource_trash_bin, reverse=True)
267 for key in trash_keys:
268 (function, pos_args, kw_args) = cls._resource_trash_bin[key]
269 try:
Yair Frieda039f872014-01-02 12:11:10 +0200270 func_name = friendly_function_call_str(function, *pos_args,
271 **kw_args)
272 LOG.debug("Cleaning up: %s" % func_name)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200273 function(*pos_args, **kw_args)
Yair Frieda039f872014-01-02 12:11:10 +0200274 except BaseException:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200275 fail_count += 1
Yair Frieda039f872014-01-02 12:11:10 +0200276 LOG.exception("Cleanup failed %s" % func_name)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200277 finally:
278 del cls._resource_trash_bin[key]
Andrea Frittoli29fea352014-09-15 13:31:14 +0100279 super(BotoTestCase, cls).resource_cleanup()
Attila Fazekasf86fa312013-07-30 19:56:39 +0200280 # NOTE(afazekas): let the super called even on exceptions
281 # The real exceptions already logged, if the super throws another,
282 # does not causes hidden issues
Attila Fazekasa23f5002012-10-23 19:32:45 +0200283 if fail_count:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100284 raise exceptions.TearDownException(num=fail_count)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200285
286 ec2_error_code = BotoExceptionMatcher()
287 # InsufficientInstanceCapacity can be both server and client error
288 ec2_error_code.server = ServerError()
289 ec2_error_code.client = ClientError()
290 s3_error_code = BotoExceptionMatcher()
291 s3_error_code.server = ServerError()
292 s3_error_code.client = ClientError()
293 valid_image_state = set(('available', 'pending', 'failed'))
Attila Fazekas3e381f72013-08-01 16:52:23 +0200294 # NOTE(afazekas): 'paused' is not valid status in EC2, but it does not have
Attila Fazekasc66ee652013-01-31 06:56:13 +0100295 # a good mapping, because it uses memory, but not really a running machine
Attila Fazekasa23f5002012-10-23 19:32:45 +0200296 valid_instance_state = set(('pending', 'running', 'shutting-down',
Attila Fazekasc66ee652013-01-31 06:56:13 +0100297 'terminated', 'stopping', 'stopped', 'paused'))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200298 valid_volume_status = set(('creating', 'available', 'in-use',
299 'deleting', 'deleted', 'error'))
300 valid_snapshot_status = set(('pending', 'completed', 'error'))
301
Attila Fazekas37f83042013-01-12 16:13:03 +0100302 gone_set = set(('_GONE',))
303
Attila Fazekas40aa3612013-01-19 22:16:38 +0100304 @classmethod
305 def get_lfunction_gone(cls, obj):
Sean Dague2416cf32013-04-10 08:29:07 -0400306 """If the object is instance of a well know type returns back with
Attila Fazekas40aa3612013-01-19 22:16:38 +0100307 with the correspoding function otherwise it assumes the obj itself
Sean Dague2416cf32013-04-10 08:29:07 -0400308 is the function.
309 """
Attila Fazekas40aa3612013-01-19 22:16:38 +0100310 ec = cls.ec2_error_code
311 if isinstance(obj, ec2.instance.Instance):
312 colusure_matcher = ec.client.InvalidInstanceID.NotFound
313 status_attr = "state"
314 elif isinstance(obj, ec2.image.Image):
315 colusure_matcher = ec.client.InvalidAMIID.NotFound
316 status_attr = "state"
317 elif isinstance(obj, ec2.snapshot.Snapshot):
318 colusure_matcher = ec.client.InvalidSnapshot.NotFound
319 status_attr = "status"
320 elif isinstance(obj, ec2.volume.Volume):
321 colusure_matcher = ec.client.InvalidVolume.NotFound
322 status_attr = "status"
323 else:
324 return obj
325
326 def _status():
327 try:
328 obj.update(validate=True)
329 except ValueError:
330 return "_GONE"
331 except exception.EC2ResponseError as exc:
Attila Fazekas55c597d2014-02-21 13:10:41 +0100332 if colusure_matcher.match(exc) is None:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100333 return "_GONE"
334 else:
335 raise
336 return getattr(obj, status_attr)
337
338 return _status
339
Attila Fazekas37f83042013-01-12 16:13:03 +0100340 def state_wait_gone(self, lfunction, final_set, valid_set):
341 if not isinstance(final_set, set):
342 final_set = set((final_set,))
343 final_set |= self.gone_set
Attila Fazekas40aa3612013-01-19 22:16:38 +0100344 lfunction = self.get_lfunction_gone(lfunction)
Masayuki Igawa224a8272014-02-17 15:07:43 +0900345 state = wait.state_wait(lfunction, final_set, valid_set)
Attila Fazekas37f83042013-01-12 16:13:03 +0100346 self.assertIn(state, valid_set | self.gone_set)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200347 return state
348
Attila Fazekas37f83042013-01-12 16:13:03 +0100349 def waitImageState(self, lfunction, wait_for):
350 return self.state_wait_gone(lfunction, wait_for,
351 self.valid_image_state)
352
Attila Fazekasa23f5002012-10-23 19:32:45 +0200353 def waitInstanceState(self, lfunction, wait_for):
Attila Fazekas37f83042013-01-12 16:13:03 +0100354 return self.state_wait_gone(lfunction, wait_for,
355 self.valid_instance_state)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200356
Attila Fazekasa23f5002012-10-23 19:32:45 +0200357 def waitSnapshotStatus(self, lfunction, wait_for):
Attila Fazekas37f83042013-01-12 16:13:03 +0100358 return self.state_wait_gone(lfunction, wait_for,
359 self.valid_snapshot_status)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200360
Attila Fazekas40aa3612013-01-19 22:16:38 +0100361 def waitVolumeStatus(self, lfunction, wait_for):
362 return self.state_wait_gone(lfunction, wait_for,
363 self.valid_volume_status)
364
Attila Fazekasa23f5002012-10-23 19:32:45 +0200365 def assertImageStateWait(self, lfunction, wait_for):
366 state = self.waitImageState(lfunction, wait_for)
367 self.assertIn(state, wait_for)
368
369 def assertInstanceStateWait(self, lfunction, wait_for):
370 state = self.waitInstanceState(lfunction, wait_for)
371 self.assertIn(state, wait_for)
372
373 def assertVolumeStatusWait(self, lfunction, wait_for):
374 state = self.waitVolumeStatus(lfunction, wait_for)
375 self.assertIn(state, wait_for)
376
377 def assertSnapshotStatusWait(self, lfunction, wait_for):
378 state = self.waitSnapshotStatus(lfunction, wait_for)
379 self.assertIn(state, wait_for)
380
381 def assertAddressDissasociatedWait(self, address):
382
383 def _disassociate():
384 cli = self.ec2_client
385 addresses = cli.get_all_addresses(addresses=(address.public_ip,))
386 if len(addresses) != 1:
387 return "INVALID"
388 if addresses[0].instance_id:
389 LOG.info("%s associated to %s",
390 address.public_ip,
391 addresses[0].instance_id)
392 return "ASSOCIATED"
393 return "DISASSOCIATED"
394
Masayuki Igawa224a8272014-02-17 15:07:43 +0900395 state = wait.state_wait(_disassociate, "DISASSOCIATED",
396 set(("ASSOCIATED", "DISASSOCIATED")))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200397 self.assertEqual(state, "DISASSOCIATED")
398
399 def assertAddressReleasedWait(self, address):
400
401 def _address_delete():
Attila Fazekas3e381f72013-08-01 16:52:23 +0200402 # NOTE(afazekas): the filter gives back IP
Attila Fazekasa23f5002012-10-23 19:32:45 +0200403 # even if it is not associated to my tenant
404 if (address.public_ip not in map(lambda a: a.public_ip,
405 self.ec2_client.get_all_addresses())):
406 return "DELETED"
407 return "NOTDELETED"
408
Masayuki Igawa224a8272014-02-17 15:07:43 +0900409 state = wait.state_wait(_address_delete, "DELETED")
Attila Fazekasa23f5002012-10-23 19:32:45 +0200410 self.assertEqual(state, "DELETED")
411
412 def assertReSearch(self, regexp, string):
413 if re.search(regexp, string) is None:
414 raise self.failureException("regexp: '%s' not found in '%s'" %
415 (regexp, string))
416
417 def assertNotReSearch(self, regexp, string):
418 if re.search(regexp, string) is not None:
419 raise self.failureException("regexp: '%s' found in '%s'" %
420 (regexp, string))
421
422 def assertReMatch(self, regexp, string):
423 if re.match(regexp, string) is None:
424 raise self.failureException("regexp: '%s' not matches on '%s'" %
425 (regexp, string))
426
427 def assertNotReMatch(self, regexp, string):
428 if re.match(regexp, string) is not None:
429 raise self.failureException("regexp: '%s' matches on '%s'" %
430 (regexp, string))
431
432 @classmethod
433 def destroy_bucket(cls, connection_data, bucket):
Sean Daguef237ccb2013-01-04 15:19:14 -0500434 """Destroys the bucket and its content, just for teardown."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200435 exc_num = 0
436 try:
Monty Taylorb2ca5ca2013-04-28 18:00:21 -0700437 with contextlib.closing(
438 boto.connect_s3(**connection_data)) as conn:
Attila Fazekasa23f5002012-10-23 19:32:45 +0200439 if isinstance(bucket, basestring):
440 bucket = conn.lookup(bucket)
Attila Fazekas40aa3612013-01-19 22:16:38 +0100441 assert isinstance(bucket, s3.bucket.Bucket)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200442 for obj in bucket.list():
443 try:
444 bucket.delete_key(obj.key)
445 obj.close()
Yair Frieda039f872014-01-02 12:11:10 +0200446 except BaseException:
447 LOG.exception("Failed to delete key %s " % obj.key)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200448 exc_num += 1
449 conn.delete_bucket(bucket)
Yair Frieda039f872014-01-02 12:11:10 +0200450 except BaseException:
451 LOG.exception("Failed to destroy bucket %s " % bucket)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200452 exc_num += 1
453 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100454 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200455
456 @classmethod
457 def destroy_reservation(cls, reservation):
Sean Daguef237ccb2013-01-04 15:19:14 -0500458 """Terminate instances in a reservation, just for teardown."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200459 exc_num = 0
460
461 def _instance_state():
462 try:
463 instance.update(validate=True)
464 except ValueError:
Attila Fazekas37f83042013-01-12 16:13:03 +0100465 return "_GONE"
Attila Fazekas40aa3612013-01-19 22:16:38 +0100466 except exception.EC2ResponseError as exc:
Attila Fazekas37f83042013-01-12 16:13:03 +0100467 if cls.ec2_error_code.\
Attila Fazekas55c597d2014-02-21 13:10:41 +0100468 client.InvalidInstanceID.NotFound.match(exc) is None:
Attila Fazekas37f83042013-01-12 16:13:03 +0100469 return "_GONE"
Attila Fazekas3e381f72013-08-01 16:52:23 +0200470 # NOTE(afazekas): incorrect code,
Attila Fazekas37f83042013-01-12 16:13:03 +0100471 # but the resource must be destoreyd
472 if exc.error_code == "InstanceNotFound":
473 return "_GONE"
474
Attila Fazekasa23f5002012-10-23 19:32:45 +0200475 return instance.state
476
477 for instance in reservation.instances:
478 try:
479 instance.terminate()
Masayuki Igawa224a8272014-02-17 15:07:43 +0900480 wait.re_search_wait(_instance_state, "_GONE")
Yair Frieda039f872014-01-02 12:11:10 +0200481 except BaseException:
482 LOG.exception("Failed to terminate instance %s " % instance)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200483 exc_num += 1
484 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100485 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200486
Attila Fazekas3e381f72013-08-01 16:52:23 +0200487 # NOTE(afazekas): The incorrect ErrorCodes makes very, very difficult
Attila Fazekasa23f5002012-10-23 19:32:45 +0200488 # to write better teardown
489
490 @classmethod
491 def destroy_security_group_wait(cls, group):
492 """Delete group.
493 Use just for teardown!
494 """
Attila Fazekas3e381f72013-08-01 16:52:23 +0200495 # NOTE(afazekas): should wait/try until all related instance terminates
Attila Fazekasa23f5002012-10-23 19:32:45 +0200496 group.delete()
497
498 @classmethod
499 def destroy_volume_wait(cls, volume):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700500 """Delete volume, tries to detach first.
Attila Fazekasa23f5002012-10-23 19:32:45 +0200501 Use just for teardown!
502 """
503 exc_num = 0
504 snaps = volume.snapshots()
505 if len(snaps):
506 LOG.critical("%s Volume has %s snapshot(s)", volume.id,
Attila Fazekasfa756cb2013-02-12 10:52:42 +0100507 map(snaps.id, snaps))
Attila Fazekasa23f5002012-10-23 19:32:45 +0200508
Attila Fazekas3e381f72013-08-01 16:52:23 +0200509 # NOTE(afazekas): detaching/attching not valid EC2 status
Attila Fazekasa23f5002012-10-23 19:32:45 +0200510 def _volume_state():
511 volume.update(validate=True)
512 try:
ghanshyam308640c2014-10-14 17:23:31 +0900513 # NOTE(gmann): Make sure volume is attached.
514 # Checking status as 'not "available"' is not enough to make
515 # sure volume is attached as it can be in "error" state
516 if volume.status == "in-use":
Attila Fazekasa23f5002012-10-23 19:32:45 +0200517 volume.detach(force=True)
Yair Frieda039f872014-01-02 12:11:10 +0200518 except BaseException:
519 LOG.exception("Failed to detach volume %s" % volume)
Attila Fazekas3e381f72013-08-01 16:52:23 +0200520 # exc_num += 1 "nonlocal" not in python2
Attila Fazekasa23f5002012-10-23 19:32:45 +0200521 return volume.status
522
523 try:
Masayuki Igawa224a8272014-02-17 15:07:43 +0900524 wait.re_search_wait(_volume_state, "available")
525 # not validates status
Attila Fazekasa23f5002012-10-23 19:32:45 +0200526 LOG.info(_volume_state())
527 volume.delete()
Yair Frieda039f872014-01-02 12:11:10 +0200528 except BaseException:
529 LOG.exception("Failed to delete volume %s" % volume)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200530 exc_num += 1
531 if exc_num:
Attila Fazekas40aa3612013-01-19 22:16:38 +0100532 raise exceptions.TearDownException(num=exc_num)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200533
534 @classmethod
535 def destroy_snapshot_wait(cls, snapshot):
Rafael Riveroc61bec72014-09-18 15:49:20 -0700536 """delete snapshot, wait until it ceases to exist."""
Attila Fazekasa23f5002012-10-23 19:32:45 +0200537 snapshot.delete()
538
539 def _update():
540 snapshot.update(validate=True)
541
Masayuki Igawa224a8272014-02-17 15:07:43 +0900542 wait.wait_exception(_update)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200543
544
545# you can specify tuples if you want to specify the status pattern
546for code in ('AddressLimitExceeded', 'AttachmentLimitExceeded', 'AuthFailure',
547 'Blocked', 'CustomerGatewayLimitExceeded', 'DependencyViolation',
548 'DiskImageSizeTooLarge', 'FilterLimitExceeded',
549 'Gateway.NotAttached', 'IdempotentParameterMismatch',
550 'IncorrectInstanceState', 'IncorrectState',
551 'InstanceLimitExceeded', 'InsufficientInstanceCapacity',
552 'InsufficientReservedInstancesCapacity',
553 'InternetGatewayLimitExceeded', 'InvalidAMIAttributeItemValue',
554 'InvalidAMIID.Malformed', 'InvalidAMIID.NotFound',
555 'InvalidAMIID.Unavailable', 'InvalidAssociationID.NotFound',
556 'InvalidAttachment.NotFound', 'InvalidConversionTaskId',
557 'InvalidCustomerGateway.DuplicateIpAddress',
558 'InvalidCustomerGatewayID.NotFound', 'InvalidDevice.InUse',
559 'InvalidDhcpOptionsID.NotFound', 'InvalidFormat',
560 'InvalidFilter', 'InvalidGatewayID.NotFound',
561 'InvalidGroup.Duplicate', 'InvalidGroupId.Malformed',
562 'InvalidGroup.InUse', 'InvalidGroup.NotFound',
563 'InvalidGroup.Reserved', 'InvalidInstanceID.Malformed',
564 'InvalidInstanceID.NotFound',
565 'InvalidInternetGatewayID.NotFound', 'InvalidIPAddress.InUse',
566 'InvalidKeyPair.Duplicate', 'InvalidKeyPair.Format',
567 'InvalidKeyPair.NotFound', 'InvalidManifest',
568 'InvalidNetworkAclEntry.NotFound',
569 'InvalidNetworkAclID.NotFound', 'InvalidParameterCombination',
570 'InvalidParameterValue', 'InvalidPermission.Duplicate',
571 'InvalidPermission.Malformed', 'InvalidReservationID.Malformed',
572 'InvalidReservationID.NotFound', 'InvalidRoute.NotFound',
573 'InvalidRouteTableID.NotFound',
574 'InvalidSecurity.RequestHasExpired',
575 'InvalidSnapshotID.Malformed', 'InvalidSnapshot.NotFound',
576 'InvalidUserID.Malformed', 'InvalidReservedInstancesId',
577 'InvalidReservedInstancesOfferingId',
578 'InvalidSubnetID.NotFound', 'InvalidVolumeID.Duplicate',
579 'InvalidVolumeID.Malformed', 'InvalidVolumeID.ZoneMismatch',
580 'InvalidVolume.NotFound', 'InvalidVpcID.NotFound',
581 'InvalidVpnConnectionID.NotFound',
582 'InvalidVpnGatewayID.NotFound',
583 'InvalidZone.NotFound', 'LegacySecurityGroup',
584 'MissingParameter', 'NetworkAclEntryAlreadyExists',
585 'NetworkAclEntryLimitExceeded', 'NetworkAclLimitExceeded',
586 'NonEBSInstance', 'PendingSnapshotLimitExceeded',
587 'PendingVerification', 'OptInRequired', 'RequestLimitExceeded',
588 'ReservedInstancesLimitExceeded', 'Resource.AlreadyAssociated',
589 'ResourceLimitExceeded', 'RouteAlreadyExists',
590 'RouteLimitExceeded', 'RouteTableLimitExceeded',
591 'RulesPerSecurityGroupLimitExceeded',
592 'SecurityGroupLimitExceeded',
593 'SecurityGroupsPerInstanceLimitExceeded',
594 'SnapshotLimitExceeded', 'SubnetLimitExceeded',
595 'UnknownParameter', 'UnsupportedOperation',
596 'VolumeLimitExceeded', 'VpcLimitExceeded',
597 'VpnConnectionLimitExceeded',
598 'VpnGatewayAttachmentLimitExceeded', 'VpnGatewayLimitExceeded'):
599 _add_matcher_class(BotoTestCase.ec2_error_code.client,
600 code, base=ClientError)
601
602for code in ('InsufficientAddressCapacity', 'InsufficientInstanceCapacity',
603 'InsufficientReservedInstanceCapacity', 'InternalError',
604 'Unavailable'):
605 _add_matcher_class(BotoTestCase.ec2_error_code.server,
606 code, base=ServerError)
607
608
609for code in (('AccessDenied', 403),
Matthew Treinish1d14c542014-06-17 20:25:40 -0400610 ('AccountProblem', 403),
611 ('AmbiguousGrantByEmailAddress', 400),
612 ('BadDigest', 400),
613 ('BucketAlreadyExists', 409),
614 ('BucketAlreadyOwnedByYou', 409),
615 ('BucketNotEmpty', 409),
616 ('CredentialsNotSupported', 400),
617 ('CrossLocationLoggingProhibited', 403),
618 ('EntityTooSmall', 400),
619 ('EntityTooLarge', 400),
620 ('ExpiredToken', 400),
621 ('IllegalVersioningConfigurationException', 400),
622 ('IncompleteBody', 400),
623 ('IncorrectNumberOfFilesInPostRequest', 400),
624 ('InlineDataTooLarge', 400),
625 ('InvalidAccessKeyId', 403),
Attila Fazekasa23f5002012-10-23 19:32:45 +0200626 'InvalidAddressingHeader',
Matthew Treinish1d14c542014-06-17 20:25:40 -0400627 ('InvalidArgument', 400),
628 ('InvalidBucketName', 400),
629 ('InvalidBucketState', 409),
630 ('InvalidDigest', 400),
631 ('InvalidLocationConstraint', 400),
632 ('InvalidPart', 400),
633 ('InvalidPartOrder', 400),
634 ('InvalidPayer', 403),
635 ('InvalidPolicyDocument', 400),
636 ('InvalidRange', 416),
637 ('InvalidRequest', 400),
638 ('InvalidSecurity', 403),
639 ('InvalidSOAPRequest', 400),
640 ('InvalidStorageClass', 400),
641 ('InvalidTargetBucketForLogging', 400),
642 ('InvalidToken', 400),
643 ('InvalidURI', 400),
644 ('KeyTooLong', 400),
645 ('MalformedACLError', 400),
646 ('MalformedPOSTRequest', 400),
647 ('MalformedXML', 400),
648 ('MaxMessageLengthExceeded', 400),
649 ('MaxPostPreDataLengthExceededError', 400),
650 ('MetadataTooLarge', 400),
651 ('MethodNotAllowed', 405),
652 ('MissingAttachment'),
653 ('MissingContentLength', 411),
654 ('MissingRequestBodyError', 400),
655 ('MissingSecurityElement', 400),
656 ('MissingSecurityHeader', 400),
657 ('NoLoggingStatusForKey', 400),
658 ('NoSuchBucket', 404),
659 ('NoSuchKey', 404),
660 ('NoSuchLifecycleConfiguration', 404),
661 ('NoSuchUpload', 404),
662 ('NoSuchVersion', 404),
663 ('NotSignedUp', 403),
664 ('NotSuchBucketPolicy', 404),
665 ('OperationAborted', 409),
666 ('PermanentRedirect', 301),
667 ('PreconditionFailed', 412),
668 ('Redirect', 307),
669 ('RequestIsNotMultiPartContent', 400),
670 ('RequestTimeout', 400),
671 ('RequestTimeTooSkewed', 403),
672 ('RequestTorrentOfBucketError', 400),
673 ('SignatureDoesNotMatch', 403),
674 ('TemporaryRedirect', 307),
675 ('TokenRefreshRequired', 400),
676 ('TooManyBuckets', 400),
677 ('UnexpectedContent', 400),
678 ('UnresolvableGrantByEmailAddress', 400),
679 ('UserKeyMustBeSpecified', 400)):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200680 _add_matcher_class(BotoTestCase.s3_error_code.client,
681 code, base=ClientError)
682
683
684for code in (('InternalError', 500),
Matthew Treinish1d14c542014-06-17 20:25:40 -0400685 ('NotImplemented', 501),
686 ('ServiceUnavailable', 503),
687 ('SlowDown', 503)):
Attila Fazekasa23f5002012-10-23 19:32:45 +0200688 _add_matcher_class(BotoTestCase.s3_error_code.server,
689 code, base=ServerError)