| ZhiQiang Fan | 39f9722 | 2013-09-20 04:49:44 +0800 | [diff] [blame] | 1 | # Copyright 2012 OpenStack Foundation | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 2 | # 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 Taylor | b2ca5ca | 2013-04-28 18:00:21 -0700 | [diff] [blame] | 16 | import contextlib | 
| Mitsuhiko Yamazaki | 46818aa | 2013-04-18 17:49:17 +0900 | [diff] [blame] | 17 | import logging as orig_logging | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 18 | import os | 
| Matthew Treinish | a83a16e | 2012-12-07 13:44:02 -0500 | [diff] [blame] | 19 | import re | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 20 |  | 
| Matthew Treinish | a83a16e | 2012-12-07 13:44:02 -0500 | [diff] [blame] | 21 | import boto | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 22 | from boto import ec2 | 
 | 23 | from boto import exception | 
 | 24 | from boto import s3 | 
| Doug Hellmann | 583ce2c | 2015-03-11 14:55:46 +0000 | [diff] [blame] | 25 | from oslo_log import log as logging | 
| Matthew Treinish | 96e9e88 | 2014-06-09 18:37:19 -0400 | [diff] [blame] | 26 | import six | 
| Matthew Treinish | f077dd2 | 2015-04-23 09:37:41 -0400 | [diff] [blame] | 27 | from six.moves.urllib import parse as urlparse | 
| Andrea Frittoli | 32d7499 | 2015-03-06 17:01:07 +0000 | [diff] [blame] | 28 | from tempest_lib import exceptions as lib_exc | 
 | 29 |  | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 30 | import tempest.clients | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 31 | from tempest.common.utils import file_utils | 
| Sean Dague | 86bd842 | 2013-12-20 09:56:44 -0500 | [diff] [blame] | 32 | from tempest import config | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 33 | from tempest import exceptions | 
| Attila Fazekas | dc21642 | 2013-01-29 15:12:14 +0100 | [diff] [blame] | 34 | import tempest.test | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 35 | from tempest.thirdparty.boto.utils import wait | 
| Matthew Treinish | a83a16e | 2012-12-07 13:44:02 -0500 | [diff] [blame] | 36 |  | 
| Sean Dague | 86bd842 | 2013-12-20 09:56:44 -0500 | [diff] [blame] | 37 | CONF = config.CONF | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 38 | LOG = logging.getLogger(__name__) | 
 | 39 |  | 
 | 40 |  | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 41 | def 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 Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 49 |         return all(map(file_utils.have_effective_read_access, args)) | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 50 |  | 
| Sean Dague | 86bd842 | 2013-12-20 09:56:44 -0500 | [diff] [blame] | 51 |     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 Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 55 |  | 
 | 56 |     A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path) | 
 | 57 |     boto_logger = logging.getLogger('boto') | 
| Matthew Treinish | f4a9b0f | 2013-07-26 16:58:26 -0400 | [diff] [blame] | 58 |     level = boto_logger.logger.level | 
| Matthew Treinish | c795b9e | 2014-06-09 17:01:10 -0400 | [diff] [blame] | 59 |     # suppress logging for boto | 
 | 60 |     boto_logger.logger.setLevel(orig_logging.CRITICAL) | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 61 |  | 
 | 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") | 
| Andrea Frittoli | 32d7499 | 2015-03-06 17:01:07 +0000 | [diff] [blame] | 68 |     # NOTE(andreaf) Setting up an extra manager here is redundant, | 
 | 69 |     # and should be removed. | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 70 |     openstack = tempest.clients.Manager() | 
 | 71 |     try: | 
| Sean Dague | 86bd842 | 2013-12-20 09:56:44 -0500 | [diff] [blame] | 72 |         if urlparse.urlparse(CONF.boto.ec2_url).hostname is None: | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 73 |             raise Exception("Failed to get hostname from the ec2_url") | 
 | 74 |         ec2client = openstack.ec2api_client | 
 | 75 |         try: | 
 | 76 |             ec2client.get_all_regions() | 
 | 77 |         except exception.BotoServerError as exc: | 
 | 78 |                 if exc.error_code is None: | 
 | 79 |                     raise Exception("EC2 target does not looks EC2 service") | 
 | 80 |                 _cred_sub_check(ec2client.connection_data) | 
 | 81 |  | 
| Andrea Frittoli | 32d7499 | 2015-03-06 17:01:07 +0000 | [diff] [blame] | 82 |     except lib_exc.Unauthorized: | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 83 |         EC2_CAN_CONNECT_ERROR = "AWS credentials not set," +\ | 
| Attila Fazekas | 62aebc4 | 2015-03-12 07:38:19 +0100 | [diff] [blame] | 84 |                                 " also failed to get it from keystone" | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 85 |     except Exception as exc: | 
 | 86 |         EC2_CAN_CONNECT_ERROR = str(exc) | 
 | 87 |  | 
 | 88 |     try: | 
| Sean Dague | 86bd842 | 2013-12-20 09:56:44 -0500 | [diff] [blame] | 89 |         if urlparse.urlparse(CONF.boto.s3_url).hostname is None: | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 90 |             raise Exception("Failed to get hostname from the s3_url") | 
 | 91 |         s3client = openstack.s3_client | 
 | 92 |         try: | 
 | 93 |             s3client.get_bucket("^INVALID*#()@INVALID.") | 
 | 94 |         except exception.BotoServerError as exc: | 
 | 95 |             if exc.status == 403: | 
 | 96 |                 _cred_sub_check(s3client.connection_data) | 
 | 97 |     except Exception as exc: | 
 | 98 |         S3_CAN_CONNECT_ERROR = str(exc) | 
