Browse code

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

Michal Ludvig authored on 2011/06/08 05:18:12
Showing 18 changed files
... ...
@@ -6,217 +6,219 @@
6 6
 from Utils import getTreeFromXml
7 7
 
8 8
 try:
9
-	import xml.etree.ElementTree as ET
9
+    import xml.etree.ElementTree as ET
10 10
 except ImportError:
11
-	import elementtree.ElementTree as ET
11
+    import elementtree.ElementTree as ET
12 12
 
13 13
 class Grantee(object):
14
-	ALL_USERS_URI = "http://acs.amazonaws.com/groups/global/AllUsers"
15
-	LOG_DELIVERY_URI = "http://acs.amazonaws.com/groups/s3/LogDelivery"
16
-
17
-	def __init__(self):
18
-		self.xsi_type = None
19
-		self.tag = None
20
-		self.name = None
21
-		self.display_name = None
22
-		self.permission = None
23
-
24
-	def __repr__(self):
25
-		return 'Grantee("%(tag)s", "%(name)s", "%(permission)s")' % { 
26
-			"tag" : self.tag, 
27
-			"name" : self.name, 
28
-			"permission" : self.permission 
29
-		}
30
-
31
-	def isAllUsers(self):
32
-		return self.tag == "URI" and self.name == Grantee.ALL_USERS_URI
33
-	
34
-	def isAnonRead(self):
35
-		return self.isAllUsers() and (self.permission == "READ" or self.permission == "FULL_CONTROL")
36
-	
37
-	def getElement(self):
38
-		el = ET.Element("Grant")
39
-		grantee = ET.SubElement(el, "Grantee", { 
40
-			'xmlns:xsi' : 'http://www.w3.org/2001/XMLSchema-instance',
41
-			'xsi:type' : self.xsi_type
42
-		})
43
-		name = ET.SubElement(grantee, self.tag)
44
-		name.text = self.name
45
-		permission = ET.SubElement(el, "Permission")
46
-		permission.text = self.permission
47
-		return el
14
+    ALL_USERS_URI = "http://acs.amazonaws.com/groups/global/AllUsers"
15
+    LOG_DELIVERY_URI = "http://acs.amazonaws.com/groups/s3/LogDelivery"
16
+
17
+    def __init__(self):
18
+        self.xsi_type = None
19
+        self.tag = None
20
+        self.name = None
21
+        self.display_name = None
22
+        self.permission = None
23
+
24
+    def __repr__(self):
25
+        return 'Grantee("%(tag)s", "%(name)s", "%(permission)s")' % {
26
+            "tag" : self.tag,
27
+            "name" : self.name,
28
+            "permission" : self.permission
29
+        }
30
+
31
+    def isAllUsers(self):
32
+        return self.tag == "URI" and self.name == Grantee.ALL_USERS_URI
33
+
34
+    def isAnonRead(self):
35
+        return self.isAllUsers() and (self.permission == "READ" or self.permission == "FULL_CONTROL")
36
+
37
+    def getElement(self):
38
+        el = ET.Element("Grant")
39
+        grantee = ET.SubElement(el, "Grantee", {
40
+            'xmlns:xsi' : 'http://www.w3.org/2001/XMLSchema-instance',
41
+            'xsi:type' : self.xsi_type
42
+        })
43
+        name = ET.SubElement(grantee, self.tag)
44
+        name.text = self.name
45
+        permission = ET.SubElement(el, "Permission")
46
+        permission.text = self.permission
47
+        return el
48 48
 
49 49
 class GranteeAnonRead(Grantee):
50
-	def __init__(self):
51
-		Grantee.__init__(self)
52
-		self.xsi_type = "Group"
53
-		self.tag = "URI"
54
-		self.name = Grantee.ALL_USERS_URI
55
-		self.permission = "READ"
50
+    def __init__(self):
51
+        Grantee.__init__(self)
52
+        self.xsi_type = "Group"
53
+        self.tag = "URI"
54
+        self.name = Grantee.ALL_USERS_URI
55
+        self.permission = "READ"
56 56
 
57 57
 class GranteeLogDelivery(Grantee):
58
-	def __init__(self, permission):
59
-		"""
60
-		permission must be either READ_ACP or WRITE
61
-		"""
62
-		Grantee.__init__(self)
63
-		self.xsi_type = "Group"
64
-		self.tag = "URI"
65
-		self.name = Grantee.LOG_DELIVERY_URI
66
-		self.permission = permission
58
+    def __init__(self, permission):
59
+        """
60
+        permission must be either READ_ACP or WRITE
61
+        """
62
+        Grantee.__init__(self)
63
+        self.xsi_type = "Group"
64
+        self.tag = "URI"
65
+        self.name = Grantee.LOG_DELIVERY_URI
66
+        self.permission = permission
67 67
 
68 68
 class ACL(object):
