1
# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
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-
11
# The above copyright notice and this permission notice shall be included
12
# in all copies or substantial portions of the Software.
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
23
from boto.cloudfront.identity import OriginAccessIdentity
24
from boto.cloudfront.object import Object, StreamingObject
25
from boto.cloudfront.signers import ActiveTrustedSigners, TrustedSigners
26
from boto.cloudfront.logging import LoggingInfo
27
from boto.s3.acl import ACL
29
class DistributionConfig:
31
def __init__(self, connection=None, origin='', enabled=False,
32
caller_reference='', cnames=None, comment='',
33
origin_access_identity=None, trusted_signers=None):
34
self.connection = connection
36
self.enabled = enabled
38
self.caller_reference = caller_reference
40
self.caller_reference = str(uuid.uuid4())
44
self.comment = comment
45
self.origin_access_identity = origin_access_identity
46
self.trusted_signers = trusted_signers
49
def get_oai_value(self):
50
if isinstance(self.origin_access_identity, OriginAccessIdentity):
51
return self.origin_access_identity.uri()
53
return self.origin_access_identity
56
s = '<?xml version="1.0" encoding="UTF-8"?>\n'
57
s += '<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2008-12-01/">\n'
58
s += ' <Origin>%s</Origin>\n' % self.origin
59
s += ' <CallerReference>%s</CallerReference>\n' % self.caller_reference
60
for cname in self.cnames:
61
s += ' <CNAME>%s</CNAME>\n' % cname
63
s += ' <Comment>%s</Comment>\n' % self.comment
70
if self.origin_access_identity:
71
val = self.get_oai_value()
72
s += '<OriginAccessIdentity>%s</OriginAccessIdentity>\n' % val
73
if self.trusted_signers:
74
s += '<TrustedSigners>\n'
75
for signer in self.trusted_signers:
77
s += ' <Self></Self>\n'
79
s += ' <AwsAccountNumber>%s</AwsAccountNumber>\n' % signer
80
s += '</TrustedSigners>\n'
83
s += ' <Bucket>%s</Bucket>\n' % self.logging.bucket
84
s += ' <Prefix>%s</Prefix>\n' % self.logging.prefix
86
s += '</DistributionConfig>\n'
89
def startElement(self, name, attrs, connection):
90
if name == 'TrustedSigners':
91
self.trusted_signers = TrustedSigners()
92
return self.trusted_signers
93
elif name == 'Logging':
94
self.logging = LoggingInfo()
99
def endElement(self, name, value, connection):
101
self.cnames.append(value)
102
elif name == 'Origin':
104
elif name == 'Comment':
106
elif name == 'Enabled':
107
if value.lower() == 'true':
111
elif name == 'CallerReference':
112
self.caller_reference = value
113
elif name == 'OriginAccessIdentity':
114
self.origin_access_identity = value
116
setattr(self, name, value)
118
class StreamingDistributionConfig(DistributionConfig):
120
def __init__(self, connection=None, origin='', enabled=False,
121
caller_reference='', cnames=None, comment=''):
122
DistributionConfig.__init__(self, connection, origin,
123
enabled, caller_reference,
127
s = '<?xml version="1.0" encoding="UTF-8"?>\n'
128
s += '<StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2009-12-01/">\n'
129
s += ' <Origin>%s</Origin>\n' % self.origin
130
s += ' <CallerReference>%s</CallerReference>\n' % self.caller_reference
131
for cname in self.cnames:
132
s += ' <CNAME>%s</CNAME>\n' % cname
134
s += ' <Comment>%s</Comment>\n' % self.comment
141
s += '</StreamingDistributionConfig>\n'
144
def startElement(self, name, attrs, connection):
147
class DistributionSummary:
149
def __init__(self, connection=None, domain_name='', id='',
150
last_modified_time=None, status='', origin='',
151
cname='', comment='', enabled=False):
152
self.connection = connection
153
self.domain_name = domain_name
155
self.last_modified_time = last_modified_time
158
self.enabled = enabled
161
self.cnames.append(cname)
162
self.comment = comment
163
self.trusted_signers = None
165
self.streaming = False
167
def startElement(self, name, attrs, connection):
168
if name == 'TrustedSigners':
169
self.trusted_signers = TrustedSigners()
170
return self.trusted_signers
173
def endElement(self, name, value, connection):
176
elif name == 'Status':
178
elif name == 'LastModifiedTime':
179
self.last_modified_time = value
180
elif name == 'DomainName':
181
self.domain_name = value
182
elif name == 'Origin':
184
elif name == 'CNAME':
185
self.cnames.append(value)
186
elif name == 'Comment':
188
elif name == 'Enabled':
189
if value.lower() == 'true':
193
elif name == 'StreamingDistributionSummary':
194
self.streaming = True
196
setattr(self, name, value)
198
def get_distribution(self):
199
return self.connection.get_distribution_info(self.id)
201
class StreamingDistributionSummary(DistributionSummary):
203
def get_distribution(self):
204
return self.connection.get_streaming_distribution_info(self.id)
208
def __init__(self, connection=None, config=None, domain_name='',
209
id='', last_modified_time=None, status=''):
210
self.connection = connection
212
self.domain_name = domain_name
214
self.last_modified_time = last_modified_time
216
self.active_signers = None
220
def startElement(self, name, attrs, connection):
221
if name == 'DistributionConfig':
222
self.config = DistributionConfig()
224
elif name == 'ActiveTrustedSigners':
225
self.active_signers = ActiveTrustedSigners()
226
return self.active_signers
230
def endElement(self, name, value, connection):
233
elif name == 'LastModifiedTime':
234
self.last_modified_time = value
235
elif name == 'Status':
237
elif name == 'DomainName':
238
self.domain_name = value
240
setattr(self, name, value)
242
def update(self, enabled=None, cnames=None, comment=None,
243
origin_access_identity=None,
244
trusted_signers=None):
246
Update the configuration of the Distribution.
249
:param enabled: Whether the Distribution is active or not.
251
:type cnames: list of str
252
:param cnames: The DNS CNAME's associated with this
253
Distribution. Maximum of 10 values.
255
:type comment: str or unicode
256
:param comment: The comment associated with the Distribution.
258
:type origin_access_identity: :class:`boto.cloudfront.identity.OriginAccessIdentity`
259
:param origin_access_identity: The CloudFront origin access identity
260
associated with the distribution. This
261
must be provided if you want the
262
distribution to serve private content.
264
:type trusted_signers: :class:`boto.cloudfront.signers.TrustedSigner`
265
:param trusted_signers: The AWS users who are authorized to sign
266
URL's for private content in this Distribution.
269
new_config = DistributionConfig(self.connection, self.config.origin,
270
self.config.enabled, self.config.caller_reference,
271
self.config.cnames, self.config.comment,
272
self.config.origin_access_identity,
273
self.config.trusted_signers)
275
new_config.enabled = enabled
277
new_config.cnames = cnames
279
new_config.comment = comment
280
if origin_access_identity != None:
281
new_config.origin_access_identity = origin_access_identity
283
new_config.trusted_signers = trusted_signers
284
self.etag = self.connection.set_distribution_config(self.id, self.etag, new_config)
285
self.config = new_config
286
self._object_class = Object
290
Deactivate the Distribution. A convenience wrapper around
293
self.update(enabled=True)
297
Activate the Distribution. A convenience wrapper around
300
self.update(enabled=False)
304
Delete this CloudFront Distribution. The content
305
associated with the Distribution is not deleted from
306
the underlying Origin bucket in S3.
308
self.connection.delete_distribution(self.id, self.etag)
310
def _get_bucket(self):
312
bucket_name = self.config.origin.split('.')[0]
313
from boto.s3.connection import S3Connection
314
s3 = S3Connection(self.connection.aws_access_key_id,
315
self.connection.aws_secret_access_key,
316
proxy=self.connection.proxy,
317
proxy_port=self.connection.proxy_port,
318
proxy_user=self.connection.proxy_user,
319
proxy_pass=self.connection.proxy_pass)
320
self._bucket = s3.get_bucket(bucket_name)
321
self._bucket.distribution = self
322
self._bucket.set_key_class(self._object_class)
325
def get_objects(self):
327
Return a list of all content objects in this distribution.
329
:rtype: list of :class:`boto.cloudfront.object.Object`
330
:return: The content objects
332
bucket = self._get_bucket()
338
def set_permissions(self, object, replace=False):
340
Sets the S3 ACL grants for the given object to the appropriate
341
value based on the type of Distribution. If the Distribution
342
is serving private content the ACL will be set to include the
343
Origin Access Identity associated with the Distribution. If
344
the Distribution is serving public content the content will
345
be set up with "public-read".
347
:type object: :class:`boto.cloudfront.object.Object`
348
:param enabled: The Object whose ACL is being set
351
:param replace: If False, the Origin Access Identity will be
352
appended to the existing ACL for the object.
353
If True, the ACL for the object will be
354
completely replaced with one that grants
355
READ permission to the Origin Access Identity.
358
if self.config.origin_access_identity:
359
id = self.config.origin_access_identity.split('/')[-1]
360
oai = self.connection.get_origin_access_identity_info(id)
361
policy = object.get_acl()
364
policy.acl.add_user_grant('READ', oai.s3_user_id)
365
object.set_acl(policy)
367
object.set_canned_acl('public-read')
369
def set_permissions_all(self, replace=False):
371
Sets the S3 ACL grants for all objects in the Distribution
372
to the appropriate value based on the type of Distribution.
375
:param replace: If False, the Origin Access Identity will be
376
appended to the existing ACL for the object.
377
If True, the ACL for the object will be
378
completely replaced with one that grants
379
READ permission to the Origin Access Identity.
382
bucket = self._get_bucket()
384
self.set_permissions(key)
386
def add_object(self, name, content, headers=None, replace=True):
388
Adds a new content object to the Distribution. The content
389
for the object will be copied to a new Key in the S3 Bucket
390
and the permissions will be set appropriately for the type
393
:type name: str or unicode
394
:param name: The name or key of the new object.
396
:type content: file-like object
397
:param content: A file-like object that contains the content
401
:param headers: A dictionary containing additional headers
402
you would like associated with the new
405
:rtype: :class:`boto.cloudfront.object.Object`
406
:return: The newly created object.
408
if self.config.origin_access_identity:
411
policy = 'public-read'
412
bucket = self._get_bucket()
413
object = bucket.new_key(name)
414
object.set_contents_from_file(content, headers=headers, policy=policy)
415
if self.config.origin_access_identity:
416
self.set_permissions(object, replace)
419
class StreamingDistribution(Distribution):
421
def __init__(self, connection=None, config=None, domain_name='',
422
id='', last_modified_time=None, status=''):
423
Distribution.__init__(self, connection, config, domain_name,
424
id, last_modified_time, status)
425
self._object_class = StreamingObject
427
def startElement(self, name, attrs, connection):
428
if name == 'StreamingDistributionConfig':
429
self.config = StreamingDistributionConfig()
434
def update(self, enabled=None, cnames=None, comment=None):
436
Update the configuration of the Distribution.
439
:param enabled: Whether the Distribution is active or not.
441
:type cnames: list of str
442
:param cnames: The DNS CNAME's associated with this
443
Distribution. Maximum of 10 values.
445
:type comment: str or unicode
446
:param comment: The comment associated with the Distribution.
449
new_config = StreamingDistributionConfig(self.connection,
452
self.config.caller_reference,
456
new_config.enabled = enabled
458
new_config.cnames = cnames
460
new_config.comment = comment
462
self.etag = self.connection.set_streaming_distribution_config(self.id,
465
self.config = new_config
468
self.connection.delete_streaming_distribution(self.id, self.etag)