| Attila Fazekas | 62aebc4 | 2015-03-12 07:38:19 +0100 | [diff] [blame] | 99 |     except lib_exc.Unauthorized: | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 100 |         S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\ | 
| Matt Riedemann | 78b0f80 | 2015-02-09 14:06:07 -0800 | [diff] [blame] | 101 |                                " failed to get them even by keystoneclient" | 
| Matthew Treinish | f4a9b0f | 2013-07-26 16:58:26 -0400 | [diff] [blame] | 102 |     boto_logger.logger.setLevel(level) | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 103 |     return {'A_I_IMAGES_READY': A_I_IMAGES_READY, | 
 | 104 |             'S3_CAN_CONNECT_ERROR': S3_CAN_CONNECT_ERROR, | 
 | 105 |             'EC2_CAN_CONNECT_ERROR': EC2_CAN_CONNECT_ERROR} | 
 | 106 |  | 
 | 107 |  | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 108 | class BotoExceptionMatcher(object): | 
 | 109 |     STATUS_RE = r'[45]\d\d' | 
 | 110 |     CODE_RE = '.*'  # regexp makes sense in group match | 
 | 111 |  | 
 | 112 |     def match(self, exc): | 
| Rafael Rivero | c61bec7 | 2014-09-18 15:49:20 -0700 | [diff] [blame] | 113 |         """:returns: Returns with an error string if it does not match, | 
 | 114 |                returns with None when it matches. | 
| Attila Fazekas | 55c597d | 2014-02-21 13:10:41 +0100 | [diff] [blame] | 115 |         """ | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 116 |         if not isinstance(exc, exception.BotoServerError): | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 117 |             return "%r not an BotoServerError instance" % exc | 
 | 118 |         LOG.info("Status: %s , error_code: %s", exc.status, exc.error_code) | 
 | 119 |         if re.match(self.STATUS_RE, str(exc.status)) is None: | 
 | 120 |             return ("Status code (%s) does not match" | 
 | 121 |                     "the expected re pattern \"%s\"" | 
 | 122 |                     % (exc.status, self.STATUS_RE)) | 
 | 123 |         if re.match(self.CODE_RE, str(exc.error_code)) is None: | 
 | 124 |             return ("Error code (%s) does not match" + | 
 | 125 |                     "the expected re pattern \"%s\"") %\ | 
 | 126 |                    (exc.error_code, self.CODE_RE) | 
| Attila Fazekas | 55c597d | 2014-02-21 13:10:41 +0100 | [diff] [blame] | 127 |         return None | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 128 |  | 
 | 129 |  | 
 | 130 | class ClientError(BotoExceptionMatcher): | 
 | 131 |     STATUS_RE = r'4\d\d' | 
 | 132 |  | 
 | 133 |  | 
 | 134 | class ServerError(BotoExceptionMatcher): | 
 | 135 |     STATUS_RE = r'5\d\d' | 
 | 136 |  | 
 | 137 |  | 
 | 138 | def _add_matcher_class(error_cls, error_data, base=BotoExceptionMatcher): | 
 | 139 |     """ | 
 | 140 |         Usable for adding an ExceptionMatcher(s) into the exception tree. | 
 | 141 |         The not leaf elements does wildcard match | 
 | 142 |     """ | 
 | 143 |     # in error_code just literal and '.' characters expected | 
| llg8212 | 43b2050 | 2014-02-22 10:32:49 +0800 | [diff] [blame] | 144 |     if not isinstance(error_data, six.string_types): | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 145 |         (error_code, status_code) = map(str, error_data) | 
 | 146 |     else: | 
 | 147 |         status_code = None | 
 | 148 |         error_code = error_data | 
 | 149 |     parts = error_code.split('.') | 
 | 150 |     basematch = "" | 
 | 151 |     num_parts = len(parts) | 
 | 152 |     max_index = num_parts - 1 | 
 | 153 |     add_cls = error_cls | 
| llg8212 | 43b2050 | 2014-02-22 10:32:49 +0800 | [diff] [blame] | 154 |     for i_part in six.moves.xrange(num_parts): | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 155 |         part = parts[i_part] | 
 | 156 |         leaf = i_part == max_index | 
 | 157 |         if not leaf: | 
 | 158 |             match = basematch + part + "[.].*" | 
 | 159 |         else: | 
 | 160 |             match = basematch + part | 
 | 161 |  | 
 | 162 |         basematch += part + "[.]" | 
 | 163 |         if not hasattr(add_cls, part): | 
 | 164 |             cls_dict = {"CODE_RE": match} | 
 | 165 |             if leaf and status_code is not None: | 
 | 166 |                 cls_dict["STATUS_RE"] = status_code | 
 | 167 |             cls = type(part, (base, ), cls_dict) | 
 | 168 |             setattr(add_cls, part, cls()) | 
 | 169 |             add_cls = cls | 
 | 170 |         elif leaf: | 
 | 171 |             raise LookupError("Tries to redefine an error code \"%s\"" % part) | 
 | 172 |         else: | 
 | 173 |             add_cls = getattr(add_cls, part) | 
 | 174 |  | 
 | 175 |  | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 176 | # TODO(afazekas): classmethod handling | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 177 | def friendly_function_name_simple(call_able): | 
 | 178 |     name = "" | 
 | 179 |     if hasattr(call_able, "im_class"): | 
 | 180 |         name += call_able.im_class.__name__ + "." | 
 | 181 |     name += call_able.__name__ | 
 | 182 |     return name | 
 | 183 |  | 
 | 184 |  | 
 | 185 | def friendly_function_call_str(call_able, *args, **kwargs): | 
 | 186 |     string = friendly_function_name_simple(call_able) | 
 | 187 |     string += "(" + ", ".join(map(str, args)) | 
 | 188 |     if len(kwargs): | 
 | 189 |         if len(args): | 
 | 190 |             string += ", " | 
 | 191 |     string += ", ".join("=".join(map(str, (key, value))) | 
| Matthew Treinish | 1d14c54 | 2014-06-17 20:25:40 -0400 | [diff] [blame] | 192 |                         for (key, value) in kwargs.items()) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 193 |     return string + ")" | 
 | 194 |  | 
 | 195 |  | 