69
-	EMPTY_ACL = "<AccessControlPolicy><Owner><ID></ID></Owner><AccessControlList></AccessControlList></AccessControlPolicy>"
70
-
71
-	def __init__(self, xml = None):
72
-		if not xml:
73
-			xml = ACL.EMPTY_ACL
74
-
75
-		self.grantees = []
76
-		self.owner_id = ""
77
-		self.owner_nick = ""
78
-
79
-		tree = getTreeFromXml(xml)
80
-		self.parseOwner(tree)
81
-		self.parseGrants(tree)
82
-
83
-	def parseOwner(self, tree):
84
-		self.owner_id = tree.findtext(".//Owner//ID")
85
-		self.owner_nick = tree.findtext(".//Owner//DisplayName")
86
-
87
-	def parseGrants(self, tree):
88
-		for grant in tree.findall(".//Grant"):
89
-			grantee = Grantee()
90
-			g = grant.find(".//Grantee")
91
-			grantee.xsi_type = g.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']
92
-			grantee.permission = grant.find('Permission').text
93
-			for el in g:
94
-				if el.tag == "DisplayName":
95
-					grantee.display_name = el.text
96
-				else:
97
-					grantee.tag = el.tag
98
-					grantee.name = el.text
99
-			self.grantees.append(grantee)
100
-
101
-	def getGrantList(self):
102
-		acl = []
103
-		for grantee in self.grantees:
104
-			if grantee.display_name:
105
-				user = grantee.display_name
106
-			elif grantee.isAllUsers():
107
-				user = "*anon*"
108
-			else:
109
-				user = grantee.name
110
-			acl.append({'grantee': user, 'permission': grantee.permission})
111
-		return acl
112
-
113
-	def getOwner(self):
114
-		return { 'id' : self.owner_id, 'nick' : self.owner_nick }
115
-
116
-	def isAnonRead(self):
117
-		for grantee in self.grantees:
118
-			if grantee.isAnonRead():
119
-				return True
120
-		return False
121
-	
122
-	def grantAnonRead(self):
123
-		if not self.isAnonRead():
124
-			self.appendGrantee(GranteeAnonRead())
125
-	
126
-	def revokeAnonRead(self):
127
-		self.grantees = [g for g in self.grantees if not g.isAnonRead()]
128
-
129
-	def appendGrantee(self, grantee):
130
-		self.grantees.append(grantee)
131
-
132
-	def hasGrant(self, name, permission):
133
-		name = name.lower()
134
-		permission = permission.upper()
135
-
136
-		for grantee in self.grantees:
137
-			if grantee.name.lower() == name:
138
-				if grantee.permission == "FULL_CONTROL":
139
-					return True
140
-				elif grantee.permission.upper() == permission:
141
-					return True
142
-
143
-		return False;
144
-
145
-	def grant(self, name, permission):
146
-		if self.hasGrant(name, permission):
147
-			return
148
-
149
-		name = name.lower()
150
-		permission = permission.upper()
151
-
152
-		if "ALL" == permission:
153
-			permission = "FULL_CONTROL"
154
-
155
-		if "FULL_CONTROL" == permission:
156
-			self.revoke(name, "ALL")
157
-
158
-		grantee = Grantee()
159
-		grantee.name = name
160
-		grantee.permission = permission
161
-
162
-		if  name.find('@') <= -1: # ultra lame attempt to differenciate emails id from canonical ids
163
-			grantee.xsi_type = "CanonicalUser"
164
-			grantee.tag = "ID"
165
-		else:
166
-			grantee.xsi_type = "AmazonCustomerByEmail"
167
-			grantee.tag = "EmailAddress"
168
-				
169
-		self.appendGrantee(grantee)
170
-
171
-
172
-	def revoke(self, name, permission):
173
-		name = name.lower()
174
-		permission = permission.upper()
175
-
176
-		if "ALL" == permission:
177
-			self.grantees = [g for g in self.grantees if not g.name.lower() == name]
178
-		else:
179
-			self.grantees = [g for g in self.grantees if not (g.name.lower() == name and g.permission.upper() ==  permission)]
180
-
181
-
182
-	def __str__(self):
183
-		tree = getTreeFromXml(ACL.EMPTY_ACL)
184
-		tree.attrib['xmlns'] = "http://s3.amazonaws.com/doc/2006-03-01/"
185
-		owner = tree.find(".//Owner//ID")
186
-		owner.text = self.owner_id
187
-		acl = tree.find(".//AccessControlList")
188
-		for grantee in self.grantees:
189
-			acl.append(grantee.getElement())
190
-		return ET.tostring(tree)
69
+    EMPTY_ACL = "<AccessControlPolicy><Owner><ID></ID></Owner><AccessControlList></AccessControlList></AccessControlPolicy>"
70
+
71
+    def __init__(self, xml = None):
72
+        if not xml:
73
+            xml = ACL.EMPTY_ACL
74
+
75
+        self.grantees = []
76
+        self.owner_id = ""
77
+        self.owner_nick = ""
78
+
79
+        tree = getTreeFromXml(xml)
80
+        self.parseOwner(tree)
81
+        self.parseGrants(tree)
82
+
83
+    def parseOwner(self, tree):
84
+        self.owner_id = tree.findtext(".//Owner//ID")
85
+        self.owner_nick = tree.findtext(".//Owner//DisplayName")
86
+
87
+    def parseGrants(self, tree):
88
+        for grant in tree.findall(".//Grant"):
89
+            grantee = Grantee()
90
+            g = grant.find(".//Grantee")
91
+            grantee.xsi_type = g.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']
92
+            grantee.permission = grant.find('Permission').text
93
+            for el in g:
94
+                if el.tag == "DisplayName":
95
+                    grantee.display_name = el.text
96
+                else:
97
+                    grantee.tag = el.tag
98
+                    grantee.name = el.text
99
+            self.grantees.append(grantee)
100
+
101
+    def getGrantList(self):
102
+        acl = []
103
+        for grantee in self.grantees:
104
+            if grantee.display_name:
105
+                user = grantee.display_name
106
+            elif grantee.isAllUsers():
107
+                user = "*anon*"
108
+            else:
109
+                user = grantee.name
110
+            acl.append({'grantee': user, 'permission': grantee.permission})
111
+        return acl
112
+
113
+    def getOwner(self):
114
+        return { 'id' : self.owner_id, 'nick' : self.owner_nick }
115
+
116
+    def isAnonRead(self):
117
+        for grantee in self.grantees:
118
+            if grantee.isAnonRead():
119
+                return True
120
+        return False
121
+
122
+    def grantAnonRead(self):
123
+        if not self.isAnonRead():
124
+            self.appendGrantee(GranteeAnonRead())
125
+
126
+    def revokeAnonRead(self):
127
+        self.grantees = [g for g in self.grantees if not g.isAnonRead()]
128
+
129
+    def appendGrantee(self, grantee):
130
+        self.grantees.append(grantee)
131
+
132
+    def hasGrant(self, name, permission):
133
+        name = name.lower()
134
+        permission = permission.upper()
135
+
136
+        for grantee in self.grantees:
137
+            if grantee.name.lower() == name:
138
+                if grantee.permission == "FULL_CONTROL":
139
+                    return True
140
+                elif grantee.permission.upper() == permission:
141
+                    return True
142
+
143
+        return False;
144
+
145
+    def grant(self, name, permission):
146
+        if self.hasGrant(name, permission):
147
+            return
148
+
149
+        name = name.lower()
150
+        permission = permission.upper()
151
+
152
+        if "ALL" == permission:
153
+            permission = "FULL_CONTROL"
154
+
155
+        if "FULL_CONTROL" == permission:
156
+            self.revoke(name, "ALL")
157
+
158
+        grantee = Grantee()
159
+        grantee.name = name
160
+        grantee.permission = permission
161
+
162
+        if  name.find('@') <= -1: # ultra lame attempt to differenciate emails id from canonical ids
163
+            grantee.xsi_type = "CanonicalUser"
164
+            grantee.tag = "ID"
165
+        else:
166
+            grantee.xsi_type = "AmazonCustomerByEmail"
167
+            grantee.tag = "EmailAddress"
168
+
169
+        self.appendGrantee(grantee)
170
+
171
+
172
+    def revoke(self, name, permission):
173
+        name = name.lower()
174
+        permission = permission.upper()
175
+
176
+        if "ALL" == permission:
177
+            self.grantees = [g for g in self.grantees if not g.name.lower() == name]
178
+        else:
179
+            self.grantees = [g for g in self.grantees if not (g.name.lower() == name and g.permission.upper() ==  permission)]
180
+
181
+
182
+    def __str__(self):
183
+        tree = getTreeFromXml(ACL.EMPTY_ACL)
184
+        tree.attrib['xmlns'] = "http://s3.amazonaws.com/doc/2006-03-01/"
185
+        owner = tree.find(".//Owner//ID")
186
+        owner.text = self.owner_id
187
+        acl = tree.find(".//AccessControlList")
188
+        for grantee in self.grantees:
189
+            acl.append(grantee.getElement())
190
+        return ET.tostring(tree)
191 191
 
192 192
 if __name__ == "__main__":
193
-	xml = """<?xml version="1.0" encoding="UTF-8"?>
193
+    xml = """<?xml version="1.0" encoding="UTF-8"?>
194 194
 <AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
195 195
 <Owner>
196
-	<ID>12345678901234567890</ID>
197
-	<DisplayName>owner-nickname</DisplayName>
196
+    <ID>12345678901234567890</ID>
197
+    <DisplayName>owner-nickname</DisplayName>
198 198
 </Owner>
199 199
 <AccessControlList>
200
-	<Grant>
201
-		<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
202
-			<ID>12345678901234567890</ID>
203
-			<DisplayName>owner-nickname</DisplayName>
204
-		</Grantee>
205
-		<Permission>FULL_CONTROL</Permission>
206
-	</Grant>
207
-	<Grant>
208
-		<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
209
-			<URI>http://acs.amazonaws.com/groups/global/AllUsers</URI>
210
-		</Grantee>
211
-		<Permission>READ</Permission>
212
-	</Grant>
200
+    <Grant>
201
+        <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
202
+            <ID>12345678901234567890</ID>
203
+            <DisplayName>owner-nickname</DisplayName>
204
+        </Grantee>
205
+        <Permission>FULL_CONTROL</Permission>
206
+    </Grant>
207
+    <Grant>
208
+        <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
209
+            <URI>http://acs.amazonaws.com/groups/global/AllUsers</URI>
210
+        </Grantee>
211
+        <Permission>READ</Permission>
212
+    </Grant>
213 213
 </AccessControlList>
