Browse code

* S3/CloudFront.py: Implemented DeleteDistribution(), GetDistConfig() and SetDistConfig(), fixed CreateDistribution(). * s3cmd: Enabled cfdelete.

git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3cmd/branches/s3cmd-airlock@345 830e0280-6d2a-0410-9c65-932aecc39d9d

Michal Ludvig authored on 2009/01/17 22:24:07
Showing 4 changed files
... ...
@@ -1,5 +1,12 @@
1 1
 2009-01-18  Michal Ludvig  <michal@logix.cz>
2 2
 
3
+	* S3/CloudFront.py: Implemented DeleteDistribution(),
4
+	  GetDistConfig() and SetDistConfig(), 
5
+	  fixed CreateDistribution().
6
+	* s3cmd: Enabled cfdelete.
7
+
8
+2009-01-18  Michal Ludvig  <michal@logix.cz>
9
+
3 10
 	* S3/CloudFront.py: Added Cmd.info() that handles both 
4 11
 	  cflist and cfinfo commands (in fact they're aliases).
5 12
 	  Made DistributionConfig() class consistent with other
... ...
@@ -124,7 +124,7 @@ class DistributionConfig(object):
124 124
 	##	<Enabled>true</Enabled>
125 125
 	## </DistributionConfig>
126 126
 
127
-	EMPTY_CONFIG = "<DistributionConfig></DistributionConfig>"
127
+	EMPTY_CONFIG = "<DistributionConfig><Origin/><CallerReference/><Enabled>true</Enabled></DistributionConfig>"
128 128
 	xmlns = "http://cloudfront.amazonaws.com/doc/2008-06-30/"
129 129
 	def __init__(self, xml = None, tree = None):
130 130
 		if not xml:
... ...
@@ -140,16 +140,22 @@ class DistributionConfig(object):
140 140
 	def parse(self, tree):
141 141
 		self.info = getDictFromTree(tree)
142 142
 		self.info['Enabled'] = (self.info['Enabled'].lower() == "true")
143
+		if not self.info.has_key("CNAME"):
144
+			self.info['CNAME'] = []
145
+		if type(self.info['CNAME']) != list:
146
+			self.info['CNAME'] = [self.info['CNAME']]
143 147
 		self.info['CNAME'] = [cname.lower() for cname in self.info['CNAME']]
148
+		if not self.info.has_key("Comment"):
149
+			self.info['Comment'] = ""
144 150
 
145 151
 	def __str__(self):
146
-		tree = getTreeFromXml(DistributionConfig.EMPTY_CONFIG)
152
+		tree = ET.Element("DistributionConfig")
147 153
 		tree.attrib['xmlns'] = DistributionConfig.xmlns
148 154
 
149 155
 		## Retain the order of the following calls!
150 156
 		appendXmlTextNode("Origin", self.info['Origin'], tree)
151 157
 		appendXmlTextNode("CallerReference", self.info['CallerReference'], tree)
152
-		if self.Comment:
158
+		if self.info['Comment']:
153 159
 			appendXmlTextNode("Comment", self.info['Comment'], tree)
154 160
 		for cname in self.info['CNAME']:
155 161
 			appendXmlTextNode("CNAME", cname.lower(), tree)
... ...
@@ -186,14 +192,14 @@ class CloudFront(object):
186 186
 		return response
187 187
 	
188 188
 	def CreateDistribution(self, uri, cnames = []):
189
-		dist_conf = DistributionConfig()
190
-		dist_conf.info['Enabled'] = True
191
-		dist_conf.info['Origin'] = uri.host_name()
192
-		dist_conf.info['CallerReference'] = str(uri)
193
-		dist_conf.info['Comment'] = uri.public_url()
189
+		dist_config = DistributionConfig()
190
+		dist_config.info['Enabled'] = True
191
+		dist_config.info['Origin'] = uri.host_name()
192
+		dist_config.info['CallerReference'] = str(uri)
193
+		dist_config.info['Comment'] = uri.public_url()
194 194
 		if cnames:
195
-			dist_conf.info['Cnames'] = cnames
196
-		request_body = str(dist_conf)
195
+			dist_config.info['Cnames'] = cnames
196
+		request_body = str(dist_config)
197 197
 		debug("CreateDistribution(): request_body: %s" % request_body)
198 198
 		response = self.send_request("CreateDist", body = request_body)
199 199
 		response['distribution'] = Distribution(response['data'])
... ...
@@ -202,9 +208,29 @@ class CloudFront(object):
202 202
 	def DeleteDistribution(self, cfuri):
203 203
 		if cfuri.type != "cf":
204 204
 			raise ValueError("Expected CFUri instead of: %s" % cfuri)
205
-		raise NotImplementedError()
206
-		# DisableDistribution
207
-		# response = self.send_request("DeleteDist", dist_id = cfuri.dist_id())
205
+		# Get current dist status (enabled/disabled) and Etag
206
+		info("Checking current status of %s" % cfuri)
207
+		response = self.GetDistConfig(cfuri)
208
+		if response['dist_config'].info['Enabled']:
209
+			info("Distribution is ENABLED. Disabling first.")
210
+			response['dist_config'].info['Enabled'] = False
211
+			response = self.SetDistConfig(cfuri, response['dist_config'], 
212
+			                              response['headers']['etag'])
213
+			warning("Waiting for Distribution to become disabled.")
214
+			warning("This may take several minutes, please wait.")
215
+			while True:
216
+				response = self.GetDistInfo(cfuri)
217
+				d = response['distribution']
218
+				if d.info['Status'] == "Deployed" and d.info['Enabled'] == False:
219
+					info("Distribution is now disabled")
220
+					break
221
+				warning("Still waiting...")
222
+				time.sleep(10)
223
+		headers = {}
224
+		headers['if-match'] = response['headers']['etag']
225
+		response = self.send_request("DeleteDist", dist_id = cfuri.dist_id(),
226
+		                             headers = headers)
227
+		return response
208 228
 	
209 229
 	def GetDistInfo(self, cfuri):
210 230
 		if cfuri.type != "cf":
... ...
@@ -213,13 +239,32 @@ class CloudFront(object):
213 213
 		response['distribution'] = Distribution(response['data'])
214 214
 		return response
215 215
 
216
+	def GetDistConfig(self, cfuri):
217
+		if cfuri.type != "cf":
218
+			raise ValueError("Expected CFUri instead of: %s" % cfuri)
219
+		response = self.send_request("GetDistConfig", dist_id = cfuri.dist_id())
220
+		response['dist_config'] = DistributionConfig(response['data'])
221
+		return response
222
+	
223
+	def SetDistConfig(self, cfuri, dist_config, etag = None):
224
+		if etag == None:
225
+			debug("SetDistConfig(): Etag not set. Fetching it first.")
226
+			etag = self.GetDistConfig(cfuri)['headers']['etag']
227
+		debug("SetDistConfig(): Etag = %s" % etag)
228
+		request_body = str(dist_config)
229
+		debug("SetDistConfig(): request_body: %s" % request_body)
230
+		headers = {}
231
+		headers['if-match'] = etag
232
+		response = self.send_request("SetDistConfig", dist_id = cfuri.dist_id(),
233
+		                             body = request_body, headers = headers)
234
+		return response
235
+
216 236
 	## --------------------------------------------------
217 237
 	## Low-level methods for handling CloudFront requests
218 238
 	## --------------------------------------------------
219 239
 
220
-	def send_request(self, op_name, dist_id = None, body = None, retries = _max_retries):
240
+	def send_request(self, op_name, dist_id = None, body = None, headers = {}, retries = _max_retries):
221 241
 		operation = self.operations[op_name]
222
-		headers = {}
223 242
 		if body:
224 243
 			headers['content-type'] = 'text/plain'
225 244
 		request = self.create_request(operation, dist_id, headers)
... ...
@@ -352,3 +397,18 @@ class Cmd(object):
352 352
 			pretty_output("DomainName", d.info['DomainName'])
353 353
 			pretty_output("Status", d.info['Status'])
354 354
 			pretty_output("Enabled", dc.info['Enabled'])
355
+	
356
+	@staticmethod
357
+	def delete(args):
358
+		cf = CloudFront(Config())
359
+		cfuris = []
360
+		for arg in args:
361
+			cfuris.append(S3Uri(arg))
362
+			if cfuris[-1].type != 'cf':
363
+				raise ParameterError("CloudFront URI required instead of: %s" % arg)
364
+		for cfuri in cfuris:
365
+			response = cf.DeleteDistribution(cfuri)
366
+			if response['status'] >= 400:
367
+				output("Distribution %s deleted" % cfuri)
368
+			else:
369
+				error("Distribution %s could not be deleted: %s" % (cfuri, response['reason']))
... ...
@@ -72,9 +72,9 @@ def getDictFromTree(tree):
72 72
 		if ret_dict.has_key(child.tag):
73 73
 			if not type(ret_dict[child.tag]) == list:
74 74
 				ret_dict[child.tag] = [ret_dict[child.tag]]
75
-			ret_dict[child.tag].append(child.text)
75
+			ret_dict[child.tag].append(child.text or "")
76 76
 		else:
77
-			ret_dict[child.tag] = child.text
77
+			ret_dict[child.tag] = child.text or ""
78 78
 	return ret_dict
79 79
 
80 80
 def getTextFromXml(xml, xpath):
... ...
@@ -1121,7 +1121,7 @@ def get_commands_list():
1121 1121
 	{"cmd":"cflist", "label":"List CloudFront distribution points", "param":"", "func":CfCmd.info, "argc":0},
1122 1122
 	{"cmd":"cfinfo", "label":"Display CloudFront distribution point parameters", "param":"[cf://DIST_ID]", "func":CfCmd.info, "argc":0},
1123 1123
 	{"cmd":"cfcreate", "label":"Create CloudFront distribution point", "param":"s3://BUCKET", "func":CfCmd.create, "argc":1},
1124
-	#{"cmd":"cfdelete", "label":"Delete CloudFront distribution point", "param":"cf://DIST_ID", "func":CfCmd.delete, "argc":1},
1124
+	{"cmd":"cfdelete", "label":"Delete CloudFront distribution point", "param":"cf://DIST_ID", "func":CfCmd.delete, "argc":1},
1125 1125
 	#{"cmd":"cfmodify", "label":"Change CloudFront distribution point parameters", "param":"cf://DIST_ID", "func":cmd_cf_modify, "argc":1},
1126 1126
 	]
1127 1127