| Attila Fazekas | dc21642 | 2013-01-29 15:12:14 +0100 | [diff] [blame] | 196 | class BotoTestCase(tempest.test.BaseTestCase): | 
| Sean Dague | f237ccb | 2013-01-04 15:19:14 -0500 | [diff] [blame] | 197 |     """Recommended to use as base class for boto related test.""" | 
| Chris Yeoh | 8a79b9d | 2013-01-18 19:32:47 +1030 | [diff] [blame] | 198 |  | 
| Andrea Frittoli | b21de6c | 2015-02-06 20:12:38 +0000 | [diff] [blame] | 199 |     credentials = ['primary'] | 
 | 200 |  | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 201 |     @classmethod | 
| Matthew Treinish | dfd7ac0 | 2015-02-09 17:47:31 -0500 | [diff] [blame] | 202 |     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") | 
| Andrea Frittoli | 046953c | 2015-03-12 11:20:04 +0000 | [diff] [blame] | 206 |         if not CONF.identity_feature_enabled.api_v2 or \ | 
 | 207 |                 not CONF.identity.auth_version == 'v2': | 
 | 208 |             raise cls.skipException("Identity v2 is not available") | 
| Matthew Treinish | dfd7ac0 | 2015-02-09 17:47:31 -0500 | [diff] [blame] | 209 |  | 
 | 210 |     @classmethod | 
| Andrea Frittoli | 29fea35 | 2014-09-15 13:31:14 +0100 | [diff] [blame] | 211 |     def resource_setup(cls): | 
 | 212 |         super(BotoTestCase, cls).resource_setup() | 
| Matthew Treinish | f054a9c | 2013-10-28 21:34:47 -0400 | [diff] [blame] | 213 |         cls.conclusion = decision_maker() | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 214 |         # The trash contains cleanup functions and paramaters in tuples | 
 | 215 |         # (function, *args, **kwargs) | 
 | 216 |         cls._resource_trash_bin = {} | 
 | 217 |         cls._sequence = -1 | 
 | 218 |         if (hasattr(cls, "EC2") and | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 219 |             cls.conclusion['EC2_CAN_CONNECT_ERROR'] is not None): | 
| ivan-zhu | 1feeb38 | 2013-01-24 10:14:39 +0800 | [diff] [blame] | 220 |             raise cls.skipException("EC2 " + cls.__name__ + ": " + | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 221 |                                     cls.conclusion['EC2_CAN_CONNECT_ERROR']) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 222 |         if (hasattr(cls, "S3") and | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 223 |             cls.conclusion['S3_CAN_CONNECT_ERROR'] is not None): | 
| ivan-zhu | 1feeb38 | 2013-01-24 10:14:39 +0800 | [diff] [blame] | 224 |             raise cls.skipException("S3 " + cls.__name__ + ": " + | 
| Attila Fazekas | 234d3e8 | 2013-02-22 16:39:49 +0100 | [diff] [blame] | 225 |                                     cls.conclusion['S3_CAN_CONNECT_ERROR']) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 226 |  | 
 | 227 |     @classmethod | 
 | 228 |     def addResourceCleanUp(cls, function, *args, **kwargs): | 
 | 229 |         """Adds CleanUp callable, used by tearDownClass. | 
| Attila Fazekas | b2902af | 2013-02-16 16:22:44 +0100 | [diff] [blame] | 230 |         Recommended to a use (deep)copy on the mutable args. | 
 | 231 |         """ | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 232 |         cls._sequence = cls._sequence + 1 | 
 | 233 |         cls._resource_trash_bin[cls._sequence] = (function, args, kwargs) | 
 | 234 |         return cls._sequence | 
 | 235 |  | 
 | 236 |     @classmethod | 
 | 237 |     def cancelResourceCleanUp(cls, key): | 
| Sean Dague | f237ccb | 2013-01-04 15:19:14 -0500 | [diff] [blame] | 238 |         """Cancel Clean up request.""" | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 239 |         del cls._resource_trash_bin[key] | 
 | 240 |  | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 241 |     # TODO(afazekas): Add "with" context handling | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 242 |     def assertBotoError(self, excMatcher, callableObj, | 
 | 243 |                         *args, **kwargs): | 
 | 244 |         """Example usage: | 
 | 245 |             self.assertBotoError(self.ec2_error_code.client. | 
 | 246 |                                  InvalidKeyPair.Duplicate, | 
 | 247 |                                  self.client.create_keypair, | 
| Attila Fazekas | b2902af | 2013-02-16 16:22:44 +0100 | [diff] [blame] | 248 |                                  key_name) | 
 | 249 |         """ | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 250 |         try: | 
 | 251 |             callableObj(*args, **kwargs) | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 252 |         except exception.BotoServerError as exc: | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 253 |             error_msg = excMatcher.match(exc) | 
 | 254 |             if error_msg is not None: | 
| Matthew Treinish | 843227d | 2015-04-23 10:17:17 -0400 | [diff] [blame] | 255 |                 raise self.failureException(error_msg) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 256 |         else: | 
| Matthew Treinish | 843227d | 2015-04-23 10:17:17 -0400 | [diff] [blame] | 257 |             raise self.failureException("BotoServerError not raised") | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 258 |  | 
 | 259 |     @classmethod | 
