Package cloudfiles :: Module container
[frames] | no frames]

Source Code for Module cloudfiles.container

  1  """ 
  2  container operations 
  3   
  4  Containers are storage compartments where you put your data (objects). 
  5  A container is similar to a directory or folder on a conventional filesystem 
  6  with the exception that they exist in a flat namespace, you can not create 
  7  containers inside of containers. 
  8   
  9  See COPYING for license information. 
 10  """ 
 11   
 12  from storage_object import Object, ObjectResults 
 13  from errors import ResponseError, InvalidContainerName, InvalidObjectName, \ 
 14                     ContainerNotPublic, CDNNotEnabled 
 15  from utils  import requires_name 
 16  import consts 
 17  from fjson  import json_loads 
18 19 # Because HTTPResponse objects *have* to have read() called on them 20 # before they can be used again ... 21 # pylint: disable-msg=W0612 22 23 -class Container(object):
24 """ 25 Container object and Object instance factory. 26 27 If your account has the feature enabled, containers can be publically 28 shared over a global content delivery network. 29 30 @ivar name: the container's name (generally treated as read-only) 31 @type name: str 32 @ivar object_count: the number of objects in this container (cached) 33 @type object_count: number 34 @ivar size_used: the sum of the sizes of all objects in this container 35 (cached) 36 @type size_used: number 37 @ivar cdn_ttl: the time-to-live of the CDN's public cache of this container 38 (cached, use make_public to alter) 39 @type cdn_ttl: number 40 @ivar cdn_log_retention: retention of the logs in the container. 41 @type cdn_log_retention: bool 42 43 @undocumented: _fetch_cdn_data 44 @undocumented: _list_objects_raw 45 """
46 - def __set_name(self, name):
47 # slashes make for invalid names 48 if isinstance(name, (str, unicode)) and \ 49 ('/' in name or len(name) > consts.container_name_limit): 50 raise InvalidContainerName(name) 51 self._name = name
52 53 name = property(fget=lambda self: self._name, fset=__set_name, 54 doc="the name of the container (read-only)") 55
56 - def __init__(self, connection=None, name=None, count=None, size=None):
57 """ 58 Containers will rarely if ever need to be instantiated directly by the 59 user. 60 61 Instead, use the L{create_container<Connection.create_container>}, 62 L{get_container<Connection.get_container>}, 63 L{list_containers<Connection.list_containers>} and 64 other methods on a valid Connection object. 65 """ 66 self._name = None 67 self.name = name 68 self.conn = connection 69 self.object_count = count 70 self.size_used = size 71 self.cdn_uri = None 72 self.cdn_ttl = None 73 self.cdn_log_retention = None 74 if connection.cdn_enabled: 75 self._fetch_cdn_data()
76 77 @requires_name(InvalidContainerName)
78 - def _fetch_cdn_data(self):
79 """ 80 Fetch the object's CDN data from the CDN service 81 """ 82 response = self.conn.cdn_request('HEAD', [self.name]) 83 if (response.status >= 200) and (response.status < 300): 84 for hdr in response.getheaders(): 85 if hdr[0].lower() == 'x-cdn-uri': 86 self.cdn_uri = hdr[1] 87 if hdr[0].lower() == 'x-ttl': 88 self.cdn_ttl = int(hdr[1]) 89 if hdr[0].lower() == 'x-log-retention': 90 self.cdn_log_retention = hdr[1] == "True" and True or False
91 92 @requires_name(InvalidContainerName)
93 - def make_public(self, ttl=consts.default_cdn_ttl):
94 """ 95 Either publishes the current container to the CDN or updates its 96 CDN attributes. Requires CDN be enabled on the account. 97 98 >>> container.make_public(ttl=604800) # expire in 1 week 99 100 @param ttl: cache duration in seconds of the CDN server 101 @type ttl: number 102 """ 103 if not self.conn.cdn_enabled: 104 raise CDNNotEnabled() 105 if self.cdn_uri: 106 request_method = 'POST' 107 else: 108 request_method = 'PUT' 109 hdrs = {'X-TTL': str(ttl), 'X-CDN-Enabled': 'True'} 110 response = self.conn.cdn_request(request_method, [self.name], hdrs=hdrs) 111 if (response.status < 200) or (response.status >= 300): 112 raise ResponseError(response.status, response.reason) 113 self.cdn_ttl = ttl 114 for hdr in response.getheaders(): 115 if hdr[0].lower() == 'x-cdn-uri': 116 self.cdn_uri = hdr[1]
117 118 @requires_name(InvalidContainerName)
119 - def make_private(self):
120 """ 121 Disables CDN access to this container. 122 It may continue to be available until its TTL expires. 123 124 >>> container.make_private() 125 """ 126 if not self.conn.cdn_enabled: 127 raise CDNNotEnabled() 128 hdrs = {'X-CDN-Enabled': 'False'} 129 self.cdn_uri = None 130 response = self.conn.cdn_request('POST', [self.name], hdrs=hdrs) 131 if (response.status < 200) or (response.status >= 300): 132 raise ResponseError(response.status, response.reason)
133 134 @requires_name(InvalidContainerName)
135 - def log_retention(self, log_retention=consts.cdn_log_retention):
136 """ 137 Enable CDN log retention on the container. If enabled logs will be 138 periodically (at unpredictable intervals) compressed and uploaded to 139 a ".CDN_ACCESS_LOGS" container in the form of 140 "container_name.YYYYMMDDHH-XXXX.gz". Requires CDN be enabled on the 141 account. 142 143 >>> container.log_retention(True) 144 145 @param log_retention: Enable or disable logs retention. 146 @type log_retention: bool 147 """ 148 if not self.conn.cdn_enabled: 149 raise CDNNotEnabled() 150 151 hdrs = {'X-Log-Retention': log_retention} 152 response = self.conn.cdn_request('POST', [self.name], hdrs=hdrs) 153 if (response.status < 200) or (response.status >= 300): 154 raise ResponseError(response.status, response.reason) 155 156 self.cdn_log_retention = log_retention
157
158 - def is_public(self):
159 """ 160 Returns a boolean indicating whether or not this container is 161 publically accessible via the CDN. 162 163 >>> container.is_public() 164 False 165 >>> container.make_public() 166 >>> container.is_public() 167 True 168 169 @rtype: bool 170 @return: whether or not this container is published to the CDN 171 """ 172 if not self.conn.cdn_enabled: 173 raise CDNNotEnabled() 174 return self.cdn_uri is not None
175 176 @requires_name(InvalidContainerName)
177 - def public_uri(self):
178 """ 179 Return the URI for this container, if it is publically 180 accessible via the CDN. 181 182 >>> connection['container1'].public_uri() 183 'http://c00061.cdn.cloudfiles.rackspacecloud.com' 184 185 @rtype: str 186 @return: the public URI for this container 187 """ 188 if not self.is_public(): 189 raise ContainerNotPublic() 190 return self.cdn_uri
191 192 @requires_name(InvalidContainerName)
193 - def create_object(self, object_name):
194 """ 195 Return an L{Object} instance, creating it if necessary. 196 197 When passed the name of an existing object, this method will 198 return an instance of that object, otherwise it will create a 199 new one. 200 201 >>> container.create_object('new_object') 202 <cloudfiles.storage_object.Object object at 0xb778366c> 203 >>> obj = container.create_object('new_object') 204 >>> obj.name 205 'new_object' 206 207 @type object_name: str 208 @param object_name: the name of the object to create 209 @rtype: L{Object} 210 @return: an object representing the newly created storage object 211 """ 212 return Object(self, object_name)
213 214 @requires_name(InvalidContainerName)
215 - def get_objects(self, prefix=None, limit=None, marker=None, 216 path=None, **parms):
217 """ 218 Return a result set of all Objects in the Container. 219 220 Keyword arguments are treated as HTTP query parameters and can 221 be used to limit the result set (see the API documentation). 222 223 >>> container.get_objects(limit=2) 224 ObjectResults: 2 objects 225 >>> for obj in container.get_objects(): 226 ... print obj.name 227 new_object 228 old_object 229 230 @param prefix: filter the results using this prefix 231 @type prefix: str 232 @param limit: return the first "limit" objects found 233 @type limit: int 234 @param marker: return objects whose names are greater than "marker" 235 @type marker: str 236 @param path: return all objects in "path" 237 @type path: str 238 239 @rtype: L{ObjectResults} 240 @return: an iterable collection of all storage objects in the container 241 """ 242 return ObjectResults(self, self.list_objects_info( 243 prefix, limit, marker, path, **parms))
244 245 @requires_name(InvalidContainerName)
246 - def get_object(self, object_name):
247 """ 248 Return an L{Object} instance for an existing storage object. 249 250 If an object with a name matching object_name does not exist 251 then a L{NoSuchObject} exception is raised. 252 253 >>> obj = container.get_object('old_object') 254 >>> obj.name 255 'old_object' 256 257 @param object_name: the name of the object to retrieve 258 @type object_name: str 259 @rtype: L{Object} 260 @return: an Object representing the storage object requested 261 """ 262 return Object(self, object_name, force_exists=True)
263 264 @requires_name(InvalidContainerName)
265 - def list_objects_info(self, prefix=None, limit=None, marker=None, 266 path=None, **parms):
267 """ 268 Return information about all objects in the Container. 269 270 Keyword arguments are treated as HTTP query parameters and can 271 be used limit the result set (see the API documentation). 272 273 >>> conn['container1'].list_objects_info(limit=2) 274 [{u'bytes': 4820, 275 u'content_type': u'application/octet-stream', 276 u'hash': u'db8b55400b91ce34d800e126e37886f8', 277 u'last_modified': u'2008-11-05T00:56:00.406565', 278 u'name': u'new_object'}, 279 {u'bytes': 1896, 280 u'content_type': u'application/octet-stream', 281 u'hash': u'1b49df63db7bc97cd2a10e391e102d4b', 282 u'last_modified': u'2008-11-05T00:56:27.508729', 283 u'name': u'old_object'}] 284 285 @param prefix: filter the results using this prefix 286 @type prefix: str 287 @param limit: return the first "limit" objects found 288 @type limit: int 289 @param marker: return objects with names greater than "marker" 290 @type marker: str 291 @param path: return all objects in "path" 292 @type path: str 293 294 @rtype: list({"name":"...", "hash":..., "size":..., "type":...}) 295 @return: a list of all container info as dictionaries with the 296 keys "name", "hash", "size", and "type" 297 """ 298 parms['format'] = 'json' 299 resp = self._list_objects_raw( 300 prefix, limit, marker, path, **parms) 301 return json_loads(resp)
302 303 @requires_name(InvalidContainerName)
304 - def list_objects(self, prefix=None, limit=None, marker=None, 305 path=None, **parms):
306 """ 307 Return names of all L{Object}s in the L{Container}. 308 309 Keyword arguments are treated as HTTP query parameters and can 310 be used to limit the result set (see the API documentation). 311 312 >>> container.list_objects() 313 ['new_object', 'old_object'] 314 315 @param prefix: filter the results using this prefix 316 @type prefix: str 317 @param limit: return the first "limit" objects found 318 @type limit: int 319 @param marker: return objects with names greater than "marker" 320 @type marker: str 321 @param path: return all objects in "path" 322 @type path: str 323 324 @rtype: list(str) 325 @return: a list of all container names 326 """ 327 resp = self._list_objects_raw(prefix=prefix, limit=limit, 328 marker=marker, path=path, **parms) 329 return resp.splitlines()
330 331 @requires_name(InvalidContainerName)
332 - def _list_objects_raw(self, prefix=None, limit=None, marker=None, 333 path=None, **parms):
334 """ 335 Returns a chunk list of storage object info. 336 """ 337 if prefix: parms['prefix'] = prefix 338 if limit: parms['limit'] = limit 339 if marker: parms['marker'] = marker 340 if not path is None: parms['path'] = path # empty strings are valid 341 response = self.conn.make_request('GET', [self.name], parms=parms) 342 if (response.status < 200) or (response.status > 299): 343 buff = response.read() 344 raise ResponseError(response.status, response.reason) 345 return response.read()
346
347 - def __getitem__(self, key):
348 return self.get_object(key)
349
350 - def __str__(self):
351 return self.name
352 353 @requires_name(InvalidContainerName)
354 - def delete_object(self, object_name):
355 """ 356 Permanently remove a storage object. 357 358 >>> container.list_objects() 359 ['new_object', 'old_object'] 360 >>> container.delete_object('old_object') 361 >>> container.list_objects() 362 ['new_object'] 363 364 @param object_name: the name of the object to retrieve 365 @type object_name: str 366 """ 367 if isinstance(object_name, Object): 368 object_name = object_name.name 369 if not object_name: 370 raise InvalidObjectName(object_name) 371 response = self.conn.make_request('DELETE', [self.name, object_name]) 372 if (response.status < 200) or (response.status > 299): 373 buff = response.read() 374 raise ResponseError(response.status, response.reason) 375 buff = response.read()
376
377 -class ContainerResults(object):
378 """ 379 An iterable results set object for Containers. 380 381 This class implements dictionary- and list-like interfaces. 382 """
383 - def __init__(self, conn, containers=list()):
384 self._containers = containers 385 self._names = [k['name'] for k in containers] 386 self.conn = conn
387
388 - def __getitem__(self, key):
389 return Container(self.conn, 390 self._containers[key]['name'], 391 self._containers[key]['count'], 392 self._containers[key]['bytes'])
393
394 - def __getslice__(self, i, j):
395 return [Container(self.conn, k['name'], k['count'], k['size']) for k in self._containers[i:j] ]
396
397 - def __contains__(self, item):
398 return item in self._names
399
400 - def __repr__(self):
401 return 'ContainerResults: %s containers' % len(self._containers)
402 __str__ = __repr__ 403
404 - def __len__(self):
405 return len(self._containers)
406
407 - def index(self, value, *args):
408 """ 409 returns an integer for the first index of value 410 """ 411 return self._names.index(value, *args)
412
413 - def count(self, value):
414 """ 415 returns the number of occurrences of value 416 """ 417 return self._names.count(value)
418 419 # vim:set ai sw=4 ts=4 tw=0 expandtab: 420