Merge remote branch 'upstream/master' into dev/mount_storage
authorElrond <elrond+mediagoblin.org@samba-tng.org>
Mon, 8 Aug 2011 18:00:57 +0000 (20:00 +0200)
committerElrond <elrond+mediagoblin.org@samba-tng.org>
Mon, 8 Aug 2011 18:00:57 +0000 (20:00 +0200)
Conflicts:
mediagoblin/storage.py

1  2 
mediagoblin/storage.py

index f9d2c3eab9c4c6ff1027e456766bf38f8537bd33,e449eda3b9995f34724bd51e6d30970011064a94..25598c82a1f111ec9c9da18712281966ee48aa4a
@@@ -216,114 -227,65 +227,173 @@@ class BasicFileStorage(StorageInterface
          return self._resolve_filepath(filepath)
  
  
+ class CloudFilesStorage(StorageInterface):
+     def __init__(self, **kwargs):
+         self.param_container = kwargs.get('cloudfiles_container')
+         self.param_user = kwargs.get('cloudfiles_user')
+         self.param_api_key = kwargs.get('cloudfiles_api_key')
+         self.param_host = kwargs.get('cloudfiles_host')
+         self.param_use_servicenet = kwargs.get('cloudfiles_use_servicenet')
+         if not self.param_host:
+             print('No CloudFiles host URL specified, '
+                   'defaulting to Rackspace US')
+         self.connection = cloudfiles.get_connection(
+             username=self.param_user,
+             api_key=self.param_api_key,
+             servicenet=True if self.param_use_servicenet == 'true' or \
+                 self.param_use_servicenet == True else False)
+         if not self.param_container == \
+                 self.connection.get_container(self.param_container):
+             self.container = self.connection.create_container(
+                 self.param_container)
+             self.container.make_public(
+                 ttl=60 * 60 * 2)
+         else:
+             self.container = self.connection.get_container(
+                 self.param_container)
+     def _resolve_filepath(self, filepath):
+         return '/'.join(
+             clean_listy_filepath(filepath))
+     def file_exists(self, filepath):
+         try:
+             object = self.container.get_object(
+                 self._resolve_filepath(filepath))
+             return True
+         except cloudfiles.errors.NoSuchObject:
+             return False
+     def get_file(self, filepath, mode='r'):
+         try:
+             obj = self.container.get_object(
+                 self._resolve_filepath(filepath))
+         except cloudfiles.errors.NoSuchObject:
+             obj = self.container.create_object(
+                 self._resolve_filepath(filepath))
+         return obj
+     def delete_file(self, filepath):
+         # TODO: Also delete unused directories if empty (safely, with
+         # checks to avoid race conditions).
+         self.container.delete_object(filepath)
+     def file_url(self, filepath):
+         return self.get_file(filepath).public_uri()
 +class MountStorage(StorageInterface):
 +    def __init__(self, **kwargs):
 +        self.mounttab = {}
 +
 +    def mount(self, dirpath, backend):
 +        """
 +        Mount a new backend under dirpath
 +        """
 +        new_ent = clean_listy_filepath(dirpath)
 +
 +        print "Mounting:", repr(new_ent)
 +        already, rem_1, table, rem_2 = self._resolve_to_backend(new_ent, True)
 +        print "===", repr(already), repr(rem_1), repr(rem_2)
 +
 +        assert (already is None) or (len(rem_2) > 0), "Already mounted"
 +        for part in rem_2:
 +            table[part] = {}
 +            table = table[part]
 +        assert not table.has_key(None), "Huh? Already mounted?!"
 +        table[None] = backend
 +
 +    def _resolve_to_backend(self, filepath, extra_info = False):
 +        """
 +        extra_info = True is for internal use!
 +
 +        Normally, returns the backend and the filepath inside that backend.
 +
 +        With extra_info = True it returns the last directory node and the
 +        remaining filepath from there in addition.
 +        """
 +        table = self.mounttab
 +        filepath = filepath[:]
 +        res_fp = None
 +        while True:
 +            new_be = table.get(None)
 +            if (new_be is not None) or res_fp is None:
 +                res_be = new_be
 +                res_fp = filepath[:]
 +                res_extra = (table, filepath[:])
 +                # print "... New res: %r, %r, %r" % (res_be, res_fp, res_extra)
 +            if len(filepath) == 0:
 +                break
 +            query = filepath.pop(0)
 +            entry = table.get(query)
 +            if entry is not None:
 +                table = entry
 +                res_extra = (table, filepath[:])
 +            else:
 +                break
 +        if extra_info:
 +            return (res_be, res_fp) + res_extra
 +        else:
 +            return (res_be, res_fp)
 +
 +    def resolve_to_backend(self, filepath):
 +        backend, filepath = self._resolve_to_backend(filepath)
 +        if backend is None:
 +            raise Error("Path not mounted")
 +        return backend, filepath
 +
 +    def __repr__(self, table = None, indent = []):
 +        res = []
 +        if table is None:
 +            res.append("MountStorage<")
 +            table = self.mounttab
 +        v = table.get(None)
 +        if v:
 +            res.append("  " * len(indent) + repr(indent) + ": " + repr(v))
 +        for k, v in table.iteritems():
 +            if k == None:
 +                continue
 +            res.append("  " * len(indent) + repr(k) + ":")
 +            res += self.__repr__(v, indent + [k])
 +        if table is self.mounttab:
 +            res.append(">")
 +            return "\n".join(res)
 +        else:
 +            return res
 +
 +    def file_exists(self, filepath):
 +        backend, filepath = self.resolve_to_backend(filepath)
 +        return backend.file_exists(filepath)
 +
 +    def get_file(self, filepath, mode='r'):
 +        backend, filepath = self.resolve_to_backend(filepath)
 +        return backend.get_file(filepath, mode)
 +
 +    def delete_file(self, filepath):
 +        backend, filepath = self.resolve_to_backend(filepath)
 +        return backend.delete_file(filepath)
 +
 +    def file_url(self, filepath):
 +        backend, filepath = self.resolve_to_backend(filepath)
 +        return backend.file_url(filepath)
 +
 +    def get_local_path(self, filepath):
 +        backend, filepath = self.resolve_to_backend(filepath)
 +        return backend.get_local_path(filepath)
 +
 +    def copy_locally(self, filepath, dest_path):
 +        """
 +        Need to override copy_locally, because the local_storage
 +        attribute is not correct.
 +        """
 +        backend, filepath = self.resolve_to_backend(filepath)
 +        backend.copy_locally(filepath, dest_path)
 +
 +
  ###########
  # Utilities
  ###########