214 214
 </AccessControlPolicy>
215
-	"""
216
-	acl = ACL(xml)
217
-	print "Grants:", acl.getGrantList()
218
-	acl.revokeAnonRead()
219
-	print "Grants:", acl.getGrantList()
220
-	acl.grantAnonRead()
221
-	print "Grants:", acl.getGrantList()
222
-	print acl
215
+    """
216
+    acl = ACL(xml)
217
+    print "Grants:", acl.getGrantList()
218
+    acl.revokeAnonRead()
219
+    print "Grants:", acl.getGrantList()
220
+    acl.grantAnonRead()
221
+    print "Grants:", acl.getGrantList()
222
+    print acl
223
+
224
+# vim:et:ts=4:sts=4:ai
... ...
@@ -9,82 +9,84 @@ from Utils import getTreeFromXml
9 9
 from ACL import GranteeAnonRead
10 10
 
11 11
 try:
12
-	import xml.etree.ElementTree as ET
12
+    import xml.etree.ElementTree as ET
13 13
 except ImportError:
14
-	import elementtree.ElementTree as ET
14
+    import elementtree.ElementTree as ET
15 15
 
16 16
 __all__ = []
17 17
 class AccessLog(object):
18
-	LOG_DISABLED = "<BucketLoggingStatus></BucketLoggingStatus>"
19
-	LOG_TEMPLATE = "<LoggingEnabled><TargetBucket></TargetBucket><TargetPrefix></TargetPrefix></LoggingEnabled>"
18
+    LOG_DISABLED = "<BucketLoggingStatus></BucketLoggingStatus>"
19
+    LOG_TEMPLATE = "<LoggingEnabled><TargetBucket></TargetBucket><TargetPrefix></TargetPrefix></LoggingEnabled>"
20 20
 
21
-	def __init__(self, xml = None):
22
-		if not xml:
23
-			xml = self.LOG_DISABLED
24
-		self.tree = getTreeFromXml(xml)
25
-		self.tree.attrib['xmlns'] = "http://doc.s3.amazonaws.com/2006-03-01"
26
-	
27
-	def isLoggingEnabled(self):
28
-		return bool(self.tree.find(".//LoggingEnabled"))
21
+    def __init__(self, xml = None):
22
+        if not xml:
23
+            xml = self.LOG_DISABLED
24
+        self.tree = getTreeFromXml(xml)
25
+        self.tree.attrib['xmlns'] = "http://doc.s3.amazonaws.com/2006-03-01"
29 26
 
30
-	def disableLogging(self):
31
-		el = self.tree.find(".//LoggingEnabled")
32
-		if el:
33
-			self.tree.remove(el)
34
-	
35
-	def enableLogging(self, target_prefix_uri):
36
-		el = self.tree.find(".//LoggingEnabled")
37
-		if not el:
38
-			el = getTreeFromXml(self.LOG_TEMPLATE)
39
-			self.tree.append(el)
40
-		el.find(".//TargetBucket").text = target_prefix_uri.bucket()
41
-		el.find(".//TargetPrefix").text = target_prefix_uri.object()
27
+    def isLoggingEnabled(self):
28
+        return bool(self.tree.find(".//LoggingEnabled"))
42 29
 
43
-	def targetPrefix(self):
44
-		if self.isLoggingEnabled():
45
-			el = self.tree.find(".//LoggingEnabled")
46
-			target_prefix = "s3://%s/%s" % (
47
-				self.tree.find(".//LoggingEnabled//TargetBucket").text, 
48
-				self.tree.find(".//LoggingEnabled//TargetPrefix").text)
49
-			return S3Uri.S3Uri(target_prefix)
50
-		else:
51
-			return ""
30
+    def disableLogging(self):
31
+        el = self.tree.find(".//LoggingEnabled")
32
+        if el:
33
+            self.tree.remove(el)
52 34
 
53
-	def setAclPublic(self, acl_public):
54
-		le = self.tree.find(".//LoggingEnabled")
55
-		if not le:
56
-			raise ParameterError("Logging not enabled, can't set default ACL for logs")
57
-		tg = le.find(".//TargetGrants")
58
-		if not acl_public:
59
-			if not tg:
60
-				## All good, it's not been there
61
-				return
62
-			else:
63
-				le.remove(tg)
64
-		else: # acl_public == True
65
-			anon_read = GranteeAnonRead().getElement()
66
-			if not tg:
67
-				tg = ET.SubElement(le, "TargetGrants")
68
-			## What if TargetGrants already exists? We should check if 
69
-			## AnonRead is there before appending a new one. Later...
70
-			tg.append(anon_read)
35
+    def enableLogging(self, target_prefix_uri):
36
+        el = self.tree.find(".//LoggingEnabled")
37
+        if not el:
38
+            el = getTreeFromXml(self.LOG_TEMPLATE)
39
+            self.tree.append(el)
40
+        el.find(".//TargetBucket").text = target_prefix_uri.bucket()
41
+        el.find(".//TargetPrefix").text = target_prefix_uri.object()
71 42
 
72
-	def isAclPublic(self):
73
-		raise NotImplementedError()
43
+    def targetPrefix(self):
44
+        if self.isLoggingEnabled():
45
+            el = self.tree.find(".//LoggingEnabled")
46
+            target_prefix = "s3://%s/%s" % (
47
+                self.tree.find(".//LoggingEnabled//TargetBucket").text,
48
+                self.tree.find(".//LoggingEnabled//TargetPrefix").text)
49
+            return S3Uri.S3Uri(target_prefix)
50
+        else:
51
+            return ""
74 52
 
75
-	def __str__(self):
76
-		return ET.tostring(self.tree)
53
+    def setAclPublic(self, acl_public):
54
+        le = self.tree.find(".//LoggingEnabled")
55
+        if not le:
56
+            raise ParameterError("Logging not enabled, can't set default ACL for logs")
57
+        tg = le.find(".//TargetGrants")
58
+        if not acl_public:
59
+            if not tg:
60
+                ## All good, it's not been there
61
+                return
62
+            else:
63
+                le.remove(tg)
64
+        else: # acl_public == True
65
+            anon_read = GranteeAnonRead().getElement()
66
+            if not tg:
67
+                tg = ET.SubElement(le, "TargetGrants")
68
+            ## What if TargetGrants already exists? We should check if
69
+            ## AnonRead is there before appending a new one. Later...
70
+            tg.append(anon_read)
71
+
72
+    def isAclPublic(self):
73
+        raise NotImplementedError()
74
+
75
+    def __str__(self):
76
+        return ET.tostring(self.tree)
77 77
 __all__.append("AccessLog")
78 78
 
79 79
 if __name__ == "__main__":
80
-	from S3Uri import S3Uri
81
-	log = AccessLog()
82
-	print log
83
-	log.enableLogging(S3Uri("s3://targetbucket/prefix/log-"))
84
-	print log
85
-	log.setAclPublic(True)
86
-	print log
87
-	log.setAclPublic(False)
88
-	print log
89
-	log.disableLogging()
90
-	print log
80
+    from S3Uri import S3Uri
81
+    log = AccessLog()
82
+    print log
83
+    log.enableLogging(S3Uri("s3://targetbucket/prefix/log-"))
84
+    print log
85
+    log.setAclPublic(True)
86
+    print log
87
+    log.setAclPublic(False)
88
+    print log
89
+    log.disableLogging()
90
+    print log
91
+
92
+# vim:et:ts=4:sts=4:ai
... ...
@@ -4,37 +4,39 @@
4 4
 ## License: GPL Version 2
