Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

ATTENTION -- Mega WhiteSpace conversion !!!

Historically s3cmd and modules used to use <tab> for indentation.
This is not a recommended Python coding standard and many tools
treat it as an error.

This mega patch converts all <tab>s to <4-space>s and also removes
trailing white whitespace along the way.

To get meaningful diffs across this commit use: git diff -w
  • Loading branch information...
commit d439efb41c4372a786204195d8253c7a1b683c01 1 parent 46d61bb
@mludvig mludvig authored
View
396 S3/ACL.py
@@ -6,217 +6,219 @@
from Utils import getTreeFromXml
try:
- import xml.etree.ElementTree as ET
+ import xml.etree.ElementTree as ET
except ImportError:
- import elementtree.ElementTree as ET
+ import elementtree.ElementTree as ET
class Grantee(object):
- ALL_USERS_URI = "http://acs.amazonaws.com/groups/global/AllUsers"
- LOG_DELIVERY_URI = "http://acs.amazonaws.com/groups/s3/LogDelivery"
-
- def __init__(self):
- self.xsi_type = None
- self.tag = None
- self.name = None
- self.display_name = None
- self.permission = None
-
- def __repr__(self):
- return 'Grantee("%(tag)s", "%(name)s", "%(permission)s")' % {
- "tag" : self.tag,
- "name" : self.name,
- "permission" : self.permission
- }
-
- def isAllUsers(self):
- return self.tag == "URI" and self.name == Grantee.ALL_USERS_URI
-
- def isAnonRead(self):
- return self.isAllUsers() and (self.permission == "READ" or self.permission == "FULL_CONTROL")
-
- def getElement(self):
- el = ET.Element("Grant")
- grantee = ET.SubElement(el, "Grantee", {
- 'xmlns:xsi' : 'http://www.w3.org/2001/XMLSchema-instance',
- 'xsi:type' : self.xsi_type
- })
- name = ET.SubElement(grantee, self.tag)
- name.text = self.name
- permission = ET.SubElement(el, "Permission")
- permission.text = self.permission
- return el
+ ALL_USERS_URI = "http://acs.amazonaws.com/groups/global/AllUsers"
+ LOG_DELIVERY_URI = "http://acs.amazonaws.com/groups/s3/LogDelivery"
+
+ def __init__(self):
+ self.xsi_type = None
+ self.tag = None
+ self.name = None
+ self.display_name = None
+ self.permission = None
+
+ def __repr__(self):
+ return 'Grantee("%(tag)s", "%(name)s", "%(permission)s")' % {
+ "tag" : self.tag,
+ "name" : self.name,
+ "permission" : self.permission
+ }
+
+ def isAllUsers(self):
+ return self.tag == "URI" and self.name == Grantee.ALL_USERS_URI
+
+ def isAnonRead(self):
+ return self.isAllUsers() and (self.permission == "READ" or self.permission == "FULL_CONTROL")
+
+ def getElement(self):
+ el = ET.Element("Grant")
+ grantee = ET.SubElement(el, "Grantee", {
+ 'xmlns:xsi' : 'http://www.w3.org/2001/XMLSchema-instance',
+ 'xsi:type' : self.xsi_type
+ })
+ name = ET.SubElement(grantee, self.tag)
+ name.text = self.name
+ permission = ET.SubElement(el, "Permission")
+ permission.text = self.permission
+ return el
class GranteeAnonRead(Grantee):
- def __init__(self):
- Grantee.__init__(self)
- self.xsi_type = "Group"
- self.tag = "URI"
- self.name = Grantee.ALL_USERS_URI
- self.permission = "READ"
+ def __init__(self):
+ Grantee.__init__(self)
+ self.xsi_type = "Group"
+ self.tag = "URI"
+ self.name = Grantee.ALL_USERS_URI
+ self.permission = "READ"
class GranteeLogDelivery(Grantee):
- def __init__(self, permission):
- """
- permission must be either READ_ACP or WRITE
- """
- Grantee.__init__(self)
- self.xsi_type = "Group"
- self.tag = "URI"
- self.name = Grantee.LOG_DELIVERY_URI
- self.permission = permission
+ def __init__(self, permission):
+ """
+ permission must be either READ_ACP or WRITE
+ """
+ Grantee.__init__(self)
+ self.xsi_type = "Group"
+ self.tag = "URI"
+ self.name = Grantee.LOG_DELIVERY_URI
+ self.permission = permission
class ACL(object):
- EMPTY_ACL = "<AccessControlPolicy><Owner><ID></ID></Owner><AccessControlList></AccessControlList></AccessControlPolicy>"
-
- def __init__(self, xml = None):
- if not xml:
- xml = ACL.EMPTY_ACL
-
- self.grantees = []
- self.owner_id = ""
- self.owner_nick = ""
-
- tree = getTreeFromXml(xml)
- self.parseOwner(tree)
- self.parseGrants(tree)
-
- def parseOwner(self, tree):
- self.owner_id = tree.findtext(".//Owner//ID")
- self.owner_nick = tree.findtext(".//Owner//DisplayName")
-
- def parseGrants(self, tree):
- for grant in tree.findall(".//Grant"):
- grantee = Grantee()
- g = grant.find(".//Grantee")
- grantee.xsi_type = g.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']
- grantee.permission = grant.find('Permission').text
- for el in g:
- if el.tag == "DisplayName":
- grantee.display_name = el.text
- else:
- grantee.tag = el.tag
- grantee.name = el.text
- self.grantees.append(grantee)
-
- def getGrantList(self):
- acl = []
- for grantee in self.grantees:
- if grantee.display_name:
- user = grantee.display_name
- elif grantee.isAllUsers():
- user = "*anon*"
- else:
- user = grantee.name
- acl.append({'grantee': user, 'permission': grantee.permission})
- return acl
-
- def getOwner(self):
- return { 'id' : self.owner_id, 'nick' : self.owner_nick }
-
- def isAnonRead(self):
- for grantee in self.grantees:
- if grantee.isAnonRead():
- return True
- return False
-
- def grantAnonRead(self):
- if not self.isAnonRead():
- self.appendGrantee(GranteeAnonRead())
-
- def revokeAnonRead(self):
- self.grantees = [g for g in self.grantees if not g.isAnonRead()]
-
- def appendGrantee(self, grantee):
- self.grantees.append(grantee)
-
- def hasGrant(self, name, permission):
- name = name.lower()
- permission = permission.upper()
-
- for grantee in self.grantees:
- if grantee.name.lower() == name:
- if grantee.permission == "FULL_CONTROL":
- return True
- elif grantee.permission.upper() == permission:
- return True
-
- return False;
-
- def grant(self, name, permission):
- if self.hasGrant(name, permission):
- return
-
- name = name.lower()
- permission = permission.upper()
-
- if "ALL" == permission:
- permission = "FULL_CONTROL"
-
- if "FULL_CONTROL" == permission:
- self.revoke(name, "ALL")
-
- grantee = Grantee()
- grantee.name = name
- grantee.permission = permission
-
- if name.find('@') <= -1: # ultra lame attempt to differenciate emails id from canonical ids
- grantee.xsi_type = "CanonicalUser"
- grantee.tag = "ID"
- else:
- grantee.xsi_type = "AmazonCustomerByEmail"
- grantee.tag = "EmailAddress"
-
- self.appendGrantee(grantee)
-
-
- def revoke(self, name, permission):
- name = name.lower()
- permission = permission.upper()
-
- if "ALL" == permission:
- self.grantees = [g for g in self.grantees if not g.name.lower() == name]
- else:
- self.grantees = [g for g in self.grantees if not (g.name.lower() == name and g.permission.upper() == permission)]
-
-
- def __str__(self):
- tree = getTreeFromXml(ACL.EMPTY_ACL)
- tree.attrib['xmlns'] = "http://s3.amazonaws.com/doc/2006-03-01/"
- owner = tree.find(".//Owner//ID")
- owner.text = self.owner_id
- acl = tree.find(".//AccessControlList")
- for grantee in self.grantees:
- acl.append(grantee.getElement())
- return ET.tostring(tree)
+ EMPTY_ACL = "<AccessControlPolicy><Owner><ID></ID></Owner><AccessControlList></AccessControlList></AccessControlPolicy>"
+
+ def __init__(self, xml = None):
+ if not xml:
+ xml = ACL.EMPTY_ACL
+
+ self.grantees = []
+ self.owner_id = ""
+ self.owner_nick = ""
+
+ tree = getTreeFromXml(xml)
+ self.parseOwner(tree)
+ self.parseGrants(tree)
+
+ def parseOwner(self, tree):
+ self.owner_id = tree.findtext(".//Owner//ID")
+ self.owner_nick = tree.findtext(".//Owner//DisplayName")
+
+ def parseGrants(self, tree):
+ for grant in tree.findall(".//Grant"):
+ grantee = Grantee()
+ g = grant.find(".//Grantee")
+ grantee.xsi_type = g.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']
+ grantee.permission = grant.find('Permission').text
+ for el in g:
+ if el.tag == "DisplayName":
+ grantee.display_name = el.text
+ else:
+ grantee.tag = el.tag
+ grantee.name = el.text
+ self.grantees.append(grantee)
+
+ def getGrantList(self):
+ acl = []
+ for grantee in self.grantees:
+ if grantee.display_name:
+ user = grantee.display_name
+ elif grantee.isAllUsers():
+ user = "*anon*"
+ else:
+ user = grantee.name
+ acl.append({'grantee': user, 'permission': grantee.permission})
+ return acl
+
+ def getOwner(self):
+ return { 'id' : self.owner_id, 'nick' : self.owner_nick }
+
+ def isAnonRead(self):
+ for grantee in self.grantees:
+ if grantee.isAnonRead():
+ return True
+ return False
+
+ def grantAnonRead(self):
+ if not self.isAnonRead():
+ self.appendGrantee(GranteeAnonRead())
+
+ def revokeAnonRead(self):
+ self.grantees = [g for g in self.grantees if not g.isAnonRead()]
+
+ def appendGrantee(self, grantee):
+ self.grantees.append(grantee)
+
+ def hasGrant(self, name, permission):
+ name = name.lower()
+ permission = permission.upper()
+
+ for grantee in self.grantees:
+ if grantee.name.lower() == name:
+ if grantee.permission == "FULL_CONTROL":
+ return True
+ elif grantee.permission.upper() == permission:
+ return True
+
+ return False;
+
+ def grant(self, name, permission):
+ if self.hasGrant(name, permission):
+ return
+
+ name = name.lower()
+ permission = permission.upper()
+
+ if "ALL" == permission:
+ permission = "FULL_CONTROL"
+
+ if "FULL_CONTROL" == permission:
+ self.revoke(name, "ALL")
+
+ grantee = Grantee()
+ grantee.name = name
+ grantee.permission = permission
+
+ if name.find('@') <= -1: # ultra lame attempt to differenciate emails id from canonical ids
+ grantee.xsi_type = "CanonicalUser"
+ grantee.tag = "ID"
+ else:
+ grantee.xsi_type = "AmazonCustomerByEmail"
+ grantee.tag = "EmailAddress"
+
+ self.appendGrantee(grantee)
+
+
+ def revoke(self, name, permission):
+ name = name.lower()
+ permission = permission.upper()
+
+ if "ALL" == permission:
+ self.grantees = [g for g in self.grantees if not g.name.lower() == name]
+ else:
+ self.grantees = [g for g in self.grantees if not (g.name.lower() == name and g.permission.upper() == permission)]
+
+
+ def __str__(self):
+ tree = getTreeFromXml(ACL.EMPTY_ACL)
+ tree.attrib['xmlns'] = "http://s3.amazonaws.com/doc/2006-03-01/"
+ owner = tree.find(".//Owner//ID")
+ owner.text = self.owner_id
+ acl = tree.find(".//AccessControlList")
+ for grantee in self.grantees:
+ acl.append(grantee.getElement())
+ return ET.tostring(tree)
if __name__ == "__main__":
- xml = """<?xml version="1.0" encoding="UTF-8"?>
+ xml = """<?xml version="1.0" encoding="UTF-8"?>
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Owner>
- <ID>12345678901234567890</ID>
- <DisplayName>owner-nickname</DisplayName>
+ <ID>12345678901234567890</ID>
+ <DisplayName>owner-nickname</DisplayName>
</Owner>
<AccessControlList>
- <Grant>
- <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
- <ID>12345678901234567890</ID>
- <DisplayName>owner-nickname</DisplayName>
- </Grantee>
- <Permission>FULL_CONTROL</Permission>
- </Grant>
- <Grant>
- <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
- <URI>http://acs.amazonaws.com/groups/global/AllUsers</URI>
- </Grantee>
- <Permission>READ</Permission>
- </Grant>
+ <Grant>
+ <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
+ <ID>12345678901234567890</ID>
+ <DisplayName>owner-nickname</DisplayName>
+ </Grantee>
+ <Permission>FULL_CONTROL</Permission>
+ </Grant>
+ <Grant>
+ <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
+ <URI>http://acs.amazonaws.com/groups/global/AllUsers</URI>
+ </Grantee>
+ <Permission>READ</Permission>
+ </Grant>
</AccessControlList>
</AccessControlPolicy>
- """
- acl = ACL(xml)
- print "Grants:", acl.getGrantList()
- acl.revokeAnonRead()
- print "Grants:", acl.getGrantList()
- acl.grantAnonRead()
- print "Grants:", acl.getGrantList()
- print acl
+ """
+ acl = ACL(xml)
+ print "Grants:", acl.getGrantList()
+ acl.revokeAnonRead()
+ print "Grants:", acl.getGrantList()
+ acl.grantAnonRead()
+ print "Grants:", acl.getGrantList()
+ print acl
+
+# vim:et:ts=4:sts=4:ai
View
134 S3/AccessLog.py
@@ -9,82 +9,84 @@
from ACL import GranteeAnonRead
try:
- import xml.etree.ElementTree as ET
+ import xml.etree.ElementTree as ET
except ImportError:
- import elementtree.ElementTree as ET
+ import elementtree.ElementTree as ET
__all__ = []
class AccessLog(object):
- LOG_DISABLED = "<BucketLoggingStatus></BucketLoggingStatus>"
- LOG_TEMPLATE = "<LoggingEnabled><TargetBucket></TargetBucket><TargetPrefix></TargetPrefix></LoggingEnabled>"
+ LOG_DISABLED = "<BucketLoggingStatus></BucketLoggingStatus>"
+ LOG_TEMPLATE = "<LoggingEnabled><TargetBucket></TargetBucket><TargetPrefix></TargetPrefix></LoggingEnabled>"
- def __init__(self, xml = None):
- if not xml:
- xml = self.LOG_DISABLED
- self.tree = getTreeFromXml(xml)
- self.tree.attrib['xmlns'] = "http://doc.s3.amazonaws.com/2006-03-01"
-
- def isLoggingEnabled(self):
- return bool(self.tree.find(".//LoggingEnabled"))
+ def __init__(self, xml = None):
+ if not xml:
+ xml = self.LOG_DISABLED
+ self.tree = getTreeFromXml(xml)
+ self.tree.attrib['xmlns'] = "http://doc.s3.amazonaws.com/2006-03-01"
- def disableLogging(self):
- el = self.tree.find(".//LoggingEnabled")
- if el:
- self.tree.remove(el)
-
- def enableLogging(self, target_prefix_uri):
- el = self.tree.find(".//LoggingEnabled")
- if not el:
- el = getTreeFromXml(self.LOG_TEMPLATE)
- self.tree.append(el)
- el.find(".//TargetBucket").text = target_prefix_uri.bucket()
- el.find(".//TargetPrefix").text = target_prefix_uri.object()
+ def isLoggingEnabled(self):
+ return bool(self.tree.find(".//LoggingEnabled"))
- def targetPrefix(self):
- if self.isLoggingEnabled():
- el = self.tree.find(".//LoggingEnabled")
- target_prefix = "s3://%s/%s" % (
- self.tree.find(".//LoggingEnabled//TargetBucket").text,
- self.tree.find(".//LoggingEnabled//TargetPrefix").text)
- return S3Uri.S3Uri(target_prefix)
- else:
- return ""
+ def disableLogging(self):
+ el = self.tree.find(".//LoggingEnabled")
+ if el:
+ self.tree.remove(el)
- def setAclPublic(self, acl_public):
- le = self.tree.find(".//LoggingEnabled")
- if not le:
- raise ParameterError("Logging not enabled, can't set default ACL for logs")
- tg = le.find(".//TargetGrants")
- if not acl_public:
- if not tg:
- ## All good, it's not been there
- return
- else:
- le.remove(tg)
- else: # acl_public == True
- anon_read = GranteeAnonRead().getElement()
- if not tg:
- tg = ET.SubElement(le, "TargetGrants")
- ## What if TargetGrants already exists? We should check if
- ## AnonRead is there before appending a new one. Later...
- tg.append(anon_read)
+ def enableLogging(self, target_prefix_uri):
+ el = self.tree.find(".//LoggingEnabled")
+ if not el:
+ el = getTreeFromXml(self.LOG_TEMPLATE)
+ self.tree.append(el)
+ el.find(".//TargetBucket").text = target_prefix_uri.bucket()
+ el.find(".//TargetPrefix").text = target_prefix_uri.object()
- def isAclPublic(self):
- raise NotImplementedError()
+ def targetPrefix(self):
+ if self.isLoggingEnabled():
+ el = self.tree.find(".//LoggingEnabled")
+ target_prefix = "s3://%s/%s" % (
+ self.tree.find(".//LoggingEnabled//TargetBucket").text,
+ self.tree.find(".//LoggingEnabled//TargetPrefix").text)
+ return S3Uri.S3Uri(target_prefix)
+ else:
+ return ""
- def __str__(self):
- return ET.tostring(self.tree)
+ def setAclPublic(self, acl_public):
+ le = self.tree.find(".//LoggingEnabled")
+ if not le:
+ raise ParameterError("Logging not enabled, can't set default ACL for logs")
+ tg = le.find(".//TargetGrants")
+ if not acl_public:
+ if not tg:
+ ## All good, it's not been there
+ return
+ else:
+ le.remove(tg)
+ else: # acl_public == True
+ anon_read = GranteeAnonRead().getElement()
+ if not tg:
+ tg = ET.SubElement(le, "TargetGrants")
+ ## What if TargetGrants already exists? We should check if
+ ## AnonRead is there before appending a new one. Later...
+ tg.append(anon_read)
+
+ def isAclPublic(self):
+ raise NotImplementedError()
+
+ def __str__(self):
+ return ET.tostring(self.tree)
__all__.append("AccessLog")
if __name__ == "__main__":
- from S3Uri import S3Uri
- log = AccessLog()
- print log
- log.enableLogging(S3Uri("s3://targetbucket/prefix/log-"))
- print log
- log.setAclPublic(True)
- print log
- log.setAclPublic(False)
- print log
- log.disableLogging()
- print log
+ from S3Uri import S3Uri
+ log = AccessLog()
+ print log
+ log.enableLogging(S3Uri("s3://targetbucket/prefix/log-"))
+ print log
+ log.setAclPublic(True)
+ print log
+ log.setAclPublic(False)
+ print log
+ log.disableLogging()
+ print log
+
+# vim:et:ts=4:sts=4:ai
View
70 S3/BidirMap.py
@@ -4,37 +4,39 @@
## License: GPL Version 2
class BidirMap(object):
- def __init__(self, **map):
- self.k2v = {}
- self.v2k = {}
- for key in map:
- self.__setitem__(key, map[key])
-
- def __setitem__(self, key, value):
- if self.v2k.has_key(value):
- if self.v2k[value] != key:
- raise KeyError("Value '"+str(value)+"' already in use with key '"+str(self.v2k[value])+"'")
- try:
- del(self.v2k[self.k2v[key]])
- except KeyError:
- pass
- self.k2v[key] = value
- self.v2k[value] = key
-
- def __getitem__(self, key):
- return self.k2v[key]
-
- def __str__(self):
- return self.v2k.__str__()
-
- def getkey(self, value):
- return self.v2k[value]
-
- def getvalue(self, key):
- return self.k2v[key]
-
- def keys(self):
- return [key for key in self.k2v]
-
- def values(self):
- return [value for value in self.v2k]
+ def __init__(self, **map):
+ self.k2v = {}
+ self.v2k = {}
+ for key in map:
+ self.__setitem__(key, map[key])
+
+ def __setitem__(self, key, value):
+ if self.v2k.has_key(value):
+ if self.v2k[value] != key:
+ raise KeyError("Value '"+str(value)+"' already in use with key '"+str(self.v2k[value])+"'")
+ try:
+ del(self.v2k[self.k2v[key]])
+ except KeyError:
+ pass
+ self.k2v[key] = value
+ self.v2k[value] = key
+
+ def __getitem__(self, key):
+ return self.k2v[key]
+
+ def __str__(self):
+ return self.v2k.__str__()
+
+ def getkey(self, value):
+ return self.v2k[value]
+
+ def getvalue(self, key):
+ return self.k2v[key]
+
+ def keys(self):
+ return [key for key in self.k2v]
+
+ def values(self):
+ return [value for value in self.v2k]
+
+# vim:et:ts=4:sts=4:ai
View
1,382 S3/CloudFront.py
@@ -11,9 +11,9 @@
from logging import debug, info, warning, error
try:
- import xml.etree.ElementTree as ET
+ import xml.etree.ElementTree as ET
except ImportError:
- import elementtree.ElementTree as ET
+ import elementtree.ElementTree as ET
from Config import Config
from Exceptions import *
@@ -25,714 +25,716 @@
cloudfront_resource = "/%(api_ver)s/distribution" % { 'api_ver' : cloudfront_api_version }
def output(message):
- sys.stdout.write(message + "\n")
+ sys.stdout.write(message + "\n")
def pretty_output(label, message):
- #label = ("%s " % label).ljust(20, ".")
- label = ("%s:" % label).ljust(15)
- output("%s %s" % (label, message))
+ #label = ("%s " % label).ljust(20, ".")
+ label = ("%s:" % label).ljust(15)
+ output("%s %s" % (label, message))
class DistributionSummary(object):
- ## Example:
- ##
- ## <DistributionSummary>
- ## <Id>1234567890ABC</Id>
- ## <Status>Deployed</Status>
- ## <LastModifiedTime>2009-01-16T11:49:02.189Z</LastModifiedTime>
- ## <DomainName>blahblahblah.cloudfront.net</DomainName>
- ## <S3Origin>
- ## <DNSName>example.bucket.s3.amazonaws.com</DNSName>
- ## </S3Origin>
- ## <CNAME>cdn.example.com</CNAME>
- ## <CNAME>img.example.com</CNAME>
- ## <Comment>What Ever</Comment>
- ## <Enabled>true</Enabled>
- ## </DistributionSummary>
-
- def __init__(self, tree):
- if tree.tag != "DistributionSummary":
- raise ValueError("Expected <DistributionSummary /> xml, got: <%s />" % tree.tag)
- self.parse(tree)
-
- def parse(self, tree):
- self.info = getDictFromTree(tree)
- self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
- if self.info.has_key("CNAME") and type(self.info['CNAME']) != list:
- self.info['CNAME'] = [self.info['CNAME']]
-
- def uri(self):
- return S3Uri("cf://%s" % self.info['Id'])
+ ## Example:
+ ##
+ ## <DistributionSummary>
+ ## <Id>1234567890ABC</Id>
+ ## <Status>Deployed</Status>
+ ## <LastModifiedTime>2009-01-16T11:49:02.189Z</LastModifiedTime>
+ ## <DomainName>blahblahblah.cloudfront.net</DomainName>
+ ## <S3Origin>
+ ## <DNSName>example.bucket.s3.amazonaws.com</DNSName>
+ ## </S3Origin>
+ ## <CNAME>cdn.example.com</CNAME>
+ ## <CNAME>img.example.com</CNAME>
+ ## <Comment>What Ever</Comment>
+ ## <Enabled>true</Enabled>
+ ## </DistributionSummary>
+
+ def __init__(self, tree):
+ if tree.tag != "DistributionSummary":
+ raise ValueError("Expected <DistributionSummary /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+ self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
+ if self.info.has_key("CNAME") and type(self.info['CNAME']) != list:
+ self.info['CNAME'] = [self.info['CNAME']]
+
+ def uri(self):
+ return S3Uri("cf://%s" % self.info['Id'])
class DistributionList(object):
- ## Example:
- ##
- ## <DistributionList xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
- ## <Marker />
- ## <MaxItems>100</MaxItems>
- ## <IsTruncated>false</IsTruncated>
- ## <DistributionSummary>
- ## ... handled by DistributionSummary() class ...
- ## </DistributionSummary>
- ## </DistributionList>
-
- def __init__(self, xml):
- tree = getTreeFromXml(xml)
- if tree.tag != "DistributionList":
- raise ValueError("Expected <DistributionList /> xml, got: <%s />" % tree.tag)
- self.parse(tree)
-
- def parse(self, tree):
- self.info = getDictFromTree(tree)
- ## Normalise some items
- self.info['IsTruncated'] = (self.info['IsTruncated'].lower() == "true")
-
- self.dist_summs = []
- for dist_summ in tree.findall(".//DistributionSummary"):
- self.dist_summs.append(DistributionSummary(dist_summ))
+ ## Example:
+ ##
+ ## <DistributionList xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
+ ## <Marker />
+ ## <MaxItems>100</MaxItems>
+ ## <IsTruncated>false</IsTruncated>
+ ## <DistributionSummary>
+ ## ... handled by DistributionSummary() class ...
+ ## </DistributionSummary>
+ ## </DistributionList>
+
+ def __init__(self, xml):
+ tree = getTreeFromXml(xml)
+ if tree.tag != "DistributionList":
+ raise ValueError("Expected <DistributionList /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+ ## Normalise some items
+ self.info['IsTruncated'] = (self.info['IsTruncated'].lower() == "true")
+
+ self.dist_summs = []
+ for dist_summ in tree.findall(".//DistributionSummary"):
+ self.dist_summs.append(DistributionSummary(dist_summ))
class Distribution(object):
- ## Example:
- ##
- ## <Distribution xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
- ## <Id>1234567890ABC</Id>
- ## <Status>InProgress</Status>
- ## <LastModifiedTime>2009-01-16T13:07:11.319Z</LastModifiedTime>
- ## <DomainName>blahblahblah.cloudfront.net</DomainName>
- ## <DistributionConfig>
- ## ... handled by DistributionConfig() class ...
- ## </DistributionConfig>
- ## </Distribution>
-
- def __init__(self, xml):
- tree = getTreeFromXml(xml)
- if tree.tag != "Distribution":
- raise ValueError("Expected <Distribution /> xml, got: <%s />" % tree.tag)
- self.parse(tree)
-
- def parse(self, tree):
- self.info = getDictFromTree(tree)
- ## Normalise some items
- self.info['LastModifiedTime'] = dateS3toPython(self.info['LastModifiedTime'])
-
- self.info['DistributionConfig'] = DistributionConfig(tree = tree.find(".//DistributionConfig"))
-
- def uri(self):
- return S3Uri("cf://%s" % self.info['Id'])
+ ## Example:
+ ##
+ ## <Distribution xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
+ ## <Id>1234567890ABC</Id>
+ ## <Status>InProgress</Status>
+ ## <LastModifiedTime>2009-01-16T13:07:11.319Z</LastModifiedTime>
+ ## <DomainName>blahblahblah.cloudfront.net</DomainName>
+ ## <DistributionConfig>
+ ## ... handled by DistributionConfig() class ...
+ ## </DistributionConfig>
+ ## </Distribution>
+
+ def __init__(self, xml):
+ tree = getTreeFromXml(xml)
+ if tree.tag != "Distribution":
+ raise ValueError("Expected <Distribution /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+ ## Normalise some items
+ self.info['LastModifiedTime'] = dateS3toPython(self.info['LastModifiedTime'])
+
+ self.info['DistributionConfig'] = DistributionConfig(tree = tree.find(".//DistributionConfig"))
+
+ def uri(self):
+ return S3Uri("cf://%s" % self.info['Id'])
class DistributionConfig(object):
- ## Example:
- ##
- ## <DistributionConfig>
- ## <Origin>somebucket.s3.amazonaws.com</Origin>
- ## <CallerReference>s3://somebucket/</CallerReference>
- ## <Comment>http://somebucket.s3.amazonaws.com/</Comment>
- ## <Enabled>true</Enabled>
- ## <Logging>
- ## <Bucket>bu.ck.et</Bucket>
- ## <Prefix>/cf-somebucket/</Prefix>
- ## </Logging>
- ## </DistributionConfig>
-
- EMPTY_CONFIG = "<DistributionConfig><Origin/><CallerReference/><Enabled>true</Enabled></DistributionConfig>"
- xmlns = "http://cloudfront.amazonaws.com/doc/%(api_ver)s/" % { 'api_ver' : cloudfront_api_version }
- def __init__(self, xml = None, tree = None):
- if xml is None:
- xml = DistributionConfig.EMPTY_CONFIG
-
- if tree is None:
- tree = getTreeFromXml(xml)
-
- if tree.tag != "DistributionConfig":
- raise ValueError("Expected <DistributionConfig /> xml, got: <%s />" % tree.tag)
- self.parse(tree)
-
- def parse(self, tree):
- self.info = getDictFromTree(tree)
- self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
- if not self.info.has_key("CNAME"):
- self.info['CNAME'] = []
- if type(self.info['CNAME']) != list:
- self.info['CNAME'] = [self.info['CNAME']]
- self.info['CNAME'] = [cname.lower() for cname in self.info['CNAME']]
- if not self.info.has_key("Comment"):
- self.info['Comment'] = ""
- if not self.info.has_key("DefaultRootObject"):
- self.info['DefaultRootObject'] = ""
- ## Figure out logging - complex node not parsed by getDictFromTree()
- logging_nodes = tree.findall(".//Logging")
- if logging_nodes:
- logging_dict = getDictFromTree(logging_nodes[0])
- logging_dict['Bucket'], success = getBucketFromHostname(logging_dict['Bucket'])
- if not success:
- warning("Logging to unparsable bucket name: %s" % logging_dict['Bucket'])
- self.info['Logging'] = S3UriS3("s3://%(Bucket)s/%(Prefix)s" % logging_dict)
- else:
- self.info['Logging'] = None
-
- def __str__(self):
- tree = ET.Element("DistributionConfig")
- tree.attrib['xmlns'] = DistributionConfig.xmlns
-
- ## Retain the order of the following calls!
- appendXmlTextNode("Origin", self.info['Origin'], tree)
- appendXmlTextNode("CallerReference", self.info['CallerReference'], tree)
- for cname in self.info['CNAME']:
- appendXmlTextNode("CNAME", cname.lower(), tree)
- if self.info['Comment']:
- appendXmlTextNode("Comment", self.info['Comment'], tree)
- appendXmlTextNode("Enabled", str(self.info['Enabled']).lower(), tree)
- # don't create a empty DefaultRootObject element as it would result in a MalformedXML error
- if str(self.info['DefaultRootObject']):
- appendXmlTextNode("DefaultRootObject", str(self.info['DefaultRootObject']), tree)
- if self.info['Logging']:
- logging_el = ET.Element("Logging")
- appendXmlTextNode("Bucket", getHostnameFromBucket(self.info['Logging'].bucket()), logging_el)
- appendXmlTextNode("Prefix", self.info['Logging'].object(), logging_el)
- tree.append(logging_el)
- return ET.tostring(tree)
+ ## Example:
+ ##
+ ## <DistributionConfig>
+ ## <Origin>somebucket.s3.amazonaws.com</Origin>
+ ## <CallerReference>s3://somebucket/</CallerReference>
+ ## <Comment>http://somebucket.s3.amazonaws.com/</Comment>
+ ## <Enabled>true</Enabled>
+ ## <Logging>
+ ## <Bucket>bu.ck.et</Bucket>
+ ## <Prefix>/cf-somebucket/</Prefix>
+ ## </Logging>
+ ## </DistributionConfig>
+
+ EMPTY_CONFIG = "<DistributionConfig><Origin/><CallerReference/><Enabled>true</Enabled></DistributionConfig>"
+ xmlns = "http://cloudfront.amazonaws.com/doc/%(api_ver)s/" % { 'api_ver' : cloudfront_api_version }
+ def __init__(self, xml = None, tree = None):
+ if xml is None:
+ xml = DistributionConfig.EMPTY_CONFIG
+
+ if tree is None:
+ tree = getTreeFromXml(xml)
+
+ if tree.tag != "DistributionConfig":
+ raise ValueError("Expected <DistributionConfig /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+ self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
+ if not self.info.has_key("CNAME"):
+ self.info['CNAME'] = []
+ if type(self.info['CNAME']) != list:
+ self.info['CNAME'] = [self.info['CNAME']]
+ self.info['CNAME'] = [cname.lower() for cname in self.info['CNAME']]
+ if not self.info.has_key("Comment"):
+ self.info['Comment'] = ""
+ if not self.info.has_key("DefaultRootObject"):
+ self.info['DefaultRootObject'] = ""
+ ## Figure out logging - complex node not parsed by getDictFromTree()
+ logging_nodes = tree.findall(".//Logging")
+ if logging_nodes:
+ logging_dict = getDictFromTree(logging_nodes[0])
+ logging_dict['Bucket'], success = getBucketFromHostname(logging_dict['Bucket'])
+ if not success:
+ warning("Logging to unparsable bucket name: %s" % logging_dict['Bucket'])
+ self.info['Logging'] = S3UriS3("s3://%(Bucket)s/%(Prefix)s" % logging_dict)
+ else:
+ self.info['Logging'] = None
+
+ def __str__(self):
+ tree = ET.Element("DistributionConfig")
+ tree.attrib['xmlns'] = DistributionConfig.xmlns
+
+ ## Retain the order of the following calls!
+ appendXmlTextNode("Origin", self.info['Origin'], tree)
+ appendXmlTextNode("CallerReference", self.info['CallerReference'], tree)
+ for cname in self.info['CNAME']:
+ appendXmlTextNode("CNAME", cname.lower(), tree)
+ if self.info['Comment']:
+ appendXmlTextNode("Comment", self.info['Comment'], tree)
+ appendXmlTextNode("Enabled", str(self.info['Enabled']).lower(), tree)
+ # don't create a empty DefaultRootObject element as it would result in a MalformedXML error
+ if str(self.info['DefaultRootObject']):
+ appendXmlTextNode("DefaultRootObject", str(self.info['DefaultRootObject']), tree)
+ if self.info['Logging']:
+ logging_el = ET.Element("Logging")
+ appendXmlTextNode("Bucket", getHostnameFromBucket(self.info['Logging'].bucket()), logging_el)
+ appendXmlTextNode("Prefix", self.info['Logging'].object(), logging_el)
+ tree.append(logging_el)
+ return ET.tostring(tree)
class Invalidation(object):
- ## Example:
- ##
- ## <Invalidation xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/">
- ## <Id>id</Id>
- ## <Status>status</Status>
- ## <CreateTime>date</CreateTime>
- ## <InvalidationBatch>
- ## <Path>/image1.jpg</Path>
- ## <Path>/image2.jpg</Path>
- ## <Path>/videos/movie.flv</Path>
- ## <CallerReference>my-batch</CallerReference>
- ## </InvalidationBatch>
- ## </Invalidation>
-
- def __init__(self, xml):
- tree = getTreeFromXml(xml)
- if tree.tag != "Invalidation":
- raise ValueError("Expected <Invalidation /> xml, got: <%s />" % tree.tag)
- self.parse(tree)
-
- def parse(self, tree):
- self.info = getDictFromTree(tree)
-
- def __str__(self):
- return str(self.info)
+ ## Example:
+ ##
+ ## <Invalidation xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/">
+ ## <Id>id</Id>
+ ## <Status>status</Status>
+ ## <CreateTime>date</CreateTime>
+ ## <InvalidationBatch>
+ ## <Path>/image1.jpg</Path>
+ ## <Path>/image2.jpg</Path>
+ ## <Path>/videos/movie.flv</Path>
+ ## <CallerReference>my-batch</CallerReference>
+ ## </InvalidationBatch>
+ ## </Invalidation>
+
+ def __init__(self, xml):
+ tree = getTreeFromXml(xml)
+ if tree.tag != "Invalidation":
+ raise ValueError("Expected <Invalidation /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+
+ def __str__(self):
+ return str(self.info)
class InvalidationList(object):
- ## Example:
- ##
- ## <InvalidationList>
- ## <Marker/>
- ## <NextMarker>Invalidation ID</NextMarker>
- ## <MaxItems>2</MaxItems>
- ## <IsTruncated>true</IsTruncated>
- ## <InvalidationSummary>
- ## <Id>[Second Invalidation ID]</Id>
- ## <Status>Completed</Status>
- ## </InvalidationSummary>
- ## <InvalidationSummary>
- ## <Id>[First Invalidation ID]</Id>
- ## <Status>Completed</Status>
- ## </InvalidationSummary>
- ## </InvalidationList>
-
- def __init__(self, xml):
- tree = getTreeFromXml(xml)
- if tree.tag != "InvalidationList":
- raise ValueError("Expected <InvalidationList /> xml, got: <%s />" % tree.tag)
- self.parse(tree)
-
- def parse(self, tree):
- self.info = getDictFromTree(tree)
-
- def __str__(self):
- return str(self.info)
+ ## Example:
+ ##
+ ## <InvalidationList>
+ ## <Marker/>
+ ## <NextMarker>Invalidation ID</NextMarker>
+ ## <MaxItems>2</MaxItems>
+ ## <IsTruncated>true</IsTruncated>
+ ## <InvalidationSummary>
+ ## <Id>[Second Invalidation ID]</Id>
+ ## <Status>Completed</Status>
+ ## </InvalidationSummary>
+ ## <InvalidationSummary>
+ ## <Id>[First Invalidation ID]</Id>
+ ## <Status>Completed</Status>
+ ## </InvalidationSummary>
+ ## </InvalidationList>
+
+ def __init__(self, xml):
+ tree = getTreeFromXml(xml)
+ if tree.tag != "InvalidationList":
+ raise ValueError("Expected <InvalidationList /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+
+ def __str__(self):
+ return str(self.info)
class InvalidationBatch(object):
- ## Example:
- ##
- ## <InvalidationBatch>
- ## <Path>/image1.jpg</Path>
- ## <Path>/image2.jpg</Path>
- ## <Path>/videos/movie.flv</Path>
- ## <Path>/sound%20track.mp3</Path>
- ## <CallerReference>my-batch</CallerReference>
- ## </InvalidationBatch>
-
- def __init__(self, reference = None, distribution = None, paths = []):
- if reference:
- self.reference = reference
- else:
- if not distribution:
- distribution="0"
- self.reference = "%s.%s.%s" % (distribution,
- datetime.strftime(datetime.now(),"%Y%m%d%H%M%S"),
- random.randint(1000,9999))
- self.paths = []
- self.add_objects(paths)
-
- def add_objects(self, paths):
- self.paths.extend(paths)
-
- def get_reference(self):
- return self.reference
-
- def __str__(self):
- tree = ET.Element("InvalidationBatch")
-
- for path in self.paths:
- if path[0] != "/":
- path = "/" + path
- appendXmlTextNode("Path", path, tree)
- appendXmlTextNode("CallerReference", self.reference, tree)
- return ET.tostring(tree)
+ ## Example:
+ ##
+ ## <InvalidationBatch>
+ ## <Path>/image1.jpg</Path>
+ ## <Path>/image2.jpg</Path>
+ ## <Path>/videos/movie.flv</Path>
+ ## <Path>/sound%20track.mp3</Path>
+ ## <CallerReference>my-batch</CallerReference>
+ ## </InvalidationBatch>
+
+ def __init__(self, reference = None, distribution = None, paths = []):
+ if reference:
+ self.reference = reference
+ else:
+ if not distribution:
+ distribution="0"
+ self.reference = "%s.%s.%s" % (distribution,
+ datetime.strftime(datetime.now(),"%Y%m%d%H%M%S"),
+ random.randint(1000,9999))
+ self.paths = []
+ self.add_objects(paths)
+
+ def add_objects(self, paths):
+ self.paths.extend(paths)
+
+ def get_reference(self):
+ return self.reference
+
+ def __str__(self):
+ tree = ET.Element("InvalidationBatch")
+
+ for path in self.paths:
+ if path[0] != "/":
+ path = "/" + path
+ appendXmlTextNode("Path", path, tree)
+ appendXmlTextNode("CallerReference", self.reference, tree)
+ return ET.tostring(tree)
class CloudFront(object):
- operations = {
- "CreateDist" : { 'method' : "POST", 'resource' : "" },
- "DeleteDist" : { 'method' : "DELETE", 'resource' : "/%(dist_id)s" },
- "GetList" : { 'method' : "GET", 'resource' : "" },
- "GetDistInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s" },
- "GetDistConfig" : { 'method' : "GET", 'resource' : "/%(dist_id)s/config" },
- "SetDistConfig" : { 'method' : "PUT", 'resource' : "/%(dist_id)s/config" },
- "Invalidate" : { 'method' : "POST", 'resource' : "/%(dist_id)s/invalidation" },
- "GetInvalList" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation" },
- "GetInvalInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation/%(request_id)s" },
- }
-
- ## Maximum attempts of re-issuing failed requests
- _max_retries = 5
- dist_list = None
-
- def __init__(self, config):
- self.config = config
-
- ## --------------------------------------------------
- ## Methods implementing CloudFront API
- ## --------------------------------------------------
-
- def GetList(self):
- response = self.send_request("GetList")
- response['dist_list'] = DistributionList(response['data'])
- if response['dist_list'].info['IsTruncated']:
- raise NotImplementedError("List is truncated. Ask s3cmd author to add support.")
- ## TODO: handle Truncated
- return response
-
- def CreateDistribution(self, uri, cnames_add = [], comment = None, logging = None, default_root_object = None):
- dist_config = DistributionConfig()
- dist_config.info['Enabled'] = True
- dist_config.info['Origin'] = uri.host_name()
- dist_config.info['CallerReference'] = str(uri)
- dist_config.info['DefaultRootObject'] = default_root_object
- if comment == None:
- dist_config.info['Comment'] = uri.public_url()
- else:
- dist_config.info['Comment'] = comment
- for cname in cnames_add:
- if dist_config.info['CNAME'].count(cname) == 0:
- dist_config.info['CNAME'].append(cname)
- if logging:
- dist_config.info['Logging'] = S3UriS3(logging)
- request_body = str(dist_config)
- debug("CreateDistribution(): request_body: %s" % request_body)
- response = self.send_request("CreateDist", body = request_body)
- response['distribution'] = Distribution(response['data'])
- return response
-
- def ModifyDistribution(self, cfuri, cnames_add = [], cnames_remove = [],
- comment = None, enabled = None, logging = None,
+ operations = {
+ "CreateDist" : { 'method' : "POST", 'resource' : "" },
+ "DeleteDist" : { 'method' : "DELETE", 'resource' : "/%(dist_id)s" },
+ "GetList" : { 'method' : "GET", 'resource' : "" },
+ "GetDistInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s" },
+ "GetDistConfig" : { 'method' : "GET", 'resource' : "/%(dist_id)s/config" },
+ "SetDistConfig" : { 'method' : "PUT", 'resource' : "/%(dist_id)s/config" },
+ "Invalidate" : { 'method' : "POST", 'resource' : "/%(dist_id)s/invalidation" },
+ "GetInvalList" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation" },
+ "GetInvalInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation/%(request_id)s" },
+ }
+
+ ## Maximum attempts of re-issuing failed requests
+ _max_retries = 5
+ dist_list = None
+
+ def __init__(self, config):
+ self.config = config
+
+ ## --------------------------------------------------
+ ## Methods implementing CloudFront API
+ ## --------------------------------------------------
+
+ def GetList(self):
+ response = self.send_request("GetList")
+ response['dist_list'] = DistributionList(response['data'])
+ if response['dist_list'].info['IsTruncated']:
+ raise NotImplementedError("List is truncated. Ask s3cmd author to add support.")
+ ## TODO: handle Truncated
+ return response
+
+ def CreateDistribution(self, uri, cnames_add = [], comment = None, logging = None, default_root_object = None):
+ dist_config = DistributionConfig()
+ dist_config.info['Enabled'] = True
+ dist_config.info['Origin'] = uri.host_name()
+ dist_config.info['CallerReference'] = str(uri)
+ dist_config.info['DefaultRootObject'] = default_root_object
+ if comment == None:
+ dist_config.info['Comment'] = uri.public_url()
+ else:
+ dist_config.info['Comment'] = comment
+ for cname in cnames_add:
+ if dist_config.info['CNAME'].count(cname) == 0:
+ dist_config.info['CNAME'].append(cname)
+ if logging:
+ dist_config.info['Logging'] = S3UriS3(logging)
+ request_body = str(dist_config)
+ debug("CreateDistribution(): request_body: %s" % request_body)
+ response = self.send_request("CreateDist", body = request_body)
+ response['distribution'] = Distribution(response['data'])
+ return response
+
+ def ModifyDistribution(self, cfuri, cnames_add = [], cnames_remove = [],
+ comment = None, enabled = None, logging = None,
default_root_object = None):
- if cfuri.type != "cf":
- raise ValueError("Expected CFUri instead of: %s" % cfuri)
- # Get current dist status (enabled/disabled) and Etag
- info("Checking current status of %s" % cfuri)
- response = self.GetDistConfig(cfuri)
- dc = response['dist_config']
- if enabled != None:
- dc.info['Enabled'] = enabled
- if comment != None:
- dc.info['Comment'] = comment
- if default_root_object != None:
- dc.info['DefaultRootObject'] = default_root_object
- for cname in cnames_add:
- if dc.info['CNAME'].count(cname) == 0:
- dc.info['CNAME'].append(cname)
- for cname in cnames_remove:
- while dc.info['CNAME'].count(cname) > 0:
- dc.info['CNAME'].remove(cname)
- if logging != None:
- if logging == False:
- dc.info['Logging'] = False
- else:
- dc.info['Logging'] = S3UriS3(logging)
- response = self.SetDistConfig(cfuri, dc, response['headers']['etag'])
- return response
-
- def DeleteDistribution(self, cfuri):
- if cfuri.type != "cf":
- raise ValueError("Expected CFUri instead of: %s" % cfuri)
- # Get current dist status (enabled/disabled) and Etag
- info("Checking current status of %s" % cfuri)
- response = self.GetDistConfig(cfuri)
- if response['dist_config'].info['Enabled']:
- info("Distribution is ENABLED. Disabling first.")
- response['dist_config'].info['Enabled'] = False
- response = self.SetDistConfig(cfuri, response['dist_config'],
- response['headers']['etag'])
- warning("Waiting for Distribution to become disabled.")
- warning("This may take several minutes, please wait.")
- while True:
- response = self.GetDistInfo(cfuri)
- d = response['distribution']
- if d.info['Status'] == "Deployed" and d.info['Enabled'] == False:
- info("Distribution is now disabled")
- break
- warning("Still waiting...")
- time.sleep(10)
- headers = {}
- headers['if-match'] = response['headers']['etag']
- response = self.send_request("DeleteDist", dist_id = cfuri.dist_id(),
- headers = headers)
- return response
-
- def GetDistInfo(self, cfuri):
- if cfuri.type != "cf":
- raise ValueError("Expected CFUri instead of: %s" % cfuri)
- response = self.send_request("GetDistInfo", dist_id = cfuri.dist_id())
- response['distribution'] = Distribution(response['data'])
- return response
-
- def GetDistConfig(self, cfuri):
- if cfuri.type != "cf":
- raise ValueError("Expected CFUri instead of: %s" % cfuri)
- response = self.send_request("GetDistConfig", dist_id = cfuri.dist_id())
- response['dist_config'] = DistributionConfig(response['data'])
- return response
-
- def SetDistConfig(self, cfuri, dist_config, etag = None):
- if etag == None:
- debug("SetDistConfig(): Etag not set. Fetching it first.")
- etag = self.GetDistConfig(cfuri)['headers']['etag']
- debug("SetDistConfig(): Etag = %s" % etag)
- request_body = str(dist_config)
- debug("SetDistConfig(): request_body: %s" % request_body)
- headers = {}
- headers['if-match'] = etag
- response = self.send_request("SetDistConfig", dist_id = cfuri.dist_id(),
- body = request_body, headers = headers)
- return response
-
- def InvalidateObjects(self, uri, paths):
- # uri could be either cf:// or s3:// uri
- cfuri = self.get_dist_name_for_bucket(uri)
- if len(paths) > 999:
- try:
- tmp_filename = Utils.mktmpfile()
- f = open(tmp_filename, "w")
- f.write("\n".join(paths)+"\n")
- f.close()
- warning("Request to invalidate %d paths (max 999 supported)" % len(paths))
- warning("All the paths are now saved in: %s" % tmp_filename)
- except:
- pass
- raise ParameterError("Too many paths to invalidate")
- invalbatch = InvalidationBatch(distribution = cfuri.dist_id(), paths = paths)
- debug("InvalidateObjects(): request_body: %s" % invalbatch)
- response = self.send_request("Invalidate", dist_id = cfuri.dist_id(),
- body = str(invalbatch))
- response['dist_id'] = cfuri.dist_id()
- if response['status'] == 201:
- inval_info = Invalidation(response['data']).info
- response['request_id'] = inval_info['Id']
- debug("InvalidateObjects(): response: %s" % response)
- return response
-
- def GetInvalList(self, cfuri):
- if cfuri.type != "cf":
- raise ValueError("Expected CFUri instead of: %s" % cfuri)
- response = self.send_request("GetInvalList", dist_id = cfuri.dist_id())
- response['inval_list'] = InvalidationList(response['data'])
- return response
-
- def GetInvalInfo(self, cfuri):
- if cfuri.type != "cf":
- raise ValueError("Expected CFUri instead of: %s" % cfuri)
- if cfuri.request_id() is None:
- raise ValueError("Expected CFUri with Request ID")
- response = self.send_request("GetInvalInfo", dist_id = cfuri.dist_id(), request_id = cfuri.request_id())
- response['inval_status'] = Invalidation(response['data'])
- return response
-
- ## --------------------------------------------------
- ## Low-level methods for handling CloudFront requests
- ## --------------------------------------------------
-
- def send_request(self, op_name, dist_id = None, request_id = None, body = None, headers = {}, retries = _max_retries):
- operation = self.operations[op_name]
- if body:
- headers['content-type'] = 'text/plain'
- request = self.create_request(operation, dist_id, request_id, headers)
- conn = self.get_connection()
- debug("send_request(): %s %s" % (request['method'], request['resource']))
- conn.request(request['method'], request['resource'], body, request['headers'])
- http_response = conn.getresponse()
- response = {}
- response["status"] = http_response.status
- response["reason"] = http_response.reason
- response["headers"] = dict(http_response.getheaders())
- response["data"] = http_response.read()
- conn.close()
-
- debug("CloudFront: response: %r" % response)
-
- if response["status"] >= 500:
- e = CloudFrontError(response)
- if retries:
- warning(u"Retrying failed request: %s" % op_name)
- warning(unicode(e))
- warning("Waiting %d sec..." % self._fail_wait(retries))
- time.sleep(self._fail_wait(retries))
- return self.send_request(op_name, dist_id, body, retries - 1)
- else:
- raise e
-
- if response["status"] < 200 or response["status"] > 299:
- raise CloudFrontError(response)
-
- return response
-
- def create_request(self, operation, dist_id = None, request_id = None, headers = None):
- resource = cloudfront_resource + (
- operation['resource'] % { 'dist_id' : dist_id, 'request_id' : request_id })
-
- if not headers:
- headers = {}
-
- if headers.has_key("date"):
- if not headers.has_key("x-amz-date"):
- headers["x-amz-date"] = headers["date"]
- del(headers["date"])
-
- if not headers.has_key("x-amz-date"):
- headers["x-amz-date"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
-
- signature = self.sign_request(headers)
- headers["Authorization"] = "AWS "+self.config.access_key+":"+signature
-
- request = {}
- request['resource'] = resource
- request['headers'] = headers
- request['method'] = operation['method']
-
- return request
-
- def sign_request(self, headers):
- string_to_sign = headers['x-amz-date']
- signature = sign_string(string_to_sign)
- debug(u"CloudFront.sign_request('%s') = %s" % (string_to_sign, signature))
- return signature
-
- def get_connection(self):
- if self.config.proxy_host != "":
- raise ParameterError("CloudFront commands don't work from behind a HTTP proxy")
- return httplib.HTTPSConnection(self.config.cloudfront_host)
-
- def _fail_wait(self, retries):
- # Wait a few seconds. The more it fails the more we wait.
- return (self._max_retries - retries + 1) * 3
-
- def get_dist_name_for_bucket(self, uri):
- if (uri.type == "cf"):
- return uri
- if (uri.type != "s3"):
- raise ParameterError("CloudFront or S3 URI required instead of: %s" % arg)
-
- debug("_get_dist_name_for_bucket(%r)" % uri)
- if CloudFront.dist_list is None:
- response = self.GetList()
- CloudFront.dist_list = {}
- for d in response['dist_list'].dist_summs:
- CloudFront.dist_list[getBucketFromHostname(d.info['S3Origin']['DNSName'])[0]] = d.uri()
- debug("dist_list: %s" % CloudFront.dist_list)
- try:
- return CloudFront.dist_list[uri.bucket()]
- except Exception, e:
- debug(e)
- raise ParameterError("Unable to translate S3 URI to CloudFront distribution name: %s" % arg)
+ if cfuri.type != "cf":
+ raise ValueError("Expected CFUri instead of: %s" % cfuri)
+ # Get current dist status (enabled/disabled) and Etag
+ info("Checking current status of %s" % cfuri)
+ response = self.GetDistConfig(cfuri)
+ dc = response['dist_config']
+ if enabled != None:
+ dc.info['Enabled'] = enabled
+ if comment != None:
+ dc.info['Comment'] = comment
+ if default_root_object != None:
+ dc.info['DefaultRootObject'] = default_root_object
+ for cname in cnames_add:
+ if dc.info['CNAME'].count(cname) == 0:
+ dc.info['CNAME'].append(cname)
+ for cname in cnames_remove:
+ while dc.info['CNAME'].count(cname) > 0:
+ dc.info['CNAME'].remove(cname)
+ if logging != None:
+ if logging == False:
+ dc.info['Logging'] = False
+ else:
+ dc.info['Logging'] = S3UriS3(logging)
+ response = self.SetDistConfig(cfuri, dc, response['headers']['etag'])
+ return response
+
+ def DeleteDistribution(self, cfuri):
+ if cfuri.type != "cf":
+ raise ValueError("Expected CFUri instead of: %s" % cfuri)
+ # Get current dist status (enabled/disabled) and Etag
+ info("Checking current status of %s" % cfuri)
+ response = self.GetDistConfig(cfuri)
+ if response['dist_config'].info['Enabled']:
+ info("Distribution is ENABLED. Disabling first.")
+ response['dist_config'].info['Enabled'] = False
+ response = self.SetDistConfig(cfuri, response['dist_config'],
+ response['headers']['etag'])
+ warning("Waiting for Distribution to become disabled.")
+ warning("This may take several minutes, please wait.")
+ while True:
+ response = self.GetDistInfo(cfuri)
+ d = response['distribution']
+ if d.info['Status'] == "Deployed" and d.info['Enabled'] == False:
+ info("Distribution is now disabled")
+ break
+ warning("Still waiting...")
+ time.sleep(10)
+ headers = {}
+ headers['if-match'] = response['headers']['etag']
+ response = self.send_request("DeleteDist", dist_id = cfuri.dist_id(),
+ headers = headers)
+ return response
+
+ def GetDistInfo(self, cfuri):
+ if cfuri.type != "cf":
+ raise ValueError("Expected CFUri instead of: %s" % cfuri)
+ response = self.send_request("GetDistInfo", dist_id = cfuri.dist_id())
+ response['distribution'] = Distribution(response['data'])
+ return response
+
+ def GetDistConfig(self, cfuri):
+ if cfuri.type != "cf":
+ raise ValueError("Expected CFUri instead of: %s" % cfuri)
+ response = self.send_request("GetDistConfig", dist_id = cfuri.dist_id())
+ response['dist_config'] = DistributionConfig(response['data'])
+ return response
+
+ def SetDistConfig(self, cfuri, dist_config, etag = None):
+ if etag == None:
+ debug("SetDistConfig(): Etag not set. Fetching it first.")
+ etag = self.GetDistConfig(cfuri)['headers']['etag']
+ debug("SetDistConfig(): Etag = %s" % etag)
+ request_body = str(dist_config)
+ debug("SetDistConfig(): request_body: %s" % request_body)
+ headers = {}
+ headers['if-match'] = etag
+ response = self.send_request("SetDistConfig", dist_id = cfuri.dist_id(),
+ body = request_body, headers = headers)
+ return response
+
+ def InvalidateObjects(self, uri, paths):
+ # uri could be either cf:// or s3:// uri
+ cfuri = self.get_dist_name_for_bucket(uri)
+ if len(paths) > 999:
+ try:
+ tmp_filename = Utils.mktmpfile()
+ f = open(tmp_filename, "w")
+ f.write("\n".join(paths)+"\n")
+ f.close()
+ warning("Request to invalidate %d paths (max 999 supported)" % len(paths))
+ warning("All the paths are now saved in: %s" % tmp_filename)
+ except:
+ pass
+ raise ParameterError("Too many paths to invalidate")
+ invalbatch = InvalidationBatch(distribution = cfuri.dist_id(), paths = paths)
+ debug("InvalidateObjects(): request_body: %s" % invalbatch)
+ response = self.send_request("Invalidate", dist_id = cfuri.dist_id(),
+ body = str(invalbatch))
+ response['dist_id'] = cfuri.dist_id()
+ if response['status'] == 201:
+ inval_info = Invalidation(response['data']).info
+ response['request_id'] = inval_info['Id']
+ debug("InvalidateObjects(): response: %s" % response)
+ return response
+
+ def GetInvalList(self, cfuri):
+ if cfuri.type != "cf":
+ raise ValueError("Expected CFUri instead of: %s" % cfuri)
+ response = self.send_request("GetInvalList", dist_id = cfuri.dist_id())
+ response['inval_list'] = InvalidationList(response['data'])
+ return response
+
+ def GetInvalInfo(self, cfuri):
+ if cfuri.type != "cf":
+ raise ValueError("Expected CFUri instead of: %s" % cfuri)
+ if cfuri.request_id() is None:
+ raise ValueError("Expected CFUri with Request ID")
+ response = self.send_request("GetInvalInfo", dist_id = cfuri.dist_id(), request_id = cfuri.request_id())
+ response['inval_status'] = Invalidation(response['data'])
+ return response
+
+ ## --------------------------------------------------
+ ## Low-level methods for handling CloudFront requests
+ ## --------------------------------------------------
+
+ def send_request(self, op_name, dist_id = None, request_id = None, body = None, headers = {}, retries = _max_retries):
+ operation = self.operations[op_name]
+ if body:
+ headers['content-type'] = 'text/plain'
+ request = self.create_request(operation, dist_id, request_id, headers)
+ conn = self.get_connection()
+ debug("send_request(): %s %s" % (request['method'], request['resource']))
+ conn.request(request['method'], request['resource'], body, request['headers'])
+ http_response = conn.getresponse()
+ response = {}
+ response["status"] = http_response.status
+ response["reason"] = http_response.reason
+ response["headers"] = dict(http_response.getheaders())
+ response["data"] = http_response.read()
+ conn.close()
+
+ debug("CloudFront: response: %r" % response)
+
+ if response["status"] >= 500:
+ e = CloudFrontError(response)
+ if retries:
+ warning(u"Retrying failed request: %s" % op_name)
+ warning(unicode(e))
+ warning("Waiting %d sec..." % self._fail_wait(retries))
+ time.sleep(self._fail_wait(retries))
+ return self.send_request(op_name, dist_id, body, retries - 1)
+ else:
+ raise e
+
+ if response["status"] < 200 or response["status"] > 299:
+ raise CloudFrontError(response)
+
+ return response
+
+ def create_request(self, operation, dist_id = None, request_id = None, headers = None):
+ resource = cloudfront_resource + (
+ operation['resource'] % { 'dist_id' : dist_id, 'request_id' : request_id })
+
+ if not headers:
+ headers = {}
+
+ if headers.has_key("date"):
+ if not headers.has_key("x-amz-date"):
+ headers["x-amz-date"] = headers["date"]
+ del(headers["date"])
+
+ if not headers.has_key("x-amz-date"):
+ headers["x-amz-date"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
+
+ signature = self.sign_request(headers)
+ headers["Authorization"] = "AWS "+self.config.access_key+":"+signature
+
+ request = {}
+ request['resource'] = resource
+ request['headers'] = headers
+ request['method'] = operation['method']
+
+ return request
+
+ def sign_request(self, headers):
+ string_to_sign = headers['x-amz-date']
+ signature = sign_string(string_to_sign)
+ debug(u"CloudFront.sign_request('%s') = %s" % (string_to_sign, signature))
+ return signature
+
+ def get_connection(self):
+ if self.config.proxy_host != "":
+ raise ParameterError("CloudFront commands don't work from behind a HTTP proxy")
+ return httplib.HTTPSConnection(self.config.cloudfront_host)
+
+ def _fail_wait(self, retries):
+ # Wait a few seconds. The more it fails the more we wait.
+ return (self._max_retries - retries + 1) * 3
+
+ def get_dist_name_for_bucket(self, uri):
+ if (uri.type == "cf"):
+ return uri
+ if (uri.type != "s3"):
+ raise ParameterError("CloudFront or S3 URI required instead of: %s" % arg)
+
+ debug("_get_dist_name_for_bucket(%r)" % uri)
+ if CloudFront.dist_list is None:
+ response = self.GetList()
+ CloudFront.dist_list = {}
+ for d in response['dist_list'].dist_summs:
+ CloudFront.dist_list[getBucketFromHostname(d.info['S3Origin']['DNSName'])[0]] = d.uri()
+ debug("dist_list: %s" % CloudFront.dist_list)
+ try:
+ return CloudFront.dist_list[uri.bucket()]
+ except Exception, e:
+ debug(e)
+ raise ParameterError("Unable to translate S3 URI to CloudFront distribution name: %s" % arg)
class Cmd(object):
- """
- Class that implements CloudFront commands
- """
-
- class Options(object):
- cf_cnames_add = []
- cf_cnames_remove = []
- cf_comment = None
- cf_enable = None
- cf_logging = None
- cf_default_root_object = None
-
- def option_list(self):
- return [opt for opt in dir(self) if opt.startswith("cf_")]
-
- def update_option(self, option, value):
- setattr(Cmd.options, option, value)
-
- options = Options()
-
- @staticmethod
- def _parse_args(args):
- cf = CloudFront(Config())
- cfuris = []
- for arg in args:
- uri = cf.get_dist_name_for_bucket(S3Uri(arg))
- cfuris.append(uri)
- return cfuris
-
- @staticmethod
- def info(args):
- cf = CloudFront(Config())
- if not args:
- response = cf.GetList()
- for d in response['dist_list'].dist_summs:
- if d.info.has_key("S3Origin"):
- origin = S3UriS3.httpurl_to_s3uri(d.info['S3Origin']['DNSName'])
- elif d.info.has_key("CustomOrigin"):
- origin = "http://%s/" % d.info['CustomOrigin']['DNSName']
- else:
- origin = "<unknown>"
- pretty_output("Origin", origin)
- pretty_output("DistId", d.uri())
- pretty_output("DomainName", d.info['DomainName'])
- if d.info.has_key("CNAME"):
- pretty_output("CNAMEs", ", ".join(d.info['CNAME']))
- pretty_output("Status", d.info['Status'])
- pretty_output("Enabled", d.info['Enabled'])
- output("")
- else:
- cfuris = Cmd._parse_args(args)
- for cfuri in cfuris:
- response = cf.GetDistInfo(cfuri)
- d = response['distribution']
- dc = d.info['DistributionConfig']
- if dc.info.has_key("S3Origin"):
- origin = S3UriS3.httpurl_to_s3uri(dc.info['S3Origin']['DNSName'])
- elif dc.info.has_key("CustomOrigin"):
- origin = "http://%s/" % dc.info['CustomOrigin']['DNSName']
- else:
- origin = "<unknown>"
- pretty_output("Origin", origin)
- pretty_output("DistId", d.uri())
- pretty_output("DomainName", d.info['DomainName'])
- if dc.info.has_key("CNAME"):
- pretty_output("CNAMEs", ", ".join(dc.info['CNAME']))
- pretty_output("Status", d.info['Status'])
- pretty_output("Comment", dc.info['Comment'])
- pretty_output("Enabled", dc.info['Enabled'])
- pretty_output("DfltRootObject", dc.info['DefaultRootObject'])
- pretty_output("Logging", dc.info['Logging'] or "Disabled")
- pretty_output("Etag", response['headers']['etag'])
-
- @staticmethod
- def create(args):
- cf = CloudFront(Config())
- buckets = []
- for arg in args:
- uri = S3Uri(arg)
- if uri.type != "s3":
- raise ParameterError("Bucket can only be created from a s3:// URI instead of: %s" % arg)
- if uri.object():
- raise ParameterError("Use s3:// URI with a bucket name only instead of: %s" % arg)
- if not uri.is_dns_compatible():
- raise ParameterError("CloudFront can only handle lowercase-named buckets.")
- buckets.append(uri)
- if not buckets:
- raise ParameterError("No valid bucket names found")
- for uri in buckets:
- info("Creating distribution from: %s" % uri)
- response = cf.CreateDistribution(uri, cnames_add = Cmd.options.cf_cnames_add,
- comment = Cmd.options.cf_comment,
- logging = Cmd.options.cf_logging,
+ """
+ Class that implements CloudFront commands
+ """
+
+ class Options(object):
+ cf_cnames_add = []
+ cf_cnames_remove = []
+ cf_comment = None
+ cf_enable = None
+ cf_logging = None
+ cf_default_root_object = None
+
+ def option_list(self):
+ return [opt for opt in dir(self) if opt.startswith("cf_")]
+
+ def update_option(self, option, value):
+ setattr(Cmd.options, option, value)
+
+ options = Options()
+
+ @staticmethod
+ def _parse_args(args):
+ cf = CloudFront(Config())
+ cfuris = []
+ for arg in args:
+ uri = cf.get_dist_name_for_bucket(S3Uri(arg))
+ cfuris.append(uri)
+ return cfuris
+
+ @staticmethod
+ def info(args):
+ cf = CloudFront(Config())
+ if not args:
+ response = cf.GetList()
+ for d in response['dist_list'].dist_summs:
+ if d.info.has_key("S3Origin"):
+ origin = S3UriS3.httpurl_to_s3uri(d.info['S3Origin']['DNSName'])
+ elif d.info.has_key("CustomOrigin"):
+ origin = "http://%s/" % d.info['CustomOrigin']['DNSName']
+ else:
+ origin = "<unknown>"
+ pretty_output("Origin", origin)
+ pretty_output("DistId", d.uri())
+ pretty_output("DomainName", d.info['DomainName'])
+ if d.info.has_key("CNAME"):
+ pretty_output("CNAMEs", ", ".join(d.info['CNAME']))
+ pretty_output("Status", d.info['Status'])
+ pretty_output("Enabled", d.info['Enabled'])
+ output("")
+ else:
+ cfuris = Cmd._parse_args(args)
+ for cfuri in cfuris:
+ response = cf.GetDistInfo(cfuri)
+ d = response['distribution']
+ dc = d.info['DistributionConfig']
+ if dc.info.has_key("S3Origin"):
+ origin = S3UriS3.httpurl_to_s3uri(dc.info['S3Origin']['DNSName'])
+ elif dc.info.has_key("CustomOrigin"):
+ origin = "http://%s/" % dc.info['CustomOrigin']['DNSName']
+ else:
+ origin = "<unknown>"
+ pretty_output("Origin", origin)
+ pretty_output("DistId", d.uri())
+ pretty_output("DomainName", d.info['DomainName'])
+ if dc.info.has_key("CNAME"):
+ pretty_output("CNAMEs", ", ".join(dc.info['CNAME']))
+ pretty_output("Status", d.info['Status'])
+ pretty_output("Comment", dc.info['Comment'])
+ pretty_output("Enabled", dc.info['Enabled'])
+ pretty_output("DfltRootObject", dc.info['DefaultRootObject'])
+ pretty_output("Logging", dc.info['Logging'] or "Disabled")
+ pretty_output("Etag", response['headers']['etag'])
+
+ @staticmethod
+ def create(args):
+ cf = CloudFront(Config())
+ buckets = []
+ for arg in args:
+ uri = S3Uri(arg)
+ if uri.type != "s3":
+ raise ParameterError("Bucket can only be created from a s3:// URI instead of: %s" % arg)
+ if uri.object():
+ raise ParameterError("Use s3:// URI with a bucket name only instead of: %s" % arg)
+ if not uri.is_dns_compatible():
+ raise ParameterError("CloudFront can only handle lowercase-named buckets.")
+ buckets.append(uri)
+ if not buckets:
+ raise ParameterError("No valid bucket names found")
+ for uri in buckets:
+ info("Creating distribution from: %s" % uri)
+ response = cf.CreateDistribution(uri, cnames_add = Cmd.options.cf_cnames_add,
+ comment = Cmd.options.cf_comment,
+ logging = Cmd.options.cf_logging,
default_root_object = Cmd.options.cf_default_root_object)
- d = response['distribution']
- dc = d.info['DistributionConfig']
- output("Distribution created:")
- pretty_output("Origin", S3UriS3.httpurl_to_s3uri(dc.info['Origin']))
- pretty_output("DistId", d.uri())
- pretty_output("DomainName", d.info['DomainName'])
- pretty_output("CNAMEs", ", ".join(dc.info['CNAME']))
- pretty_output("Comment", dc.info['Comment'])
- pretty_output("Status", d.info['Status'])
- pretty_output("Enabled", dc.info['Enabled'])
- pretty_output("DefaultRootObject", dc.info['DefaultRootObject'])
- pretty_output("Etag", response['headers']['etag'])
-
- @staticmethod
- def delete(args):
- cf = CloudFront(Config())
- cfuris = Cmd._parse_args(args)
- for cfuri in cfuris:
- response = cf.DeleteDistribution(cfuri)
- if response['status'] >= 400:
- error("Distribution %s could not be deleted: %s" % (cfuri, response['reason']))
- output("Distribution %s deleted" % cfuri)
-
- @staticmethod
- def modify(args):
- cf = CloudFront(Config())
- if len(args) > 1:
- raise ParameterError("Too many parameters. Modify one Distribution at a time.")
- try:
- cfuri = Cmd._parse_args(args)[0]
- except IndexError, e:
- raise ParameterError("No valid Distribution URI found.")
- response = cf.ModifyDistribution(cfuri,
- cnames_add = Cmd.options.cf_cnames_add,
- cnames_remove = Cmd.options.cf_cnames_remove,
- comment = Cmd.options.cf_comment,
- enabled = Cmd.options.cf_enable,
- logging = Cmd.options.cf_logging,
+ d = response['distribution']
+ dc = d.info['DistributionConfig']
+ output("Distribution created:")
+ pretty_output("Origin", S3UriS3.httpurl_to_s3uri(dc.info['Origin']))
+ pretty_output("DistId", d.uri())
+ pretty_output("DomainName", d.info['DomainName'])
+ pretty_output("CNAMEs", ", ".join(dc.info['CNAME']))
+ pretty_output("Comment", dc.info['Comment'])
+ pretty_output("Status", d.info['Status'])
+ pretty_output("Enabled", dc.info['Enabled'])
+ pretty_output("DefaultRootObject", dc.info['DefaultRootObject'])
+ pretty_output("Etag", response['headers']['etag'])
+
+ @staticmethod
+ def delete(args):
+ cf = CloudFront(Config())
+ cfuris = Cmd._parse_args(args)
+ for cfuri in cfuris:
+ response = cf.DeleteDistribution(cfuri)
+ if response['status'] >= 400:
+ error("Distribution %s could not be deleted: %s" % (cfuri, response['reason']))
+ output("Distribution %s deleted" % cfuri)
+
+ @staticmethod
+ def modify(args):
+ cf = CloudFront(Config())
+ if len(args) > 1:
+ raise ParameterError("Too many parameters. Modify one Distribution at a time.")
+ try:
+ cfuri = Cmd._parse_args(args)[0]
+ except IndexError, e:
+ raise ParameterError("No valid Distribution URI found.")
+ response = cf.ModifyDistribution(cfuri,
+ cnames_add = Cmd.options.cf_cnames_add,
+ cnames_remove = Cmd.options.cf_cnames_remove,
+ comment = Cmd.options.cf_comment,
+ enabled = Cmd.options.cf_enable,
+ logging = Cmd.options.cf_logging,
default_root_object = Cmd.options.cf_default_root_object)
- if response['status'] >= 400:
- error("Distribution %s could not be modified: %s" % (cfuri, response['reason']))
- output("Distribution modified: %s" % cfuri)
- response = cf.GetDistInfo(cfuri)
- d = response['distribution']
- dc = d.info['DistributionConfig']
- pretty_output("Origin", S3UriS3.httpurl_to_s3uri(dc.info['Origin']))
- pretty_output("DistId", d.uri())
- pretty_output("DomainName", d.info['DomainName'])
- pretty_output("Status", d.info['Status'])
- pretty_output("CNAMEs", ", ".join(dc.info['CNAME']))
- pretty_output("Comment", dc.info['Comment'])
- pretty_output("Enabled", dc.info['Enabled'])
- pretty_output("DefaultRootObject", dc.info['DefaultRootObject'])
- pretty_output("Etag", response['headers']['etag'])
-
- @staticmethod
- def invalinfo(args):
- cf = CloudFront(Config())
- cfuris = Cmd._parse_args(args)
- requests = []
- for cfuri in cfuris:
- if cfuri.request_id():
- requests.append(str(cfuri))
- else:
- inval_list = cf.GetInvalList(cfuri)
- try:
- for i in inval_list['inval_list'].info['InvalidationSummary']:
- requests.append("/".join(["cf:/", cfuri.dist_id(), i["Id"]]))
- except:
- continue
- for req in requests:
- cfuri = S3Uri(req)
- inval_info = cf.GetInvalInfo(cfuri)
- st = inval_info['inval_status'].info
- pretty_output("URI", str(cfuri))
- pretty_output("Status", st['Status'])
- pretty_output("Created", st['CreateTime'])
- pretty_output("Nr of paths", len(st['InvalidationBatch']['Path']))
- pretty_output("Reference", st['InvalidationBatch']['CallerReference'])
- output("")
+ if response['status'] >= 400:
+ error("Distribution %s could not be modified: %s" % (cfuri, response['reason']))
+ output("Distribution modified: %s" % cfuri)
+ response = cf.GetDistInfo(cfuri)
+ d = response['distribution']
+ dc = d.info['DistributionConfig']
+ pretty_output("Origin", S3UriS3.httpurl_to_s3uri(dc.info['Origin']))
+ pretty_output("DistId", d.uri())
+ pretty_output("DomainName", d.info['DomainName'])
+ pretty_output("Status", d.info['Status'])
+ pretty_output("CNAMEs", ", ".join(dc.info['CNAME']))
+ pretty_output("Comment", dc.info['Comment'])
+ pretty_output("Enabled", dc.info['Enabled'])
+ pretty_output("DefaultRootObject", dc.info['DefaultRootObject'])
+ pretty_output("Etag", response['headers']['etag'])
+
+ @staticmethod
+ def invalinfo(args):
+ cf = CloudFront(Config())
+ cfuris = Cmd._parse_args(args)
+ requests = []
+ for cfuri in cfuris:
+ if cfuri.request_id():
+ requests.append(str(cfuri))
+ else:
+ inval_list = cf.GetInvalList(cfuri)
+ try:
+ for i in inval_list['inval_list'].info['InvalidationSummary']:
+ requests.append("/".join(["cf:/", cfuri.dist_id(), i["Id"]]))
+ except:
+ continue
+ for req in requests:
+ cfuri = S3Uri(req)
+ inval_info = cf.GetInvalInfo(cfuri)
+ st = inval_info['inval_status'].info
+ pretty_output("URI", str(cfuri))
+ pretty_output("Status", st['Status'])
+ pretty_output("Created", st['CreateTime'])
+ pretty_output("Nr of paths", len(st['InvalidationBatch']['Path']))
+ pretty_output("Reference", st['InvalidationBatch']['CallerReference'])
+ output("")
+
+# vim:et:ts=4:sts=4:ai
View
362 S3/Config.py
@@ -10,190 +10,190 @@
from SortedDict import SortedDict
class Config(object):
- _instance = None
- _parsed_files = []
- _doc = {}
- access_key = ""
- secret_key = ""
- host_base = "s3.amazonaws.com"
- host_bucket = "%(bucket)s.s3.amazonaws.com"
- simpledb_host = "sdb.amazonaws.com"
- cloudfront_host = "cloudfront.amazonaws.com"
- verbosity = logging.WARNING
- progress_meter = True
- progress_class = Progress.ProgressCR
- send_chunk = 4096
- recv_chunk = 4096
- list_md5 = False
- human_readable_sizes = False
- extra_headers = SortedDict(ignore_case = True)
- force = False
- enable = None
- get_continue = False
- skip_existing = False
- recursive = False
- acl_public = None
- acl_grants = []
- acl_revokes = []
- proxy_host = ""
- proxy_port = 3128
- encrypt = False
- dry_run = False
- preserve_attrs = True
- preserve_attrs_list = [
- 'uname', # Verbose owner Name (e.g. 'root')
- 'uid', # Numeric user ID (e.g. 0)
- 'gname', # Group name (e.g. 'users')
- 'gid', # Numeric group ID (e.g. 100)
- 'atime', # Last access timestamp
- 'mtime', # Modification timestamp
- 'ctime', # Creation timestamp
- 'mode', # File mode (e.g. rwxr-xr-x = 755)
- #'acl', # Full ACL (not yet supported)
- ]
- delete_removed = False
- _doc['delete_removed'] = "[sync] Remove remote S3 objects when local file has been deleted"
- gpg_passphrase = ""
- gpg_command = ""
- gpg_encrypt = "%(gpg_command)s -c --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s"
- gpg_decrypt = "%(gpg_command)s -d --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s"