| Andrea Frittoli | 29fea35 | 2014-09-15 13:31:14 +0100 | [diff] [blame] | 260 |     def resource_cleanup(cls): | 
| Attila Fazekas | b2902af | 2013-02-16 16:22:44 +0100 | [diff] [blame] | 261 |         """Calls the callables added by addResourceCleanUp, | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 262 |         when you overwrite this function don't forget to call this too. | 
| Attila Fazekas | b2902af | 2013-02-16 16:22:44 +0100 | [diff] [blame] | 263 |         """ | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 264 |         fail_count = 0 | 
 | 265 |         trash_keys = sorted(cls._resource_trash_bin, reverse=True) | 
 | 266 |         for key in trash_keys: | 
 | 267 |             (function, pos_args, kw_args) = cls._resource_trash_bin[key] | 
 | 268 |             try: | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 269 |                 func_name = friendly_function_call_str(function, *pos_args, | 
 | 270 |                                                        **kw_args) | 
 | 271 |                 LOG.debug("Cleaning up: %s" % func_name) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 272 |                 function(*pos_args, **kw_args) | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 273 |             except BaseException: | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 274 |                 fail_count += 1 | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 275 |                 LOG.exception("Cleanup failed %s" % func_name) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 276 |             finally: | 
 | 277 |                 del cls._resource_trash_bin[key] | 
| Andrea Frittoli | 29fea35 | 2014-09-15 13:31:14 +0100 | [diff] [blame] | 278 |         super(BotoTestCase, cls).resource_cleanup() | 
| Attila Fazekas | f86fa31 | 2013-07-30 19:56:39 +0200 | [diff] [blame] | 279 |         # NOTE(afazekas): let the super called even on exceptions | 
 | 280 |         # The real exceptions already logged, if the super throws another, | 
 | 281 |         # does not causes hidden issues | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 282 |         if fail_count: | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 283 |             raise exceptions.TearDownException(num=fail_count) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 284 |  | 
 | 285 |     ec2_error_code = BotoExceptionMatcher() | 
 | 286 |     # InsufficientInstanceCapacity can be both server and client error | 
 | 287 |     ec2_error_code.server = ServerError() | 
 | 288 |     ec2_error_code.client = ClientError() | 
 | 289 |     s3_error_code = BotoExceptionMatcher() | 
 | 290 |     s3_error_code.server = ServerError() | 
 | 291 |     s3_error_code.client = ClientError() | 
 | 292 |     valid_image_state = set(('available', 'pending', 'failed')) | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 293 |     # NOTE(afazekas): 'paused' is not valid status in EC2, but it does not have | 
| Attila Fazekas | c66ee65 | 2013-01-31 06:56:13 +0100 | [diff] [blame] | 294 |     # a good mapping, because it uses memory, but not really a running machine | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 295 |     valid_instance_state = set(('pending', 'running', 'shutting-down', | 
| Attila Fazekas | c66ee65 | 2013-01-31 06:56:13 +0100 | [diff] [blame] | 296 |                                 'terminated', 'stopping', 'stopped', 'paused')) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 297 |     valid_volume_status = set(('creating', 'available', 'in-use', | 
 | 298 |                                'deleting', 'deleted', 'error')) | 
 | 299 |     valid_snapshot_status = set(('pending', 'completed', 'error')) | 
 | 300 |  | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 301 |     gone_set = set(('_GONE',)) | 
 | 302 |  | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 303 |     @classmethod | 
 | 304 |     def get_lfunction_gone(cls, obj): | 
| Sean Dague | 2416cf3 | 2013-04-10 08:29:07 -0400 | [diff] [blame] | 305 |         """If the object is instance of a well know type returns back with | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 306 |             with the correspoding function otherwise it assumes the obj itself | 
| Sean Dague | 2416cf3 | 2013-04-10 08:29:07 -0400 | [diff] [blame] | 307 |             is the function. | 
 | 308 |             """ | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 309 |         ec = cls.ec2_error_code | 
 | 310 |         if isinstance(obj, ec2.instance.Instance): | 
 | 311 |             colusure_matcher = ec.client.InvalidInstanceID.NotFound | 
 | 312 |             status_attr = "state" | 
 | 313 |         elif isinstance(obj, ec2.image.Image): | 
 | 314 |             colusure_matcher = ec.client.InvalidAMIID.NotFound | 
 | 315 |             status_attr = "state" | 
 | 316 |         elif isinstance(obj, ec2.snapshot.Snapshot): | 
 | 317 |             colusure_matcher = ec.client.InvalidSnapshot.NotFound | 
 | 318 |             status_attr = "status" | 
 | 319 |         elif isinstance(obj, ec2.volume.Volume): | 
 | 320 |             colusure_matcher = ec.client.InvalidVolume.NotFound | 
 | 321 |             status_attr = "status" | 
 | 322 |         else: | 
 | 323 |             return obj | 
 | 324 |  | 
 | 325 |         def _status(): | 
 | 326 |             try: | 
 | 327 |                 obj.update(validate=True) | 
 | 328 |             except ValueError: | 
 | 329 |                 return "_GONE" | 
 | 330 |             except exception.EC2ResponseError as exc: | 