5 5
 
6 6
 class BidirMap(object):
7
-	def __init__(self, **map):
8
-		self.k2v = {}
9
-		self.v2k = {}
10
-		for key in map:
11
-			self.__setitem__(key, map[key])
12
-
13
-	def __setitem__(self, key, value):
14
-		if self.v2k.has_key(value):
15
-			if self.v2k[value] != key:
16
-				raise KeyError("Value '"+str(value)+"' already in use with key '"+str(self.v2k[value])+"'")
17
-		try:
18
-			del(self.v2k[self.k2v[key]])
19
-		except KeyError:
20
-			pass
21
-		self.k2v[key] = value
22
-		self.v2k[value] = key
23
-
24
-	def __getitem__(self, key):
25
-		return self.k2v[key]
26
-
27
-	def __str__(self):
28
-		return self.v2k.__str__()
29
-
30
-	def getkey(self, value):
31
-		return self.v2k[value]
32
-	
33
-	def getvalue(self, key):
34
-		return self.k2v[key]
35
-
36
-	def keys(self):
37
-		return [key for key in self.k2v]
38
-
39
-	def values(self):
40
-		return [value for value in self.v2k]
7
+    def __init__(self, **map):
8
+        self.k2v = {}
9
+        self.v2k = {}
10
+        for key in map:
11
+            self.__setitem__(key, map[key])
12
+
13
+    def __setitem__(self, key, value):
14
+        if self.v2k.has_key(value):
15
+            if self.v2k[value] != key:
16
+                raise KeyError("Value '"+str(value)+"' already in use with key '"+str(self.v2k[value])+"'")
17
+        try:
18
+            del(self.v2k[self.k2v[key]])
19
+        except KeyError:
20
+            pass
21
+        self.k2v[key] = value
22
+        self.v2k[value] = key
23
+
24
+    def __getitem__(self, key):
25
+        return self.k2v[key]
26
+
27
+    def __str__(self):
28
+        return self.v2k.__str__()
29
+
30
+    def getkey(self, value):
31
+        return self.v2k[value]
32
+
33
+    def getvalue(self, key):
34
+        return self.k2v[key]
35
+
36
+    def keys(self):
37
+        return [key for key in self.k2v]
38
+
39
+    def values(self):
40
+        return [value for value in self.v2k]
41
+
42
+# vim:et:ts=4:sts=4:ai
... ...
@@ -11,9 +11,9 @@ from datetime import datetime
11 11
 from logging import debug, info, warning, error
12 12
 
13 13
 try:
14
-	import xml.etree.ElementTree as ET
14
+    import xml.etree.ElementTree as ET
15 15
 except ImportError:
16
-	import elementtree.ElementTree as ET
16
+    import elementtree.ElementTree as ET
17 17
 
18 18
 from Config import Config
19 19
 from Exceptions import *
... ...
@@ -25,714 +25,716 @@ cloudfront_api_version = "2010-11-01"
25 25
 cloudfront_resource = "/%(api_ver)s/distribution" % { 'api_ver' : cloudfront_api_version }
26 26
 
27 27
 def output(message):
28
-	sys.stdout.write(message + "\n")
28
+    sys.stdout.write(message + "\n")
29 29
 
30 30
 def pretty_output(label, message):
31
-	#label = ("%s " % label).ljust(20, ".")
32
-	label = ("%s:" % label).ljust(15)
33
-	output("%s %s" % (label, message))
31
+    #label = ("%s " % label).ljust(20, ".")
32
+    label = ("%s:" % label).ljust(15)
33
+    output("%s %s" % (label, message))
34 34
 
35 35
 class DistributionSummary(object):
36
-	## Example:
37
-	##
38
-	## <DistributionSummary>
39
-	##	<Id>1234567890ABC</Id>
40
-	##	<Status>Deployed</Status>
41
-	##	<LastModifiedTime>2009-01-16T11:49:02.189Z</LastModifiedTime>
42
-	##	<DomainName>blahblahblah.cloudfront.net</DomainName>
43
-	##	<S3Origin>
44
-	##     <DNSName>example.bucket.s3.amazonaws.com</DNSName>
45
-	##  </S3Origin>
46
-	##  <CNAME>cdn.example.com</CNAME>
47
-	##  <CNAME>img.example.com</CNAME>
48
-	##  <Comment>What Ever</Comment>
49
-	##	<Enabled>true</Enabled>
50
-	## </DistributionSummary>
51
-
52
-	def __init__(self, tree):
53
-		if tree.tag != "DistributionSummary":
54
-			raise ValueError("Expected <DistributionSummary /> xml, got: <%s />" % tree.tag)
55
-		self.parse(tree)
56
-
57
-	def parse(self, tree):
58
-		self.info = getDictFromTree(tree)
59
-		self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
60
-		if self.info.has_key("CNAME") and type(self.info['CNAME']) != list:
61
-			self.info['CNAME'] = [self.info['CNAME']]
62
-
63
-	def uri(self):
64
-		return S3Uri("cf://%s" % self.info['Id'])
36
+    ## Example:
37
+    ##
38
+    ## <DistributionSummary>
39
+    ##  <Id>1234567890ABC</Id>
40
+    ##  <Status>Deployed</Status>
41
+    ##  <LastModifiedTime>2009-01-16T11:49:02.189Z</LastModifiedTime>
42
+    ##  <DomainName>blahblahblah.cloudfront.net</DomainName>
43
+    ##  <S3Origin>
44
+    ##     <DNSName>example.bucket.s3.amazonaws.com</DNSName>
45
+    ##  </S3Origin>
46
+    ##  <CNAME>cdn.example.com</CNAME>
47
+    ##  <CNAME>img.example.com</CNAME>
48
+    ##  <Comment>What Ever</Comment>
49
+    ##  <Enabled>true</Enabled>
50
+    ## </DistributionSummary>
51
+
52
+    def __init__(self, tree):
53
+        if tree.tag != "DistributionSummary":
54
+            raise ValueError("Expected <DistributionSummary /> xml, got: <%s />" % tree.tag)
55
+        self.parse(tree)
56
+
57
+    def parse(self, tree):
58
+        self.info = getDictFromTree(tree)
59
+        self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
60
+        if self.info.has_key("CNAME") and type(self.info['CNAME']) != list:
61
+            self.info['CNAME'] = [self.info['CNAME']]
62
+
63
+    def uri(self):
64
+        return S3Uri("cf://%s" % self.info['Id'])
65 65
 
66 66
 class DistributionList(object):
67
-	## Example:
68
-	## 
69
-	## <DistributionList xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
70
-	##	<Marker />
71
-	##	<MaxItems>100</MaxItems>
72
-	##	<IsTruncated>false</IsTruncated>
73
-	##	<DistributionSummary>
74
-	##	... handled by DistributionSummary() class ...
75
-	##	</DistributionSummary>
76
-	## </DistributionList>
77
-
78
-	def __init__(self, xml):
79
-		tree = getTreeFromXml(xml)
80
-		if tree.tag != "DistributionList":
81
-			raise ValueError("Expected <DistributionList /> xml, got: <%s />" % tree.tag)
82
-		self.parse(tree)
83
-
84
-	def parse(self, tree):
85
-		self.info = getDictFromTree(tree)
86
-		## Normalise some items
87
-		self.info['IsTruncated'] = (self.info['IsTruncated'].lower() == "true")
88
-
89
-		self.dist_summs = []
90
-		for dist_summ in tree.findall(".//DistributionSummary"):
91
-			self.dist_summs.append(DistributionSummary(dist_summ))
67
+    ## Example:
68
+    ##
69
+    ## <DistributionList xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
70
+    ##  <Marker />
71
+    ##  <MaxItems>100</MaxItems>
72
+    ##  <IsTruncated>false</IsTruncated>
73
+    ##  <DistributionSummary>
74
+    ##  ... handled by DistributionSummary() class ...
75
+    ##  </DistributionSummary>
76
+    ## </DistributionList>
77
+
78
+    def __init__(self, xml):
79
+        tree = getTreeFromXml(xml)
80
+        if tree.tag != "DistributionList":
81
+            raise ValueError("Expected <DistributionList /> xml, got: <%s />" % tree.tag)
82
+        self.parse(tree)
83
+
84
+    def parse(self, tree):
85
+        self.info = getDictFromTree(tree)
86
+        ## Normalise some items
87
+        self.info['IsTruncated'] = (self.info['IsTruncated'].lower() == "true")
88
+
89
+        self.dist_summs = []
90
+        for dist_summ in tree.findall(".//DistributionSummary"):
91
+            self.dist_summs.append(DistributionSummary(dist_summ))
92 92
 
93 93
 class Distribution(object):
94
-	## Example:
95
-	##
96
-	## <Distribution xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
97
-	##	<Id>1234567890ABC</Id>
98
-	##	<Status>InProgress</Status>
99
-	##	<LastModifiedTime>2009-01-16T13:07:11.319Z</LastModifiedTime>
100
-	##	<DomainName>blahblahblah.cloudfront.net</DomainName>
101
-	##	<DistributionConfig>
102
-	##	... handled by DistributionConfig() class ...
103
-	##	</DistributionConfig>
104
-	## </Distribution>
105
-
106
-	def __init__(self, xml):
107
-		tree = getTreeFromXml(xml)
108
-		if tree.tag != "Distribution":
109
-			raise ValueError("Expected <Distribution /> xml, got: <%s />" % tree.tag)
110
-		self.parse(tree)
111
-
112
-	def parse(self, tree):
113
-		self.info = getDictFromTree(tree)
114
-		## Normalise some items
115
-		self.info['LastModifiedTime'] = dateS3toPython(self.info['LastModifiedTime'])
116
-
117
-		self.info['DistributionConfig'] = DistributionConfig(tree = tree.find(".//DistributionConfig"))
118
-	
119
-	def uri(self):
120
-		return S3Uri("cf://%s" % self.info['Id'])
94
+    ## Example:
95
+    ##
96
+    ## <Distribution xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">
97
+    ##  <Id>1234567890ABC</Id>
98
+    ##  <Status>InProgress</Status>
99
+    ##  <LastModifiedTime>2009-01-16T13:07:11.319Z</LastModifiedTime>
100
+    ##  <DomainName>blahblahblah.cloudfront.net</DomainName>
101
+    ##  <DistributionConfig>
102
+    ##  ... handled by DistributionConfig() class ...
103
+    ##  </DistributionConfig>
104
+    ## </Distribution>
105
+
106
+    def __init__(self, xml):
107
+        tree = getTreeFromXml(xml)
108
+        if tree.tag != "Distribution":
109
+            raise ValueError("Expected <Distribution /> xml, got: <%s />" % tree.tag)
110
+        self.parse(tree)
111
+
112
+    def parse(self, tree):
113
+        self.info = getDictFromTree(tree)
114
+        ## Normalise some items
115
+        self.info['LastModifiedTime'] = dateS3toPython(self.info['LastModifiedTime'])
116
+
117
+        self.info['DistributionConfig'] = DistributionConfig(tree = tree.find(".//DistributionConfig"))
118
+
119
+    def uri(self):
120
+        return S3Uri("cf://%s" % self.info['Id'])
121 121
 
122 122
 class DistributionConfig(object):
123
-	## Example:
124
-	##
125
-	## <DistributionConfig>
126
-	##	<Origin>somebucket.s3.amazonaws.com</Origin>
127
-	##	<CallerReference>s3://somebucket/</CallerReference>
128
-	##	<Comment>http://somebucket.s3.amazonaws.com/</Comment>
129
-	##	<Enabled>true</Enabled>
130
-	##  <Logging>
131
-	##    <Bucket>bu.ck.et</Bucket>
132
-	##    <Prefix>/cf-somebucket/</Prefix>
133
-	##  </Logging>
134
-	## </DistributionConfig>
135
-
136
-	EMPTY_CONFIG = "<DistributionConfig><Origin/><CallerReference/><Enabled>true</Enabled></DistributionConfig>"
137
-	xmlns = "http://cloudfront.amazonaws.com/doc/%(api_ver)s/" % { 'api_ver' : cloudfront_api_version }
138
-	def __init__(self, xml = None, tree = None):
139
-		if xml is None:
140
-			xml = DistributionConfig.EMPTY_CONFIG
141
-
142
-		if tree is None:
143
-			tree = getTreeFromXml(xml)
144
-
145
-		if tree.tag != "DistributionConfig":
146
-			raise ValueError("Expected <DistributionConfig /> xml, got: <%s />" % tree.tag)
147
-		self.parse(tree)
148
-
149
-	def parse(self, tree):
150
-		self.info = getDictFromTree(tree)
151
-		self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
152
-		if not self.info.has_key("CNAME"):
153
-			self.info['CNAME'] = []
154
-		if type(self.info['CNAME']) != list:
155
-			self.info['CNAME'] = [self.info['CNAME']]
156
-		self.info['CNAME'] = [cname.lower() for cname in self.info['CNAME']]
157
-		if not self.info.has_key("Comment"):
158
-			self.info['Comment'] = ""
159
-		if not self.info.has_key("DefaultRootObject"):
160
-			self.info['DefaultRootObject'] = ""
161
-		## Figure out logging - complex node not parsed by getDictFromTree()
162
-		logging_nodes = tree.findall(".//Logging")
163
-		if logging_nodes:
164
-			logging_dict = getDictFromTree(logging_nodes[0])
165
-			logging_dict['Bucket'], success = getBucketFromHostname(logging_dict['Bucket'])
166
-			if not success:
167
-				warning("Logging to unparsable bucket name: %s" % logging_dict['Bucket'])
168
-			self.info['Logging'] = S3UriS3("s3://%(Bucket)s/%(Prefix)s" % logging_dict)
169
-		else:
170
-			self.info['Logging'] = None
171
-
172
-	def __str__(self):
173
-		tree = ET.Element("DistributionConfig")
174
-		tree.attrib['xmlns'] = DistributionConfig.xmlns
175
-
176
-		## Retain the order of the following calls!
177
-		appendXmlTextNode("Origin", self.info['Origin'], tree)
178
-		appendXmlTextNode("CallerReference", self.info['CallerReference'], tree)
179
-		for cname in self.info['CNAME']:
180
-			appendXmlTextNode("CNAME", cname.lower(), tree)
181
-		if self.info['Comment']:
182
-			appendXmlTextNode("Comment", self.info['Comment'], tree)
183
-		appendXmlTextNode("Enabled", str(self.info['Enabled']).lower(), tree)
184
-		# don't create a empty DefaultRootObject element as it would result in a MalformedXML error
185
-		if str(self.info['DefaultRootObject']):
186
-			appendXmlTextNode("DefaultRootObject", str(self.info['DefaultRootObject']), tree)
187
-		if self.info['Logging']:
188
-			logging_el = ET.Element("Logging")
189
-			appendXmlTextNode("Bucket", getHostnameFromBucket(self.info['Logging'].bucket()), logging_el)
190
-			appendXmlTextNode("Prefix", self.info['Logging'].object(), logging_el)
191
-			tree.append(logging_el)
192
-		return ET.tostring(tree)
123
+    ## Example:
124
+    ##
125
+    ## <DistributionConfig>
126
+    ##  <Origin>somebucket.s3.amazonaws.com</Origin>
127
+    ##  <CallerReference>s3://somebucket/</CallerReference>
128
+    ##  <Comment>http://somebucket.s3.amazonaws.com/</Comment>
129
+    ##  <Enabled>true</Enabled>
130
+    ##  <Logging>
131
+    ##    <Bucket>bu.ck.et</Bucket>
132
+    ##    <Prefix>/cf-somebucket/</Prefix>
133
+    ##  </Logging>
134
+    ## </DistributionConfig>
135
+
136
+    EMPTY_CONFIG = "<DistributionConfig><Origin/><CallerReference/><Enabled>true</Enabled></DistributionConfig>"
137
+    xmlns = "http://cloudfront.amazonaws.com/doc/%(api_ver)s/" % { 'api_ver' : cloudfront_api_version }
138
+    def __init__(self, xml = None, tree = None):
139
+        if xml is None:
140
+            xml = DistributionConfig.EMPTY_CONFIG
141
+
142
+        if tree is None:
143
+            tree = getTreeFromXml(xml)
144
+
145
+        if tree.tag != "DistributionConfig":
146
+            raise ValueError("Expected <DistributionConfig /> xml, got: <%s />" % tree.tag)
147
+        self.parse(tree)
148
+
149
+    def parse(self, tree):
150
+        self.info = getDictFromTree(tree)
151
+        self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
152
+        if not self.info.has_key("CNAME"):
153
+            self.info['CNAME'] = []
154
+        if type(self.info['CNAME']) != list:
155
+            self.info['CNAME'] = [self.info['CNAME']]
156
+        self.info['CNAME'] = [cname.lower() for cname in self.info['CNAME']]
157
+        if not self.info.has_key("Comment"):
158
+            self.info['Comment'] = ""
159
+        if not self.info.has_key("DefaultRootObject"):
160
+            self.info['DefaultRootObject'] = ""
161
+        ## Figure out logging - complex node not parsed by getDictFromTree()
162
+        logging_nodes = tree.findall(".//Logging")
163
+        if logging_nodes:
164
+            logging_dict = getDictFromTree(logging_nodes[0])
165
+            logging_dict['Bucket'], success = getBucketFromHostname(logging_dict['Bucket'])
166
+            if not success:
167
+                warning("Logging to unparsable bucket name: %s" % logging_dict['Bucket'])
168
+            self.info['Logging'] = S3UriS3("s3://%(Bucket)s/%(Prefix)s" % logging_dict)
169
+        else:
170
+            self.info['Logging'] = None
171
+
172
+    def __str__(self):
173
+        tree = ET.Element("DistributionConfig")
174
+        tree.attrib['xmlns'] = DistributionConfig.xmlns
175
+
176
+        ## Retain the order of the following calls!
177
+        appendXmlTextNode("Origin", self.info['Origin'], tree)
178
+        appendXmlTextNode("CallerReference", self.info['CallerReference'], tree)
179
+        for cname in self.info['CNAME']:
180
+            appendXmlTextNode("CNAME", cname.lower(), tree)
181
+        if self.info['Comment']:
182
+            appendXmlTextNode("Comment", self.info['Comment'], tree)
183
+        appendXmlTextNode("Enabled", str(self.info['Enabled']).lower(), tree)
184
+        # don't create a empty DefaultRootObject element as it would result in a MalformedXML error
185
+        if str(self.info['DefaultRootObject']):
186
+            appendXmlTextNode("DefaultRootObject", str(self.info['DefaultRootObject']), tree)
187
+        if self.info['Logging']:
188
+            logging_el = ET.Element("Logging")
189
+            appendXmlTextNode("Bucket", getHostnameFromBucket(self.info['Logging'].bucket()), logging_el)
190
+            appendXmlTextNode("Prefix", self.info['Logging'].object(), logging_el)
191
+            tree.append(logging_el)
192
+        return ET.tostring(tree)
193 193
 
194 194
 class Invalidation(object):
195
-	## Example:
196
-	##
197
-	## <Invalidation xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/">
198
-	##   <Id>id</Id>
199
-	##   <Status>status</Status>
200
-	##   <CreateTime>date</CreateTime>
201
-	##   <InvalidationBatch>
202
-	##       <Path>/image1.jpg</Path>
203
-	##       <Path>/image2.jpg</Path>
204
-	##       <Path>/videos/movie.flv</Path>
205
-	##       <CallerReference>my-batch</CallerReference>
206
-	##   </InvalidationBatch>
207
-	## </Invalidation>
208
-
209
-	def __init__(self, xml):
210
-		tree = getTreeFromXml(xml)
211
-		if tree.tag != "Invalidation":
212
-			raise ValueError("Expected <Invalidation /> xml, got: <%s />" % tree.tag)
213
-		self.parse(tree)
214
-
215
-	def parse(self, tree):
216
-		self.info = getDictFromTree(tree)
217
-
218
-	def __str__(self):
219
-		return str(self.info)
195
+    ## Example:
196
+    ##
197
+    ## <Invalidation xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/">
198
+    ##   <Id>id</Id>
199
+    ##   <Status>status</Status>
200
+    ##   <CreateTime>date</CreateTime>
201
+    ##   <InvalidationBatch>
202
+    ##       <Path>/image1.jpg</Path>
203
+    ##       <Path>/image2.jpg</Path>
204
+    ##       <Path>/videos/movie.flv</Path>
205
+    ##       <CallerReference>my-batch</CallerReference>
206
+    ##   </InvalidationBatch>
207
+    ## </Invalidation>
208
+
209
+    def __init__(self, xml):
210
+        tree = getTreeFromXml(xml)
211
+        if tree.tag != "Invalidation":
212
+            raise ValueError("Expected <Invalidation /> xml, got: <%s />" % tree.tag)
213
+        self.parse(tree)
214
+
215
+    def parse(self, tree):
216
+        self.info = getDictFromTree(tree)
217
+
218
+    def __str__(self):
219
+        return str(self.info)
220 220
 
221 221
 class InvalidationList(object):
222
-	## Example:
223
-	##
224
-	## <InvalidationList>
225
-	##   <Marker/>
226
-	##   <NextMarker>Invalidation ID</NextMarker>
227
-	##   <MaxItems>2</MaxItems>
228
-	##   <IsTruncated>true</IsTruncated>
229
-	##   <InvalidationSummary>
230
-	##     <Id>[Second Invalidation ID]</Id>
231
-	##     <Status>Completed</Status>
232
-	##   </InvalidationSummary>
233
-	##   <InvalidationSummary>
234
-	##     <Id>[First Invalidation ID]</Id>
235
-	##     <Status>Completed</Status>
236
-	##   </InvalidationSummary>
237
-	## </InvalidationList>
238
-
239
-	def __init__(self, xml):
240
-		tree = getTreeFromXml(xml)
241
-		if tree.tag != "InvalidationList":
242
-			raise ValueError("Expected <InvalidationList /> xml, got: <%s />" % tree.tag)
243
-		self.parse(tree)
244
-
245
-	def parse(self, tree):
246
-		self.info = getDictFromTree(tree)
247
-
248
-	def __str__(self):
249
-		return str(self.info)
222
+    ## Example:
223
+    ##
224
+    ## <InvalidationList>
225
+    ##   <Marker/>
226
+    ##   <NextMarker>Invalidation ID</NextMarker>
227
+    ##   <MaxItems>2</MaxItems>
228
+    ##   <IsTruncated>true</IsTruncated>
229
+    ##   <InvalidationSummary>
230
+    ##     <Id>[Second Invalidation ID]</Id>
231
+    ##     <Status>Completed</Status>
232
+    ##   </InvalidationSummary>
233
+    ##   <InvalidationSummary>
234
+    ##     <Id>[First Invalidation ID]</Id>
235
+    ##     <Status>Completed</Status>
236
+    ##   </InvalidationSummary>
237
+    ## </InvalidationList>
238
+
239
+    def __init__(self, xml):
240
+        tree = getTreeFromXml(xml)
241
+        if tree.tag != "InvalidationList":
242
+            raise ValueError("Expected <InvalidationList /> xml, got: <%s />" % tree.tag)
243
+        self.parse(tree)
244
+
245
+    def parse(self, tree):
246
+        self.info = getDictFromTree(tree)
247
+
248
+    def __str__(self):
249
+        return str(self.info)
250 250
 
251 251
 class InvalidationBatch(object):
252
-	## Example:
253
-	##
254
-	## <InvalidationBatch>
255
-	##   <Path>/image1.jpg</Path>
256
-	##   <Path>/image2.jpg</Path>
257
-	##   <Path>/videos/movie.flv</Path>
258
-	##   <Path>/sound%20track.mp3</Path>
259
-	##   <CallerReference>my-batch</CallerReference>
260
-	## </InvalidationBatch>
261
-
262
-	def __init__(self, reference = None, distribution = None, paths = []):
263
-		if reference:
264
-			self.reference = reference
265
-		else:
266
-			if not distribution:
267
-				distribution="0"
268
-			self.reference = "%s.%s.%s" % (distribution,
269
-				datetime.strftime(datetime.now(),"%Y%m%d%H%M%S"),
270
-				random.randint(1000,9999))
271
-		self.paths = []
272
-		self.add_objects(paths)
273
-
274
-	def add_objects(self, paths):
275
-		self.paths.extend(paths)
276
-
277
-	def get_reference(self):
278
-		return self.reference
279
-
280
-	def __str__(self):
281
-		tree = ET.Element("InvalidationBatch")
282
-
283
-		for path in self.paths:
284
-			if path[0] != "/":
285
-				path = "/" + path
286
-			appendXmlTextNode("Path", path, tree)
287
-		appendXmlTextNode("CallerReference", self.reference, tree)
288
-		return ET.tostring(tree)
252
+    ## Example:
253
+    ##
254
+    ## <InvalidationBatch>
255
+    ##   <Path>/image1.jpg</Path>
256
+    ##   <Path>/image2.jpg</Path>
257
+    ##   <Path>/videos/movie.flv</Path>
258
+    ##   <Path>/sound%20track.mp3</Path>
259
+    ##   <CallerReference>my-batch</CallerReference>
260
+    ## </InvalidationBatch>
261
+
262
+    def __init__(self, reference = None, distribution = None, paths = []):
263
+        if reference:
264
+            self.reference = reference
265
+        else:
266
+            if not distribution:
267
+                distribution="0"
268
+            self.reference = "%s.%s.%s" % (distribution,
269
+                datetime.strftime(datetime.now(),"%Y%m%d%H%M%S"),
270
+                random.randint(1000,9999))
271
+        self.paths = []
272
+        self.add_objects(paths)
273
+
274
+    def add_objects(self, paths):
275
+        self.paths.extend(paths)
276
+
277
+    def get_reference(self):
278
+        return self.reference
279
+
280
+    def __str__(self):
281
+        tree = ET.Element("InvalidationBatch")
282
+
283
+        for path in self.paths:
284
+            if path[0] != "/":
285
+                path = "/" + path
286
+            appendXmlTextNode("Path", path, tree)
287
+        appendXmlTextNode("CallerReference", self.reference, tree)
288
+        return ET.tostring(tree)
289 289
 
290 290
 class CloudFront(object):
291
-	operations = {
292
-		"CreateDist" : { 'method' : "POST", 'resource' : "" },
293
-		"DeleteDist" : { 'method' : "DELETE", 'resource' : "/%(dist_id)s" },
294
-		"GetList" : { 'method' : "GET", 'resource' : "" },
295
-		"GetDistInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s" },
296
-		"GetDistConfig" : { 'method' : "GET", 'resource' : "/%(dist_id)s/config" },
297
-		"SetDistConfig" : { 'method' : "PUT", 'resource' : "/%(dist_id)s/config" },
298
-		"Invalidate" : { 'method' : "POST", 'resource' : "/%(dist_id)s/invalidation" },
299
-		"GetInvalList" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation" },
300
-		"GetInvalInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation/%(request_id)s" },
301
-	}
302
-
303
-	## Maximum attempts of re-issuing failed requests
304
-	_max_retries = 5
305
-	dist_list = None
306
-
307
-	def __init__(self, config):
308
-		self.config = config
309
-
310
-	## --------------------------------------------------
311
-	## Methods implementing CloudFront API
312
-	## --------------------------------------------------
313
-
314
-	def GetList(self):
315
-		response = self.send_request("GetList")
316
-		response['dist_list'] = DistributionList(response['data'])
317
-		if response['dist_list'].info['IsTruncated']:
318
-			raise NotImplementedError("List is truncated. Ask s3cmd author to add support.")
319
-		## TODO: handle Truncated 
320
-		return response
321
-	
322
-	def CreateDistribution(self, uri, cnames_add = [], comment = None, logging = None, default_root_object = None):
323
-		dist_config = DistributionConfig()
324
-		dist_config.info['Enabled'] = True
325
-		dist_config.info['Origin'] = uri.host_name()
326
-		dist_config.info['CallerReference'] = str(uri)
327
-		dist_config.info['DefaultRootObject'] = default_root_object
328
-		if comment == None:
329
-			dist_config.info['Comment'] = uri.public_url()
330
-		else:
331
-			dist_config.info['Comment'] = comment
332
-		for cname in cnames_add:
333
-			if dist_config.info['CNAME'].count(cname) == 0:
334
-				dist_config.info['CNAME'].append(cname)
335
-		if logging:
336
-			dist_config.info['Logging'] = S3UriS3(logging)
337
-		request_body = str(dist_config)
338
-		debug("CreateDistribution(): request_body: %s" % request_body)
339
-		response = self.send_request("CreateDist", body = request_body)
340
-		response['distribution'] = Distribution(response['data'])
341
-		return response
342
-	
343
-	def ModifyDistribution(self, cfuri, cnames_add = [], cnames_remove = [],
344
-	                       comment = None, enabled = None, logging = None,
291
+    operations = {
292
+        "CreateDist" : { 'method' : "POST", 'resource' : "" },
293
+        "DeleteDist" : { 'method' : "DELETE", 'resource' : "/%(dist_id)s" },
294
+        "GetList" : { 'method' : "GET", 'resource' : "" },
295
+        "GetDistInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s" },
296
+        "GetDistConfig" : { 'method' : "GET", 'resource' : "/%(dist_id)s/config" },
297
+        "SetDistConfig" : { 'method' : "PUT", 'resource' : "/%(dist_id)s/config" },
298
+        "Invalidate" : { 'method' : "POST", 'resource' : "/%(dist_id)s/invalidation" },
299
+        "GetInvalList" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation" },
300
+        "GetInvalInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation/%(request_id)s" },
301
+    }
302
+
303
+    ## Maximum attempts of re-issuing failed requests
304
+    _max_retries = 5
305
+    dist_list = None
306
+
307
+    def __init__(self, config):
308
+        self.config = config
309
+
310
+    ## --------------------------------------------------
311
+    ## Methods implementing CloudFront API
312
+    ## --------------------------------------------------
313
+
314
+    def GetList(self):
315
+        response = self.send_request("GetList")
316
+        response['dist_list'] = DistributionList(response['data'])
317
+        if response['dist_list'].info['IsTruncated']:
318
+            raise NotImplementedError("List is truncated. Ask s3cmd author to add support.")
319
+        ## TODO: handle Truncated
320
+        return response
321
+
322
+    def CreateDistribution(self, uri, cnames_add = [], comment = None, logging = None, default_root_object = None):
323
+        dist_config = DistributionConfig()
324
+        dist_config.info['Enabled'] = True
325
+        dist_config.info['Origin'] = uri.host_name()
326
+        dist_config.info['CallerReference'] = str(uri)
327
+        dist_config.info['DefaultRootObject'] = default_root_object
328
+        if comment == None:
329
+            dist_config.info['Comment'] = uri.public_url()
330
+        else:
331
+            dist_config.info['Comment'] = comment
332
+        for cname in cnames_add:
333
+            if dist_config.info['CNAME'].count(cname) == 0:
334
+                dist_config.info['CNAME'].append(cname)
335
+        if logging:
336
+            dist_config.info['Logging'] = S3UriS3(logging)
337
+        request_body = str(dist_config)
338
+        debug("CreateDistribution(): request_body: %s" % request_body)
339
+        response = self.send_request("CreateDist", body = request_body)
340
+        response['distribution'] = Distribution(response['data'])
341
+        return response
342
+
343
+    def ModifyDistribution(self, cfuri, cnames_add = [], cnames_remove = [],
344
+                           comment = None, enabled = None, logging = None,
345 345
                            default_root_object = None):
