1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 from mediagoblin
.storage
import StorageInterface
, clean_listy_filepath
20 class MountStorage(StorageInterface
):
22 Experimental "Mount" virtual Storage Interface
24 This isn't an interface to some real storage, instead it's a
25 redirecting interface, that redirects requests to other
28 For example, say you have the paths:
30 1. ['user_data', 'cwebber', 'avatar.jpg']
31 2. ['user_data', 'elrond', 'avatar.jpg']
32 3. ['media_entries', '34352f304c3f4d0ad8ad0f043522b6f2', 'thumb.jpg']
34 You could mount media_entries under CloudFileStorage and user_data
35 under BasicFileStorage. Then 1 would be passed to
36 BasicFileStorage under the path ['cwebber', 'avatar.jpg'] and 3
37 would be passed to CloudFileStorage under
38 ['34352f304c3f4d0ad8ad0f043522b6f2', 'thumb.jpg'].
40 In other words, this is kind of like mounting /home/ and /etc/
41 under different filesystems on your operating system... but with
42 mediagoblin filestorages :)
44 To set this up, you currently need to call the mount() method with
45 the target path and a backend, that shall be available under that
46 target path. You have to mount things in a sensible order,
47 especially you can't mount ["a", "b"] before ["a"].
49 def __init__(self
, **kwargs
):
52 def mount(self
, dirpath
, backend
):
54 Mount a new backend under dirpath
56 new_ent
= clean_listy_filepath(dirpath
)
58 print "Mounting:", repr(new_ent
)
59 already
, rem_1
, table
, rem_2
= self
._resolve
_to
_backend
(new_ent
, True)
60 print "===", repr(already
), repr(rem_1
), repr(rem_2
), len(table
)
62 assert (len(rem_2
) > 0) or (None not in table
), \
63 "That path is already mounted"
64 assert (len(rem_2
) > 0) or (len(table
) == 0), \
65 "A longer path is already mounted here"
72 def _resolve_to_backend(self
, filepath
, extra_info
=False):
74 extra_info = True is for internal use!
76 Normally, returns the backend and the filepath inside that backend.
78 With extra_info = True it returns the last directory node and the
79 remaining filepath from there in addition.
82 filepath
= filepath
[:]
85 new_be
= table
.get(None)
86 if (new_be
is not None) or res_fp
is None:
89 res_extra
= (table
, filepath
[:])
90 # print "... New res: %r, %r, %r" % (res_be, res_fp, res_extra)
91 if len(filepath
) == 0:
93 query
= filepath
.pop(0)
94 entry
= table
.get(query
)
97 res_extra
= (table
, filepath
[:])
101 return (res_be
, res_fp
) + res_extra
103 return (res_be
, res_fp
)
105 def resolve_to_backend(self
, filepath
):
106 backend
, filepath
= self
._resolve
_to
_backend
(filepath
)
108 raise Error("Path not mounted")
109 return backend
, filepath
111 def __repr__(self
, table
=None, indent
=[]):
114 res
.append("MountStorage<")
115 table
= self
.mounttab
118 res
.append(" " * len(indent
) + repr(indent
) + ": " + repr(v
))
119 for k
, v
in table
.iteritems():
122 res
.append(" " * len(indent
) + repr(k
) + ":")
123 res
+= self
.__repr
__(v
, indent
+ [k
])
124 if table
is self
.mounttab
:
126 return "\n".join(res
)
130 def file_exists(self
, filepath
):
131 backend
, filepath
= self
.resolve_to_backend(filepath
)
132 return backend
.file_exists(filepath
)
134 def get_file(self
, filepath
, mode
='r'):
135 backend
, filepath
= self
.resolve_to_backend(filepath
)
136 return backend
.get_file(filepath
, mode
)
138 def delete_file(self
, filepath
):
139 backend
, filepath
= self
.resolve_to_backend(filepath
)
140 return backend
.delete_file(filepath
)
142 def file_url(self
, filepath
):
143 backend
, filepath
= self
.resolve_to_backend(filepath
)
144 return backend
.file_url(filepath
)
146 def get_local_path(self
, filepath
):
147 backend
, filepath
= self
.resolve_to_backend(filepath
)
148 return backend
.get_local_path(filepath
)
150 def copy_locally(self
, filepath
, dest_path
):
152 Need to override copy_locally, because the local_storage
153 attribute is not correct.
155 backend
, filepath
= self
.resolve_to_backend(filepath
)
156 backend
.copy_locally(filepath
, dest_path
)