| Attila Fazekas | 55c597d | 2014-02-21 13:10:41 +0100 | [diff] [blame] | 331 |                 if colusure_matcher.match(exc) is None: | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 332 |                     return "_GONE" | 
 | 333 |                 else: | 
 | 334 |                     raise | 
 | 335 |             return getattr(obj, status_attr) | 
 | 336 |  | 
 | 337 |         return _status | 
 | 338 |  | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 339 |     def state_wait_gone(self, lfunction, final_set, valid_set): | 
 | 340 |         if not isinstance(final_set, set): | 
 | 341 |             final_set = set((final_set,)) | 
 | 342 |         final_set |= self.gone_set | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 343 |         lfunction = self.get_lfunction_gone(lfunction) | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 344 |         state = wait.state_wait(lfunction, final_set, valid_set) | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 345 |         self.assertIn(state, valid_set | self.gone_set) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 346 |         return state | 
 | 347 |  | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 348 |     def waitImageState(self, lfunction, wait_for): | 
 | 349 |         return self.state_wait_gone(lfunction, wait_for, | 
 | 350 |                                     self.valid_image_state) | 
 | 351 |  | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 352 |     def waitInstanceState(self, lfunction, wait_for): | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 353 |         return self.state_wait_gone(lfunction, wait_for, | 
 | 354 |                                     self.valid_instance_state) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 355 |  | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 356 |     def waitSnapshotStatus(self, lfunction, wait_for): | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 357 |         return self.state_wait_gone(lfunction, wait_for, | 
 | 358 |                                     self.valid_snapshot_status) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 359 |  | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 360 |     def waitVolumeStatus(self, lfunction, wait_for): | 
 | 361 |         return self.state_wait_gone(lfunction, wait_for, | 
 | 362 |                                     self.valid_volume_status) | 
 | 363 |  | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 364 |     def assertImageStateWait(self, lfunction, wait_for): | 
 | 365 |         state = self.waitImageState(lfunction, wait_for) | 
 | 366 |         self.assertIn(state, wait_for) | 
 | 367 |  | 
 | 368 |     def assertInstanceStateWait(self, lfunction, wait_for): | 
 | 369 |         state = self.waitInstanceState(lfunction, wait_for) | 
 | 370 |         self.assertIn(state, wait_for) | 
 | 371 |  | 
 | 372 |     def assertVolumeStatusWait(self, lfunction, wait_for): | 
 | 373 |         state = self.waitVolumeStatus(lfunction, wait_for) | 
 | 374 |         self.assertIn(state, wait_for) | 
 | 375 |  | 
 | 376 |     def assertSnapshotStatusWait(self, lfunction, wait_for): | 
 | 377 |         state = self.waitSnapshotStatus(lfunction, wait_for) | 
 | 378 |         self.assertIn(state, wait_for) | 
 | 379 |  | 
 | 380 |     def assertAddressDissasociatedWait(self, address): | 
 | 381 |  | 
 | 382 |         def _disassociate(): | 
 | 383 |             cli = self.ec2_client | 
 | 384 |             addresses = cli.get_all_addresses(addresses=(address.public_ip,)) | 
 | 385 |             if len(addresses) != 1: | 
 | 386 |                 return "INVALID" | 
 | 387 |             if addresses[0].instance_id: | 
 | 388 |                 LOG.info("%s associated to %s", | 
 | 389 |                          address.public_ip, | 
 | 390 |                          addresses[0].instance_id) | 
 | 391 |                 return "ASSOCIATED" | 
 | 392 |             return "DISASSOCIATED" | 
 | 393 |  | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 394 |         state = wait.state_wait(_disassociate, "DISASSOCIATED", | 
 | 395 |                                 set(("ASSOCIATED", "DISASSOCIATED"))) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 396 |         self.assertEqual(state, "DISASSOCIATED") | 
 | 397 |  | 
 | 398 |     def assertAddressReleasedWait(self, address): | 
 | 399 |  | 
 | 400 |         def _address_delete(): | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 401 |             # NOTE(afazekas): the filter gives back IP | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 402 |             # even if it is not associated to my tenant | 
 | 403 |             if (address.public_ip not in map(lambda a: a.public_ip, | 
 | 404 |                 self.ec2_client.get_all_addresses())): | 
 | 405 |                     return "DELETED" | 
 | 406 |             return "NOTDELETED" | 
 | 407 |  | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 408 |         state = wait.state_wait(_address_delete, "DELETED") | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 409 |         self.assertEqual(state, "DELETED") | 
 | 410 |  | 
 | 411 |     def assertReSearch(self, regexp, string): | 
 | 412 |         if re.search(regexp, string) is None: | 
 | 413 |             raise self.failureException("regexp: '%s' not found in '%s'" % | 
 | 414 |                                         (regexp, string)) | 
 | 415 |  | 
 | 416 |     def assertNotReSearch(self, regexp, string): | 
 | 417 |         if re.search(regexp, string) is not None: | 
 | 418 |             raise self.failureException("regexp: '%s' found in '%s'" % | 
 | 419 |                                         (regexp, string)) | 
 | 420 |  | 
 | 421 |     def assertReMatch(self, regexp, string): | 
 | 422 |         if re.match(regexp, string) is None: | 
 | 423 |             raise self.failureException("regexp: '%s' not matches on '%s'" % | 
 | 424 |                                         (regexp, string)) | 
 | 425 |  | 
 | 426 |     def assertNotReMatch(self, regexp, string): | 
 | 427 |         if re.match(regexp, string) is not None: | 
 | 428 |             raise self.failureException("regexp: '%s' matches on '%s'" % | 
 | 429 |                                         (regexp, string)) | 
 | 430 |  | 
 | 431 |     @classmethod | 
 | 432 |     def destroy_bucket(cls, connection_data, bucket): | 
| Sean Dague | f237ccb | 2013-01-04 15:19:14 -0500 | [diff] [blame] | 433 |         """Destroys the bucket and its content, just for teardown.""" | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 434 |         exc_num = 0 | 
 | 435 |         try: | 
