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