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 24 -class Container(object):
25 """ 26 Container object and Object instance factory. 27 28 If your account has the feature enabled, containers can be publically 29 shared over a global content delivery network. 30 31 @ivar name: the container's name (generally treated as read-only) 32 @type name: str 33 @ivar object_count: the number of objects in this container (cached) 34 @type object_count: number 35 @ivar size_used: the sum of the sizes of all objects in this container 36 (cached) 37 @type size_used: number 38 @ivar cdn_ttl: the time-to-live of the CDN's public cache of this container 39 (cached, use make_public to alter) 40 @type cdn_ttl: number 41 @ivar cdn_log_retention: retention of the logs in the container. 42 @type cdn_log_retention: bool 43 @ivar cdn_acl_user_agent: enable ACL restriction by User Agent 44 for this container. 45 @type cdn_acl_user_agent: str 46 @ivar cdn_acl_referrer: enable ACL restriction by Referrer 47 for this container. 48 @type cdn_acl_referrer: str 49 50 @undocumented: _fetch_cdn_data 51 @undocumented: _list_objects_raw 52 """
53 - def __set_name(self, name):
54 # slashes make for invalid names 55 if isinstance(name, (str, unicode)) and \ 56 ('/' in name or len(name) > consts.container_name_limit): 57 raise InvalidContainerName(name) 58 self._name = name
59 60 name = property(fget=lambda self: self._name, fset=__set_name, 61 doc="the name of the container (read-only)") 62
63 - def __init__(self, connection=None, name=None, count=None, size=None):
64 """ 65 Containers will rarely if ever need to be instantiated directly by the 66 user. 67 68 Instead, use the L{create_container<Connection.create_container>}, 69 L{get_container<Connection.get_container>}, 70 L{list_containers<Connection.list_containers>} and 71 other methods on a valid Connection object. 72 """ 73 self._name = None 74 self.name = name 75 self.conn = connection 76 self.object_count = count 77 self.size_used = size 78 self.cdn_uri = None 79 self.cdn_ttl = None 80 self.cdn_log_retention = None 81 self.cdn_acl_user_agent = None 82 self.cdn_acl_referrer = None 83 if connection.cdn_enabled: 84 self._fetch_cdn_data()
85 86 @requires_name(InvalidContainerName)
87 - def _fetch_cdn_data(self):
88 """ 89 Fetch the object's CDN data from the CDN service 90 """ 91 response = self.conn.cdn_request('HEAD', [self.name]) 92 if (response.status >= 200) and (response.status < 300): 93 for hdr in response.getheaders(): 94 if hdr[0].lower() == 'x-cdn-uri': 95 self.cdn_uri = hdr[1] 96 if hdr[0].lower() == 'x-ttl': 97 self.cdn_ttl = int(hdr[1]) 98 if hdr[0].lower() == 'x-log-retention': 99 self.cdn_log_retention = hdr[1] == "True" and True or False 100 if hdr[0].lower() == 'x-user-agent-acl': 101 self.cdn_acl_user_agent = hdr[1] 102 if hdr[0].lower() == 'x-referrer-acl': 103 self.cdn_acl_referrer = hdr[1]
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, \ 124 [self.name], hdrs=hdrs) 125 if (response.status < 200) or (response.status >= 300): 126 raise ResponseError(response.status, response.reason) 127 self.cdn_ttl = ttl 128 for hdr in response.getheaders(): 129 if hdr[0].lower() == 'x-cdn-uri': 130 self.cdn_uri = hdr[1]
131 132 @requires_name(InvalidContainerName)
133 - def make_private(self):
134 """ 135 Disables CDN access to this container. 136 It may continue to be available until its TTL expires. 137 138 >>> container.make_private() 139 """ 140 if not self.conn.cdn_enabled: 141 raise CDNNotEnabled() 142 hdrs = {'X-CDN-Enabled': 'False'} 143 self.cdn_uri = None 144 response = self.conn.cdn_request('POST', [self.name], hdrs=hdrs) 145 if (response.status < 200) or (response.status >= 300): 146 raise ResponseError(response.status, response.reason)
147 148 @requires_name(InvalidContainerName)
149 - def acl_user_agent(self, cdn_acl_user_agent=consts.cdn_acl_user_agent):
150 """ 151 Enable ACL restriction by User Agent for this container. 152 153 >>> container.acl_user_agent("Mozilla") 154 155 @param cdn_acl_user_agent: Set the user agent ACL 156 @type cdn_acl_user_agent: str 157 """ 158 if not self.conn.cdn_enabled: 159 raise CDNNotEnabled() 160 161 hdrs = {'X-User-Agent-ACL': cdn_acl_user_agent} 162 response = self.conn.cdn_request('POST', [self.name], hdrs=hdrs) 163 if (response.status < 200) or (response.status >= 300): 164 raise ResponseError(response.status, response.reason) 165 166 self.cdn_acl_user_agent = cdn_acl_user_agent
167 168 @requires_name(InvalidContainerName)
169 - def acl_referrer(self, cdn_acl_referrer=consts.cdn_acl_referrer):
170 """ 171 Enable ACL restriction by referrer for this container. 172 173 >>> container.acl_referrer("http://www.example.com") 174 175 @param cdn_acl_user_agent: Set the referrer ACL 176 @type cdn_acl_user_agent: str 177 """ 178 if not self.conn.cdn_enabled: 179 raise CDNNotEnabled() 180 181 hdrs = {'X-Referrer-ACL': cdn_acl_referrer} 182 response = self.conn.cdn_request('POST', [self.name], hdrs=hdrs) 183 if (response.status < 200) or (response.status >= 300): 184 raise ResponseError(response.status, response.reason) 185 186 self.cdn_acl_referrer = cdn_acl_referrer
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, delimiter=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 @param delimiter: use this character as a delimiter for subdirectories 293 @type delimiter: char 294 295 @rtype: L{ObjectResults} 296 @return: an iterable collection of all storage objects in the container 297 """ 298 return ObjectResults(self, self.list_objects_info( 299 prefix, limit, marker, path, delimiter, **parms))
300 301 @requires_name(InvalidContainerName)
302 - def get_object(self, object_name):
303 """ 304 Return an L{Object} instance for an existing storage object. 305 306 If an object with a name matching object_name does not exist 307 then a L{NoSuchObject} exception is raised. 308 309 >>> obj = container.get_object('old_object') 310 >>> obj.name 311 'old_object' 312 313 @param object_name: the name of the object to retrieve 314 @type object_name: str 315 @rtype: L{Object} 316 @return: an Object representing the storage object requested 317 """ 318 return Object(self, object_name, force_exists=True)
319 320 @requires_name(InvalidContainerName)
321 - def list_objects_info(self, prefix=None, limit=None, marker=None, 322 path=None, delimiter=None, **parms):
323 """ 324 Return information about all objects in the Container. 325 326 Keyword arguments are treated as HTTP query parameters and can 327 be used limit the result set (see the API documentation). 328 329 >>> conn['container1'].list_objects_info(limit=2) 330 [{u'bytes': 4820, 331 u'content_type': u'application/octet-stream', 332 u'hash': u'db8b55400b91ce34d800e126e37886f8', 333 u'last_modified': u'2008-11-05T00:56:00.406565', 334 u'name': u'new_object'}, 335 {u'bytes': 1896, 336 u'content_type': u'application/octet-stream', 337 u'hash': u'1b49df63db7bc97cd2a10e391e102d4b', 338 u'last_modified': u'2008-11-05T00:56:27.508729', 339 u'name': u'old_object'}] 340 341 @param prefix: filter the results using this prefix 342 @type prefix: str 343 @param limit: return the first "limit" objects found 344 @type limit: int 345 @param marker: return objects with names greater than "marker" 346 @type marker: str 347 @param path: return all objects in "path" 348 @type path: str 349 @param delimiter: use this character as a delimiter for subdirectories 350 @type delimiter: char 351 352 @rtype: list({"name":"...", "hash":..., "size":..., "type":...}) 353 @return: a list of all container info as dictionaries with the 354 keys "name", "hash", "size", and "type" 355 """ 356 parms['format'] = 'json' 357 resp = self._list_objects_raw( 358 prefix, limit, marker, path, delimiter, **parms) 359 return json_loads(resp)
360 361 @requires_name(InvalidContainerName)
362 - def list_objects(self, prefix=None, limit=None, marker=None, 363 path=None, delimiter=None, **parms):
364 """ 365 Return names of all L{Object}s in the L{Container}. 366 367 Keyword arguments are treated as HTTP query parameters and can 368 be used to limit the result set (see the API documentation). 369 370 >>> container.list_objects() 371 ['new_object', 'old_object'] 372 373 @param prefix: filter the results using this prefix 374 @type prefix: str 375 @param limit: return the first "limit" objects found 376 @type limit: int 377 @param marker: return objects with names greater than "marker" 378 @type marker: str 379 @param path: return all objects in "path" 380 @type path: str 381 @param delimiter: use this character as a delimiter for subdirectories 382 @type delimiter: char 383 384 @rtype: list(str) 385 @return: a list of all container names 386 """ 387 resp = self._list_objects_raw(prefix=prefix, limit=limit, 388 marker=marker, path=path, 389 delimiter=delimiter, **parms) 390 return resp.splitlines()
391 392 @requires_name(InvalidContainerName)
393 - def _list_objects_raw(self, prefix=None, limit=None, marker=None, 394 path=None, delimiter=None, **parms):
395 """ 396 Returns a chunk list of storage object info. 397 """ 398 if prefix: 399 parms['prefix'] = prefix 400 if limit: 401 parms['limit'] = limit 402 if marker: 403 parms['marker'] = marker 404 if delimiter: 405 parms['delimiter'] = delimiter 406 if not path is None: 407 parms['path'] = path # empty strings are valid 408 response = self.conn.make_request('GET', [self.name], parms=parms) 409 if (response.status < 200) or (response.status > 299): 410 response.read() 411 raise ResponseError(response.status, response.reason) 412 return response.read()
413
414 - def __getitem__(self, key):
415 return self.get_object(key)
416
417 - def __str__(self):
418 return self.name
419 420 @requires_name(InvalidContainerName)
421 - def delete_object(self, object_name):
422 """ 423 Permanently remove a storage object. 424 425 >>> container.list_objects() 426 ['new_object', 'old_object'] 427 >>> container.delete_object('old_object') 428 >>> container.list_objects() 429 ['new_object'] 430 431 @param object_name: the name of the object to retrieve 432 @type object_name: str 433 """ 434 if isinstance(object_name, Object): 435 object_name = object_name.name 436 if not object_name: 437 raise InvalidObjectName(object_name) 438 response = self.conn.make_request('DELETE', [self.name, object_name]) 439 if (response.status < 200) or (response.status > 299): 440 response.read() 441 raise ResponseError(response.status, response.reason) 442 response.read()
443
444 445 -class ContainerResults(object):
446 """ 447 An iterable results set object for Containers. 448 449 This class implements dictionary- and list-like interfaces. 450 """
451 - def __init__(self, conn, containers=list()):
452 self._containers = containers 453 self._names = [k['name'] for k in containers] 454 self.conn = conn
455
456 - def __getitem__(self, key):
457 return Container(self.conn, 458 self._containers[key]['name'], 459 self._containers[key]['count'], 460 self._containers[key]['bytes'])
461
462 - def __getslice__(self, i, j):
463 return [Container(self.conn, k['name'], k['count'], \ 464 k['size']) for k in self._containers[i:j]]
465
466 - def __contains__(self, item):
467 return item in self._names
468
469 - def __repr__(self):
470 return 'ContainerResults: %s containers' % len(self._containers)
471 __str__ = __repr__ 472
473 - def __len__(self):
474 return len(self._containers)
475
476 - def index(self, value, *args):
477 """ 478 returns an integer for the first index of value 479 """ 480 return self._names.index(value, *args)
481
482 - def count(self, value):
483 """ 484 returns the number of occurrences of value 485 """ 486 return self._names.count(value)
487 488 # vim:set ai sw=4 ts=4 tw=0 expandtab: 489