terom@5: """ terom@5: Mount filesystems. terom@5: """ terom@5: terom@69: from pvl.backup.invoke import invoke, optargs, command terom@5: terom@5: import contextlib terom@33: import os, os.path terom@5: import logging terom@33: import tempfile terom@5: terom@5: log = logging.getLogger('pvl.backup.mount') terom@5: terom@5: terom@5: class MountError (Exception) : terom@5: pass terom@5: terom@5: class Mount (object) : terom@5: """ terom@5: Trivial filesystem mounting terom@5: """ terom@5: terom@5: MOUNT = '/bin/mount' terom@5: UMOUNT = '/bin/umount' terom@5: terom@5: terom@69: def __init__ (self, dev, mnt, readonly=False, sudo=None) : terom@5: """ terom@5: dev - device path terom@5: mnt - mount path terom@5: readonly - mount readonly terom@69: sudo - invoke sudo terom@5: """ terom@5: terom@5: self.dev = dev terom@5: self.mnt = mnt terom@5: self.readonly = readonly terom@69: self.sudo = sudo terom@5: terom@5: @property terom@5: def path (self) : terom@5: return self.mnt terom@5: terom@5: def options (self) : terom@5: """ terom@5: Mount options as a comma-separated string. terom@5: """ terom@5: terom@5: options = [ terom@5: ('ro' if self.readonly else None), terom@5: ] terom@5: terom@5: return ','.join(option for option in options if option) terom@5: terom@5: def open (self) : terom@5: """ terom@5: Mount terom@5: """ terom@5: terom@5: # check terom@5: if not os.path.isdir(self.mnt) : terom@5: raise MountError("Mountpoint is not a directory: {mnt}".format(mnt=self.mnt)) terom@5: terom@78: if self.is_mounted() : terom@5: raise MountError("Mountpoint is already mounted: {mnt}".format(mnt=self.mnt)) terom@5: terom@5: if not os.path.exists(self.dev) : terom@5: raise MountError("Device does not exist: {dev}".format(dev=self.dev)) terom@5: terom@5: # mount terom@69: invoke(self.MOUNT, optargs(self.dev, self.mnt, options=self.options()), sudo=self.sudo) terom@5: terom@78: def is_mounted (self) : terom@78: """ terom@78: Test if the given mountpoint is mounted. terom@78: """ terom@78: terom@78: # workaround http://bugs.python.org/issue2466 terom@78: if os.path.exists(self.mnt) and not os.path.exists(os.path.join(self.mnt, '.')) : terom@78: # this is a sign of a mountpoint that we do not have access to terom@78: return True terom@78: terom@78: return os.path.ismount(self.mnt) terom@78: terom@5: def close (self) : terom@5: """ terom@5: Un-mount terom@5: """ terom@5: terom@5: # check terom@78: if not self.is_mounted() : terom@5: raise MountError("Mountpoint is not mounted: {mnt}".format(mnt=self.mnt)) terom@5: terom@5: # umount terom@69: invoke(self.UMOUNT, optargs(self.mnt), sudo=self.sudo) terom@5: terom@10: def __repr__ (self) : terom@10: return "Mount(dev={dev}, mnt={mnt})".format( terom@10: dev = repr(self.dev), terom@10: mnt = repr(self.mnt), terom@10: ) terom@10: terom@10: def __str__ (self) : terom@10: return self.mnt terom@10: terom@5: @contextlib.contextmanager terom@33: def mount (dev, mnt=None, name_hint='tmp', **kwargs) : terom@5: """ terom@5: Use a temporary mount: terom@5: terom@33: with mount('/dev/...', readonly=True) as mount: terom@5: ... terom@33: terom@33: Mounts at the given mountpoint path, or a tempdir terom@5: """ terom@5: terom@33: if mnt is None : terom@33: mnt = tmpdir = tempfile.mkdtemp(suffix='.mnt', prefix=name_hint) terom@5: terom@33: log.debug("using tmp mnt: %s", tmpdir) terom@5: terom@33: else : terom@33: tmpdir = None terom@33: terom@33: log.debug("mount: %s -> %s", dev, mnt) terom@33: terom@33: # with tempdir terom@5: try : terom@33: mount = Mount(dev, mnt, **kwargs) terom@33: terom@33: # open terom@33: log.debug("open: %s", mount) terom@33: mount.open() terom@33: terom@33: try : terom@33: log.debug("got: %s", mount) terom@33: yield mount terom@33: terom@33: finally: terom@33: # cleanup terom@33: log.debug("cleanup: %s", mount) terom@78: terom@78: try : terom@78: mount.close() terom@78: terom@78: except Exception as ex : terom@78: log.warning("cleanup: %s: %s", mount, ex) terom@5: terom@5: finally: terom@33: if tmpdir : terom@33: # cleanup terom@33: log.debug("cleanup tmp mnt: %s", tmpdir) terom@33: os.rmdir(tmpdir) terom@5: