import glob
import os
import subprocess
import flask
from celery.task import Task as CeleryTask
from flask_babel import lazy_gettext as _
from xmm.core import mongo
from xmm.models import Notification
from xmm.util.text import get_host_from_url
from .base import runner
[docs]class MongoRestoreTask(CeleryTask):
"""Restore MongoDB collections from dumps created with the MongoDumpTask."""
SYSTEM_PREFIX = 'system_'
DATA_PREFIX = 'data_'
COLLECTION_PREFIX = 'collection_'
MEDIA_PREFIX = 'media_'
CMS_PREFIX = 'cms_'
def _import_collection(self, path, db, collection, quiet):
"""`mongo_restore` requires the direct path to a *.bson file when trying to import a single collection."""
path = os.path.join(path, '{}.bson'.format(collection))
script = [
flask.current_app.config['BINARIES']['mongorestore'],
'--host={}'.format(get_host_from_url(flask.current_app.config['MONGODB_URL'])),
'--db={}'.format(db),
'--collection={}'.format(collection),
'--drop',
path,
]
if quiet:
script.append('--quiet')
return subprocess.call(script)
def _import_one_collection(self, path, mongo_dbname, collection, quiet):
returncode = self._import_collection(path, mongo_dbname, collection, quiet)
if returncode == 0:
message = _('Mongorestore: {} importiert.').format(collection)
Notification.notify(message, 'success')
else:
message = _('Mongorestore fehlgeschlagen ({}).').format(returncode)
Notification.notify(message, 'error')
def _iter_collections(self, path, no_masterdata, no_collections, no_media, no_cms):
files = glob.iglob(os.path.join(path, '*.bson'))
for filename in files:
collection = os.path.basename(filename).rsplit('.', 1)[0]
if no_masterdata and collection.startswith(self.DATA_PREFIX):
continue
if no_collections and collection.startswith(self.COLLECTION_PREFIX):
continue
if no_media and collection.startswith(self.MEDIA_PREFIX):
continue
if no_cms and collection.startswith(self.CMS_PREFIX):
continue
yield collection
def _import_many_collections(self, path, mongo_dbname, collections, quiet):
succeeded = []
failed = []
returncode = -1
for collection in collections:
returncode = self._import_collection(path, mongo_dbname, collection, quiet)
if returncode == 0:
succeeded.append(collection)
else:
failed.append(collection)
if returncode == 0 and len(failed) == 0:
message = _('Mongorestore: {} collections importiert.').format(len(succeeded))
Notification.notify(message, 'success')
else:
message = _('Mongorestore fehlgeschlagen ({}).').format(', '.join(failed))
Notification.notify(message, 'error')
@runner
def run(self, task, path=None,
only=None, no_masterdata=False, no_collections=False, no_media=False, no_cms=False, quiet=False):
"""
Run the task.
:param path: Path to dump location
:param only: Import only one collection
:param no_masterdata: Wether to include masterdata
:param no_collections: Wether to include collections data
:param no_media: Wether to include media data
:param no_cms: Wether to include cms data
:param quiet: No output on command line
"""
if path is None:
path = os.path.join(flask.current_app.config['STATIC_ROOT'], 'mongodump')
os.makedirs(path, 0o775, exist_ok=True)
mongo_dbname = mongo.connection.get_default_database().name
if not path.endswith(mongo_dbname):
path = os.path.join(path, mongo_dbname)
if only is not None:
self._import_one_collection(path, mongo_dbname, only, quiet)
else:
self._import_many_collections(
path,
mongo_dbname,
self._iter_collections(
path,
no_masterdata,
no_collections,
no_media,
no_cms,
),
quiet)