| Monty Taylor | b2ca5ca | 2013-04-28 18:00:21 -0700 | [diff] [blame] | 436 |             with contextlib.closing( | 
 | 437 |                     boto.connect_s3(**connection_data)) as conn: | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 438 |                 if isinstance(bucket, basestring): | 
 | 439 |                     bucket = conn.lookup(bucket) | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 440 |                     assert isinstance(bucket, s3.bucket.Bucket) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 441 |                 for obj in bucket.list(): | 
 | 442 |                     try: | 
 | 443 |                         bucket.delete_key(obj.key) | 
 | 444 |                         obj.close() | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 445 |                     except BaseException: | 
 | 446 |                         LOG.exception("Failed to delete key %s " % obj.key) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 447 |                         exc_num += 1 | 
 | 448 |             conn.delete_bucket(bucket) | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 449 |         except BaseException: | 
 | 450 |             LOG.exception("Failed to destroy bucket %s " % bucket) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 451 |             exc_num += 1 | 
 | 452 |         if exc_num: | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 453 |             raise exceptions.TearDownException(num=exc_num) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 454 |  | 
 | 455 |     @classmethod | 
 | 456 |     def destroy_reservation(cls, reservation): | 
| Sean Dague | f237ccb | 2013-01-04 15:19:14 -0500 | [diff] [blame] | 457 |         """Terminate instances in a reservation, just for teardown.""" | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 458 |         exc_num = 0 | 
 | 459 |  | 
 | 460 |         def _instance_state(): | 
 | 461 |             try: | 
 | 462 |                 instance.update(validate=True) | 
 | 463 |             except ValueError: | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 464 |                 return "_GONE" | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 465 |             except exception.EC2ResponseError as exc: | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 466 |                 if cls.ec2_error_code.\ | 
| Attila Fazekas | 55c597d | 2014-02-21 13:10:41 +0100 | [diff] [blame] | 467 |                         client.InvalidInstanceID.NotFound.match(exc) is None: | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 468 |                     return "_GONE" | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 469 |                 # NOTE(afazekas): incorrect code, | 
| Attila Fazekas | 37f8304 | 2013-01-12 16:13:03 +0100 | [diff] [blame] | 470 |                 # but the resource must be destoreyd | 
 | 471 |                 if exc.error_code == "InstanceNotFound": | 
 | 472 |                     return "_GONE" | 
 | 473 |  | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 474 |             return instance.state | 
 | 475 |  | 
 | 476 |         for instance in reservation.instances: | 
 | 477 |             try: | 
 | 478 |                 instance.terminate() | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 479 |                 wait.re_search_wait(_instance_state, "_GONE") | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 480 |             except BaseException: | 
 | 481 |                 LOG.exception("Failed to terminate instance %s " % instance) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 482 |                 exc_num += 1 | 
 | 483 |         if exc_num: | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 484 |             raise exceptions.TearDownException(num=exc_num) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 485 |  | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 486 |     # NOTE(afazekas): The incorrect ErrorCodes makes very, very difficult | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 487 |     # to write better teardown | 
 | 488 |  | 
 | 489 |     @classmethod | 
 | 490 |     def destroy_security_group_wait(cls, group): | 
 | 491 |         """Delete group. | 
 | 492 |            Use just for teardown! | 
 | 493 |         """ | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 494 |         # NOTE(afazekas): should wait/try until all related instance terminates | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 495 |         group.delete() | 
 | 496 |  | 
 | 497 |     @classmethod | 
 | 498 |     def destroy_volume_wait(cls, volume): | 
| Rafael Rivero | c61bec7 | 2014-09-18 15:49:20 -0700 | [diff] [blame] | 499 |         """Delete volume, tries to detach first. | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 500 |            Use just for teardown! | 
 | 501 |         """ | 
 | 502 |         exc_num = 0 | 
 | 503 |         snaps = volume.snapshots() | 
 | 504 |         if len(snaps): | 
 | 505 |             LOG.critical("%s Volume has %s snapshot(s)", volume.id, | 
| Attila Fazekas | fa756cb | 2013-02-12 10:52:42 +0100 | [diff] [blame] | 506 |                          map(snaps.id, snaps)) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 507 |  | 
| Zhao Lei | 17548d9 | 2015-08-07 21:42:51 +0800 | [diff] [blame] | 508 |         # NOTE(afazekas): detaching/attaching not valid EC2 status | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 509 |         def _volume_state(): | 
 | 510 |             volume.update(validate=True) | 
 | 511 |             try: | 
| ghanshyam | 308640c | 2014-10-14 17:23:31 +0900 | [diff] [blame] | 512 |                 # NOTE(gmann): Make sure volume is attached. | 
 | 513 |                 # Checking status as 'not "available"' is not enough to make | 
 | 514 |                 # sure volume is attached as it can be in "error" state | 
 | 515 |                 if volume.status == "in-use": | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 516 |                     volume.detach(force=True) | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 517 |             except BaseException: | 
 | 518 |                 LOG.exception("Failed to detach volume %s" % volume) | 
| Attila Fazekas | 3e381f7 | 2013-08-01 16:52:23 +0200 | [diff] [blame] | 519 |                 # exc_num += 1 "nonlocal" not in python2 | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 520 |             return volume.status | 
 | 521 |  | 
 | 522 |         try: | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 523 |             wait.re_search_wait(_volume_state, "available") | 
 | 524 |             # not validates status | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 525 |             LOG.info(_volume_state()) | 
 | 526 |             volume.delete() | 
| Yair Fried | a039f87 | 2014-01-02 12:11:10 +0200 | [diff] [blame] | 527 |         except BaseException: | 
 | 528 |             LOG.exception("Failed to delete volume %s" % volume) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 529 |             exc_num += 1 | 
 | 530 |         if exc_num: | 
| Attila Fazekas | 40aa361 | 2013-01-19 22:16:38 +0100 | [diff] [blame] | 531 |             raise exceptions.TearDownException(num=exc_num) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 532 |  | 
 | 533 |     @classmethod | 
 | 534 |     def destroy_snapshot_wait(cls, snapshot): | 
