1# Copyright (c) 2006-2010 Chris Moyer http://coredumped.org/ 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the 5# "Software"), to deal in the Software without restriction, including 6# without limitation the rights to use, copy, modify, merge, publish, dis- 7# tribute, sublicense, and/or sell copies of the Software, and to permit 8# persons to whom the Software is furnished to do so, subject to the fol- 9# lowing conditions: 10# 11# The above copyright notice and this permission notice shall be included 12# in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21 22import uuid 23 24from boto.compat import urllib 25from boto.resultset import ResultSet 26 27 28class InvalidationBatch(object): 29 """A simple invalidation request. 30 :see: http://docs.amazonwebservices.com/AmazonCloudFront/2010-08-01/APIReference/index.html?InvalidationBatchDatatype.html 31 """ 32 33 def __init__(self, paths=None, connection=None, distribution=None, caller_reference=''): 34 """Create a new invalidation request: 35 :paths: An array of paths to invalidate 36 """ 37 self.paths = paths or [] 38 self.distribution = distribution 39 self.caller_reference = caller_reference 40 if not self.caller_reference: 41 self.caller_reference = str(uuid.uuid4()) 42 43 # If we passed in a distribution, 44 # then we use that as the connection object 45 if distribution: 46 self.connection = distribution 47 else: 48 self.connection = connection 49 50 def __repr__(self): 51 return '<InvalidationBatch: %s>' % self.id 52 53 def add(self, path): 54 """Add another path to this invalidation request""" 55 return self.paths.append(path) 56 57 def remove(self, path): 58 """Remove a path from this invalidation request""" 59 return self.paths.remove(path) 60 61 def __iter__(self): 62 return iter(self.paths) 63 64 def __getitem__(self, i): 65 return self.paths[i] 66 67 def __setitem__(self, k, v): 68 self.paths[k] = v 69 70 def escape(self, p): 71 """Escape a path, make sure it begins with a slash and contains no invalid characters""" 72 if not p[0] == "/": 73 p = "/%s" % p 74 return urllib.parse.quote(p) 75 76 def to_xml(self): 77 """Get this batch as XML""" 78 assert self.connection is not None 79 s = '<?xml version="1.0" encoding="UTF-8"?>\n' 80 s += '<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/%s/">\n' % self.connection.Version 81 for p in self.paths: 82 s += ' <Path>%s</Path>\n' % self.escape(p) 83 s += ' <CallerReference>%s</CallerReference>\n' % self.caller_reference 84 s += '</InvalidationBatch>\n' 85 return s 86 87 def startElement(self, name, attrs, connection): 88 if name == "InvalidationBatch": 89 self.paths = [] 90 return None 91 92 def endElement(self, name, value, connection): 93 if name == 'Path': 94 self.paths.append(value) 95 elif name == "Status": 96 self.status = value 97 elif name == "Id": 98 self.id = value 99 elif name == "CreateTime": 100 self.create_time = value 101 elif name == "CallerReference": 102 self.caller_reference = value 103 return None 104 105 106class InvalidationListResultSet(object): 107 """ 108 A resultset for listing invalidations on a given CloudFront distribution. 109 Implements the iterator interface and transparently handles paging results 110 from CF so even if you have many thousands of invalidations on the 111 distribution you can iterate over all invalidations in a reasonably 112 efficient manner. 113 """ 114 def __init__(self, markers=None, connection=None, distribution_id=None, 115 invalidations=None, marker='', next_marker=None, 116 max_items=None, is_truncated=False): 117 self.markers = markers or [] 118 self.connection = connection 119 self.distribution_id = distribution_id 120 self.marker = marker 121 self.next_marker = next_marker 122 self.max_items = max_items 123 self.auto_paginate = max_items is None 124 self.is_truncated = is_truncated 125 self._inval_cache = invalidations or [] 126 127 def __iter__(self): 128 """ 129 A generator function for listing invalidation requests for a given 130 CloudFront distribution. 131 """ 132 conn = self.connection 133 distribution_id = self.distribution_id 134 result_set = self 135 for inval in result_set._inval_cache: 136 yield inval 137 if not self.auto_paginate: 138 return 139 while result_set.is_truncated: 140 result_set = conn.get_invalidation_requests(distribution_id, 141 marker=result_set.next_marker, 142 max_items=result_set.max_items) 143 for i in result_set._inval_cache: 144 yield i 145 146 def startElement(self, name, attrs, connection): 147 for root_elem, handler in self.markers: 148 if name == root_elem: 149 obj = handler(connection, distribution_id=self.distribution_id) 150 self._inval_cache.append(obj) 151 return obj 152 153 def endElement(self, name, value, connection): 154 if name == 'IsTruncated': 155 self.is_truncated = self.to_boolean(value) 156 elif name == 'Marker': 157 self.marker = value 158 elif name == 'NextMarker': 159 self.next_marker = value 160 elif name == 'MaxItems': 161 self.max_items = int(value) 162 163 def to_boolean(self, value, true_value='true'): 164 if value == true_value: 165 return True 166 else: 167 return False 168 169class InvalidationSummary(object): 170 """ 171 Represents InvalidationSummary complex type in CloudFront API that lists 172 the id and status of a given invalidation request. 173 """ 174 def __init__(self, connection=None, distribution_id=None, id='', 175 status=''): 176 self.connection = connection 177 self.distribution_id = distribution_id 178 self.id = id 179 self.status = status 180 181 def __repr__(self): 182 return '<InvalidationSummary: %s>' % self.id 183 184 def startElement(self, name, attrs, connection): 185 pass 186 187 def endElement(self, name, value, connection): 188 if name == 'Id': 189 self.id = value 190 elif name == 'Status': 191 self.status = value 192 193 def get_distribution(self): 194 """ 195 Returns a Distribution object representing the parent CloudFront 196 distribution of the invalidation request listed in the 197 InvalidationSummary. 198 199 :rtype: :class:`boto.cloudfront.distribution.Distribution` 200 :returns: A Distribution object representing the parent CloudFront 201 distribution of the invalidation request listed in the 202 InvalidationSummary 203 """ 204 return self.connection.get_distribution_info(self.distribution_id) 205 206 def get_invalidation_request(self): 207 """ 208 Returns an InvalidationBatch object representing the invalidation 209 request referred to in the InvalidationSummary. 210 211 :rtype: :class:`boto.cloudfront.invalidation.InvalidationBatch` 212 :returns: An InvalidationBatch object representing the invalidation 213 request referred to by the InvalidationSummary 214 """ 215 return self.connection.invalidation_request_status( 216 self.distribution_id, self.id) 217