Feature #477 - Cloud Files public storage
* mediagoblin.storage is now fully PEP-8 compliant. * mediagoblin.storage:BaseFileStorage precedes CloudFilesStorage * Removed all the cloudfiles-specific options from mediagoblin.ini, now it's in the wiki, http://wiki.mediagoblin.org/ConfigureMediaGoblin#Setting_up_Cloud_Files_public_storage
This commit is contained in:
parent
851c51a354
commit
aa797ca130
@ -1,33 +1,7 @@
|
|||||||
[mediagoblin]
|
[mediagoblin]
|
||||||
##
|
queuestore_base_dir = %(here)s/user_dev/media/queue
|
||||||
# BEGIN CloudFiles public storage
|
|
||||||
##
|
|
||||||
# Uncomment the following line and fill in your details to enable Cloud Files
|
|
||||||
# (or OpenStack Object Storage [Swift])
|
|
||||||
# -
|
|
||||||
# publicstore_storage_class = mediagoblin.storage:CloudFilesStorage
|
|
||||||
publicstore_cloudfiles_user = user
|
|
||||||
publicstore_cloudfiles_api_key = 1a2b3c4d5e6f7g8h9i
|
|
||||||
publicstore_cloudfiles_container = mediagoblin
|
|
||||||
|
|
||||||
# Only applicable if you run MediaGoblin on a Rackspace Cloud Server
|
|
||||||
# it routes traffic through the internal Rackspace network, this
|
|
||||||
# means that the bandwith betis free.
|
|
||||||
publicstore_cloudfiles_use_servicenet = false
|
|
||||||
##
|
|
||||||
# END CloudFiles
|
|
||||||
##
|
|
||||||
|
|
||||||
##
|
|
||||||
# BEGIN filesystem public storage
|
|
||||||
##
|
|
||||||
publicstore_base_dir = %(here)s/user_dev/media/public
|
publicstore_base_dir = %(here)s/user_dev/media/public
|
||||||
publicstore_base_url = /mgoblin_media/
|
publicstore_base_url = /mgoblin_media/
|
||||||
##
|
|
||||||
# END
|
|
||||||
##
|
|
||||||
|
|
||||||
queuestore_base_dir = %(here)s/user_dev/media/queue
|
|
||||||
direct_remote_path = /mgoblin_static/
|
direct_remote_path = /mgoblin_static/
|
||||||
email_sender_address = "notice@mediagoblin.example.org"
|
email_sender_address = "notice@mediagoblin.example.org"
|
||||||
|
|
||||||
|
@ -29,11 +29,21 @@ from mediagoblin import util
|
|||||||
# Errors
|
# Errors
|
||||||
########
|
########
|
||||||
|
|
||||||
class Error(Exception): pass
|
|
||||||
class InvalidFilepath(Error): pass
|
|
||||||
class NoWebServing(Error): pass
|
|
||||||
|
|
||||||
class NotImplementedError(Error): pass
|
class Error(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidFilepath(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoWebServing(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotImplementedError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
@ -118,7 +128,7 @@ class StorageInterface(object):
|
|||||||
Eg, if the filename doesn't exist:
|
Eg, if the filename doesn't exist:
|
||||||
>>> storage_handler.get_unique_filename(['dir1', 'dir2', 'fname.jpg'])
|
>>> storage_handler.get_unique_filename(['dir1', 'dir2', 'fname.jpg'])
|
||||||
[u'dir1', u'dir2', u'fname.jpg']
|
[u'dir1', u'dir2', u'fname.jpg']
|
||||||
|
|
||||||
But if a file does exist, let's get one back with at uuid tacked on:
|
But if a file does exist, let's get one back with at uuid tacked on:
|
||||||
>>> storage_handler.get_unique_filename(['dir1', 'dir2', 'fname.jpg'])
|
>>> storage_handler.get_unique_filename(['dir1', 'dir2', 'fname.jpg'])
|
||||||
[u'dir1', u'dir2', u'd02c3571-dd62-4479-9d62-9e3012dada29-fname.jpg']
|
[u'dir1', u'dir2', u'd02c3571-dd62-4479-9d62-9e3012dada29-fname.jpg']
|
||||||
@ -162,61 +172,6 @@ class StorageInterface(object):
|
|||||||
dest_file.write(source_file.read())
|
dest_file.write(source_file.read())
|
||||||
|
|
||||||
|
|
||||||
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 in [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 BasicFileStorage(StorageInterface):
|
class BasicFileStorage(StorageInterface):
|
||||||
"""
|
"""
|
||||||
Basic local filesystem implementation of storage API
|
Basic local filesystem implementation of storage API
|
||||||
@ -240,7 +195,7 @@ class BasicFileStorage(StorageInterface):
|
|||||||
"""
|
"""
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
self.base_dir, *clean_listy_filepath(filepath))
|
self.base_dir, *clean_listy_filepath(filepath))
|
||||||
|
|
||||||
def file_exists(self, filepath):
|
def file_exists(self, filepath):
|
||||||
return os.path.exists(self._resolve_filepath(filepath))
|
return os.path.exists(self._resolve_filepath(filepath))
|
||||||
|
|
||||||
@ -272,6 +227,65 @@ class BasicFileStorage(StorageInterface):
|
|||||||
return self._resolve_filepath(filepath)
|
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()
|
||||||
|
|
||||||
|
|
||||||
###########
|
###########
|
||||||
# Utilities
|
# Utilities
|
||||||
###########
|
###########
|
||||||
@ -339,7 +353,7 @@ def storage_system_from_config(paste_config, storage_prefix):
|
|||||||
for key, value in paste_config.iteritems()
|
for key, value in paste_config.iteritems()
|
||||||
if prefix_re.match(key)])
|
if prefix_re.match(key)])
|
||||||
|
|
||||||
if config_params.has_key('storage_class'):
|
if 'storage_class' in config_params:
|
||||||
storage_class = config_params['storage_class']
|
storage_class = config_params['storage_class']
|
||||||
config_params.pop('storage_class')
|
config_params.pop('storage_class')
|
||||||
else:
|
else:
|
||||||
@ -347,5 +361,3 @@ def storage_system_from_config(paste_config, storage_prefix):
|
|||||||
|
|
||||||
storage_class = util.import_component(storage_class)
|
storage_class = util.import_component(storage_class)
|
||||||
return storage_class(**config_params)
|
return storage_class(**config_params)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user