| Rafael Rivero | c61bec7 | 2014-09-18 15:49:20 -0700 | [diff] [blame] | 535 |         """delete snapshot, wait until it ceases to exist.""" | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 536 |         snapshot.delete() | 
 | 537 |  | 
 | 538 |         def _update(): | 
 | 539 |             snapshot.update(validate=True) | 
 | 540 |  | 
| Masayuki Igawa | 224a827 | 2014-02-17 15:07:43 +0900 | [diff] [blame] | 541 |         wait.wait_exception(_update) | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 542 |  | 
 | 543 |  | 
 | 544 | # you can specify tuples if you want to specify the status pattern | 
 | 545 | for code in ('AddressLimitExceeded', 'AttachmentLimitExceeded', 'AuthFailure', | 
 | 546 |              'Blocked', 'CustomerGatewayLimitExceeded', 'DependencyViolation', | 
 | 547 |              'DiskImageSizeTooLarge', 'FilterLimitExceeded', | 
 | 548 |              'Gateway.NotAttached', 'IdempotentParameterMismatch', | 
 | 549 |              'IncorrectInstanceState', 'IncorrectState', | 
 | 550 |              'InstanceLimitExceeded', 'InsufficientInstanceCapacity', | 
 | 551 |              'InsufficientReservedInstancesCapacity', | 
 | 552 |              'InternetGatewayLimitExceeded', 'InvalidAMIAttributeItemValue', | 
 | 553 |              'InvalidAMIID.Malformed', 'InvalidAMIID.NotFound', | 
 | 554 |              'InvalidAMIID.Unavailable', 'InvalidAssociationID.NotFound', | 
 | 555 |              'InvalidAttachment.NotFound', 'InvalidConversionTaskId', | 
 | 556 |              'InvalidCustomerGateway.DuplicateIpAddress', | 
 | 557 |              'InvalidCustomerGatewayID.NotFound', 'InvalidDevice.InUse', | 
 | 558 |              'InvalidDhcpOptionsID.NotFound', 'InvalidFormat', | 
 | 559 |              'InvalidFilter', 'InvalidGatewayID.NotFound', | 
 | 560 |              'InvalidGroup.Duplicate', 'InvalidGroupId.Malformed', | 
 | 561 |              'InvalidGroup.InUse', 'InvalidGroup.NotFound', | 
 | 562 |              'InvalidGroup.Reserved', 'InvalidInstanceID.Malformed', | 
 | 563 |              'InvalidInstanceID.NotFound', | 
 | 564 |              'InvalidInternetGatewayID.NotFound', 'InvalidIPAddress.InUse', | 
 | 565 |              'InvalidKeyPair.Duplicate', 'InvalidKeyPair.Format', | 
 | 566 |              'InvalidKeyPair.NotFound', 'InvalidManifest', | 
 | 567 |              'InvalidNetworkAclEntry.NotFound', | 
 | 568 |              'InvalidNetworkAclID.NotFound', 'InvalidParameterCombination', | 
 | 569 |              'InvalidParameterValue', 'InvalidPermission.Duplicate', | 
 | 570 |              'InvalidPermission.Malformed', 'InvalidReservationID.Malformed', | 
 | 571 |              'InvalidReservationID.NotFound', 'InvalidRoute.NotFound', | 
 | 572 |              'InvalidRouteTableID.NotFound', | 
 | 573 |              'InvalidSecurity.RequestHasExpired', | 
 | 574 |              'InvalidSnapshotID.Malformed', 'InvalidSnapshot.NotFound', | 
 | 575 |              'InvalidUserID.Malformed', 'InvalidReservedInstancesId', | 
 | 576 |              'InvalidReservedInstancesOfferingId', | 
 | 577 |              'InvalidSubnetID.NotFound', 'InvalidVolumeID.Duplicate', | 
 | 578 |              'InvalidVolumeID.Malformed', 'InvalidVolumeID.ZoneMismatch', | 
 | 579 |              'InvalidVolume.NotFound', 'InvalidVpcID.NotFound', | 
 | 580 |              'InvalidVpnConnectionID.NotFound', | 
 | 581 |              'InvalidVpnGatewayID.NotFound', | 
 | 582 |              'InvalidZone.NotFound', 'LegacySecurityGroup', | 
 | 583 |              'MissingParameter', 'NetworkAclEntryAlreadyExists', | 
 | 584 |              'NetworkAclEntryLimitExceeded', 'NetworkAclLimitExceeded', | 
 | 585 |              'NonEBSInstance', 'PendingSnapshotLimitExceeded', | 
 | 586 |              'PendingVerification', 'OptInRequired', 'RequestLimitExceeded', | 
 | 587 |              'ReservedInstancesLimitExceeded', 'Resource.AlreadyAssociated', | 
 | 588 |              'ResourceLimitExceeded', 'RouteAlreadyExists', | 
 | 589 |              'RouteLimitExceeded', 'RouteTableLimitExceeded', | 
 | 590 |              'RulesPerSecurityGroupLimitExceeded', | 
 | 591 |              'SecurityGroupLimitExceeded', | 
 | 592 |              'SecurityGroupsPerInstanceLimitExceeded', | 
 | 593 |              'SnapshotLimitExceeded', 'SubnetLimitExceeded', | 
 | 594 |              'UnknownParameter', 'UnsupportedOperation', | 
 | 595 |              'VolumeLimitExceeded', 'VpcLimitExceeded', | 
 | 596 |              'VpnConnectionLimitExceeded', | 
 | 597 |              'VpnGatewayAttachmentLimitExceeded', 'VpnGatewayLimitExceeded'): | 
 | 598 |     _add_matcher_class(BotoTestCase.ec2_error_code.client, | 
 | 599 |                        code, base=ClientError) | 
 | 600 |  | 
 | 601 | for code in ('InsufficientAddressCapacity', 'InsufficientInstanceCapacity', | 
 | 602 |              'InsufficientReservedInstanceCapacity', 'InternalError', | 
 | 603 |              'Unavailable'): | 
 | 604 |     _add_matcher_class(BotoTestCase.ec2_error_code.server, | 
 | 605 |                        code, base=ServerError) | 
 | 606 |  | 
 | 607 |  | 
 | 608 | for code in (('AccessDenied', 403), | 
| Matthew Treinish | 1d14c54 | 2014-06-17 20:25:40 -0400 | [diff] [blame] | 609 |              ('AccountProblem', 403), | 
 | 610 |              ('AmbiguousGrantByEmailAddress', 400), | 
 | 611 |              ('BadDigest', 400), | 
 | 612 |              ('BucketAlreadyExists', 409), | 
 | 613 |              ('BucketAlreadyOwnedByYou', 409), | 
 | 614 |              ('BucketNotEmpty', 409), | 
 | 615 |              ('CredentialsNotSupported', 400), | 
 | 616 |              ('CrossLocationLoggingProhibited', 403), | 
 | 617 |              ('EntityTooSmall', 400), | 
 | 618 |              ('EntityTooLarge', 400), | 
 | 619 |              ('ExpiredToken', 400), | 
 | 620 |              ('IllegalVersioningConfigurationException', 400), | 
 | 621 |              ('IncompleteBody', 400), | 
 | 622 |              ('IncorrectNumberOfFilesInPostRequest', 400), | 
 | 623 |              ('InlineDataTooLarge', 400), | 
 | 624 |              ('InvalidAccessKeyId', 403), | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 625 |              'InvalidAddressingHeader', | 
| Matthew Treinish | 1d14c54 | 2014-06-17 20:25:40 -0400 | [diff] [blame] | 626 |              ('InvalidArgument', 400), | 
 | 627 |              ('InvalidBucketName', 400), | 
 | 628 |              ('InvalidBucketState', 409), | 
 | 629 |              ('InvalidDigest', 400), | 
 | 630 |              ('InvalidLocationConstraint', 400), | 
 | 631 |              ('InvalidPart', 400), | 
 | 632 |              ('InvalidPartOrder', 400), | 
 | 633 |              ('InvalidPayer', 403), | 
 | 634 |              ('InvalidPolicyDocument', 400), | 
 | 635 |              ('InvalidRange', 416), | 
 | 636 |              ('InvalidRequest', 400), | 
 | 637 |              ('InvalidSecurity', 403), | 
 | 638 |              ('InvalidSOAPRequest', 400), | 
 | 639 |              ('InvalidStorageClass', 400), | 
 | 640 |              ('InvalidTargetBucketForLogging', 400), | 
 | 641 |              ('InvalidToken', 400), | 
 | 642 |              ('InvalidURI', 400), | 
 | 643 |              ('KeyTooLong', 400), | 
 | 644 |              ('MalformedACLError', 400), | 
 | 645 |              ('MalformedPOSTRequest', 400), | 
 | 646 |              ('MalformedXML', 400), | 
 | 647 |              ('MaxMessageLengthExceeded', 400), | 
 | 648 |              ('MaxPostPreDataLengthExceededError', 400), | 
 | 649 |              ('MetadataTooLarge', 400), | 
 | 650 |              ('MethodNotAllowed', 405), | 
 | 651 |              ('MissingAttachment'), | 
 | 652 |              ('MissingContentLength', 411), | 
 | 653 |              ('MissingRequestBodyError', 400), | 
 | 654 |              ('MissingSecurityElement', 400), | 
 | 655 |              ('MissingSecurityHeader', 400), | 
 | 656 |              ('NoLoggingStatusForKey', 400), | 
 | 657 |              ('NoSuchBucket', 404), | 
 | 658 |              ('NoSuchKey', 404), | 
 | 659 |              ('NoSuchLifecycleConfiguration', 404), | 
 | 660 |              ('NoSuchUpload', 404), | 
 | 661 |              ('NoSuchVersion', 404), | 
 | 662 |              ('NotSignedUp', 403), | 
 | 663 |              ('NotSuchBucketPolicy', 404), | 
 | 664 |              ('OperationAborted', 409), | 
 | 665 |              ('PermanentRedirect', 301), | 
 | 666 |              ('PreconditionFailed', 412), | 
 | 667 |              ('Redirect', 307), | 
 | 668 |              ('RequestIsNotMultiPartContent', 400), | 
 | 669 |              ('RequestTimeout', 400), | 
 | 670 |              ('RequestTimeTooSkewed', 403), | 
 | 671 |              ('RequestTorrentOfBucketError', 400), | 
 | 672 |              ('SignatureDoesNotMatch', 403), | 
 | 673 |              ('TemporaryRedirect', 307), | 
 | 674 |              ('TokenRefreshRequired', 400), | 
 | 675 |              ('TooManyBuckets', 400), | 
 | 676 |              ('UnexpectedContent', 400), | 
 | 677 |              ('UnresolvableGrantByEmailAddress', 400), | 
 | 678 |              ('UserKeyMustBeSpecified', 400)): | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 679 |     _add_matcher_class(BotoTestCase.s3_error_code.client, | 
 | 680 |                        code, base=ClientError) | 
 | 681 |  | 
 | 682 |  | 
 | 683 | for code in (('InternalError', 500), | 
| Matthew Treinish | 1d14c54 | 2014-06-17 20:25:40 -0400 | [diff] [blame] | 684 |              ('NotImplemented', 501), | 
 | 685 |              ('ServiceUnavailable', 503), | 
 | 686 |              ('SlowDown', 503)): | 
| Attila Fazekas | a23f500 | 2012-10-23 19:32:45 +0200 | [diff] [blame] | 687 |     _add_matcher_class(BotoTestCase.s3_error_code.server, | 
 | 688 |                        code, base=ServerError) |