346
-		if cfuri.type != "cf":
347
-			raise ValueError("Expected CFUri instead of: %s" % cfuri)
348
-		# Get current dist status (enabled/disabled) and Etag
349
-		info("Checking current status of %s" % cfuri)
350
-		response = self.GetDistConfig(cfuri)
351
-		dc = response['dist_config']
352
-		if enabled != None:
353
-			dc.info['Enabled'] = enabled
354
-		if comment != None:
355
-			dc.info['Comment'] = comment
356
-		if default_root_object != None:
357
-			dc.info['DefaultRootObject'] = default_root_object
358
-		for cname in cnames_add:
359
-			if dc.info['CNAME'].count(cname) == 0:
360
-				dc.info['CNAME'].append(cname)
361
-		for cname in cnames_remove:
362
-			while dc.info['CNAME'].count(cname) > 0:
363
-				dc.info['CNAME'].remove(cname)
364
-		if logging != None:
365
-			if logging == False:
366
-				dc.info['Logging'] = False
367
-			else:
368
-				dc.info['Logging'] = S3UriS3(logging)
369
-		response = self.SetDistConfig(cfuri, dc, response['headers']['etag'])
370
-		return response
371
-		
372
-	def DeleteDistribution(self, cfuri):
373
-		if cfuri.type != "cf":
374
-			raise ValueError("Expected CFUri instead of: %s" % cfuri)
375
-		# Get current dist status (enabled/disabled) and Etag
376
-		info("Checking current status of %s" % cfuri)
377
-		response = self.GetDistConfig(cfuri)
378
-		if response['dist_config'].info['Enabled']:
379
-			info("Distribution is ENABLED. Disabling first.")
380
-			response['dist_config'].info['Enabled'] = False
381
-			response = self.SetDistConfig(cfuri, response['dist_config'], 
382
-			                              response['headers']['etag'])
383
-			warning("Waiting for Distribution to become disabled.")
384
-			warning("This may take several minutes, please wait.")
385
-			while True:
386
-				response = self.GetDistInfo(cfuri)
387
-				d = response['distribution']
388
-				if d.info['Status'] == "Deployed" and d.info['Enabled'] == False:
389
-					info("Distribution is now disabled")
390
-					break
391
-				warning("Still waiting...")
392
-				time.sleep(10)
393
-		headers = {}
394
-		headers['if-match'] = response['headers']['etag']
395
-		response = self.send_request("DeleteDist", dist_id = cfuri.dist_id(),
396
-		                             headers = headers)
397
-		return response
398
-	
399
-	def GetDistInfo(self, cfuri):
400
-		if cfuri.type != "cf":
401
-			raise ValueError("Expected CFUri instead of: %s" % cfuri)
402
-		response = self.send_request("GetDistInfo", dist_id = cfuri.dist_id())
403
-		response['distribution'] = Distribution(response['data'])
404
-		return response
405
-
406
-	def GetDistConfig(self, cfuri):
407
-		if cfuri.type != "cf":
408
-			raise ValueError("Expected CFUri instead of: %s" % cfuri)
409
-		response = self.send_request("GetDistConfig", dist_id = cfuri.dist_id())
410
-		response['dist_config'] = DistributionConfig(response['data'])
411
-		return response
412
-	
413
-	def SetDistConfig(self, cfuri, dist_config, etag = None):
414
-		if etag == None:
415
-			debug("SetDistConfig(): Etag not set. Fetching it first.")
416
-			etag = self.GetDistConfig(cfuri)['headers']['etag']
417
-		debug("SetDistConfig(): Etag = %s" % etag)
418
-		request_body = str(dist_config)
419
-		debug("SetDistConfig(): request_body: %s" % request_body)
420
-		headers = {}
421
-		headers['if-match'] = etag
422
-		response = self.send_request("SetDistConfig", dist_id = cfuri.dist_id(),
423
-		                             body = request_body, headers = headers)
424
-		return response
425
-
426
-	def InvalidateObjects(self, uri, paths):
427
-		# uri could be either cf:// or s3:// uri
428
-		cfuri = self.get_dist_name_for_bucket(uri)
429
-		if len(paths) > 999:
430
-			try:
431
-				tmp_filename = Utils.mktmpfile()
432
-				f = open(tmp_filename, "w")
433
-				f.write("\n".join(paths)+"\n")
434
-				f.close()
435
-				warning("Request to invalidate %d paths (max 999 supported)" % len(paths))
436
-				warning("All the paths are now saved in: %s" % tmp_filename)
437
-			except:
438
-				pass
439
-			raise ParameterError("Too many paths to invalidate")
440
-		invalbatch = InvalidationBatch(distribution = cfuri.dist_id(), paths = paths)
441
-		debug("InvalidateObjects(): request_body: %s" % invalbatch)
442
-		response = self.send_request("Invalidate", dist_id = cfuri.dist_id(),
443
-		                             body = str(invalbatch))
444
-		response['dist_id'] = cfuri.dist_id()
445
-		if response['status'] == 201:
446
-			inval_info = Invalidation(response['data']).info
447
-			response['request_id'] = inval_info['Id']
448
-		debug("InvalidateObjects(): response: %s" % response)
449
-		return response
450
-
451
-	def GetInvalList(self, cfuri):
452
-		if cfuri.type != "cf":
453
-			raise ValueError("Expected CFUri instead of: %s" % cfuri)
454
-		response = self.send_request("GetInvalList", dist_id = cfuri.dist_id())
455
-		response['inval_list'] = InvalidationList(response['data'])
456
-		return response
457
-
458
-	def GetInvalInfo(self, cfuri):
459
-		if cfuri.type != "cf":
460
-			raise ValueError("Expected CFUri instead of: %s" % cfuri)
461
-		if cfuri.request_id() is None:
462
-			raise ValueError("Expected CFUri with Request ID")
463
-		response = self.send_request("GetInvalInfo", dist_id = cfuri.dist_id(), request_id = cfuri.request_id())
464
-		response['inval_status'] = Invalidation(response['data'])
465
-		return response
466