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