diff options
author | Joerg Steffens <joerg.steffens@bareos.com> | 2017-06-27 13:03:54 +0300 |
---|---|---|
committer | Joerg Steffens <joerg.steffens@bareos.com> | 2017-06-27 16:02:16 +0300 |
commit | 2eb63fef391b9dd764583c837879ca7eb57f209b (patch) | |
tree | 9b6bfa5af84deca3bc593f33a9cab02337c7081a | |
parent | 8e63b63bbadb6b3992624db9d4140e603e494fc5 (diff) |
moved python module bareos.fuse to bareos-fuse repository
The python module bareos.fuse have been part of the
https://github.com/bareos/python-bareos.git repository.
This commit move it to the https://github.com/bareos/bareos-fuse repository.
32 files changed, 15 insertions, 1301 deletions
diff --git a/bareos/fuse/__init__.py b/bareos/fuse/__init__.py deleted file mode 100644 index 215da07..0000000 --- a/bareos/fuse/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#__all__ = [ "bconsole" ] -from bareos.fuse.exceptions import * -from bareos.fuse.bareosfuse import BareosFuse -import bareos.fuse.node diff --git a/bareos/fuse/bareosfuse.py b/bareos/fuse/bareosfuse.py deleted file mode 100644 index c851c0e..0000000 --- a/bareos/fuse/bareosfuse.py +++ /dev/null @@ -1,152 +0,0 @@ -""" -FUSE filesystem on bareos data. -""" - -from bareos.bsock import DirectorConsoleJson -import bareos.bsock -from bareos.util import Path -from bareos.fuse.root import Root -import errno -import fuse -import logging -import socket -import stat -import os.path -from pprint import pformat - -fuse.fuse_python_api = (0, 2) - - -class BareosFuse(fuse.Fuse): - - def __init__(self, *args, **kw): - self.bsock = None - self.bareos = None - self.restoreclient = None - self.restorejob = None - self.restorepath = '/var/cache/bareosfs/' - super(BareosFuse, self).__init__(*args, **kw) - self.multithreaded = False - - def initLogging(self): - self.logger = logging.getLogger() - self.logger.setLevel(logging.DEBUG) - if hasattr(self, "logfile"): - hdlr = logging.FileHandler(self.logfile) - # limit message size - formatter = logging.Formatter('%(asctime)s %(levelname)-7s %(module)s %(funcName)s( %(message).800s )') - hdlr.setFormatter(formatter) - self.logger.addHandler(hdlr) - - def parse(self, *args, **kw): - super(BareosFuse, self).parse(*args, **kw) - if self.fuse_args.mount_expected(): - options = [ 'address', 'port', 'dirname', 'name', 'password' ] - self.bsockParameter = {} - for i in options: - if hasattr(self, i): - self.bsockParameter[i] = getattr(self,i) - else: - #self.logger.debug( "%s: missing, default: %s" %(i, str(getattr(self,i,None)))) - pass - if not hasattr(self, 'password'): - raise bareos.fuse.ParameterMissing("missing parameter password") - else: - password = bareos.bsock.Password(self.password) - self.bsockParameter['password']=password - self.restorepath = os.path.normpath(self.restorepath) - if not os.path.isabs(self.restorepath): - raise bareos.fuse.RestorePathInvalid("restorepath must be an absolute path") - - - def main(self, *args, **kw): - # use main() instead of fsinit, - # as this prevents FUSE from being started in case of errors. - - self.initLogging() - self.logger.debug('start') - if self.fuse_args.mount_expected(): - try: - self.bsock = bareos.bsock.BSockJson(**self.bsockParameter) - except socket.error as e: - self.logger.exception(e) - raise bareos.fuse.SocketConnectionRefused(e) - self.bareos = Root(self.bsock, self.restoreclient, self.restorejob, self.restorepath) - super(BareosFuse, self).main(*args, **kw) - self.logger.debug('done') - - def getattr(self, path): - if self.bareos: - stat = self.bareos.getattr(Path(path)) - if isinstance(stat, fuse.Stat): - self.logger.debug("{path}: dev={stat.st_dev}, ino={stat.st_ino}, mode={stat.st_mode}, nlink={stat.st_nlink}, uid={stat.st_uid}, gid={stat.st_gid}, size={stat.st_size}, atime={stat.st_atime}, ctime={stat.st_ctime}, mtime={stat.st_mtime}".format(path=path, stat=stat)) - else: - self.logger.debug("%s: (int) %i" % (path, stat)) - return stat - - def read(self, path, size, offset): - result = self.bareos.read(Path(path), size, offset) - self.logger.debug("%s: %s" % (path, result)) - return result - - def readdir(self, path, offset): - entries = self.bareos.readdir(Path(path), offset) - self.logger.debug("%s: %s" % (path, entries)) - if not entries: - return -errno.ENOENT - else: - return [fuse.Direntry(f) for f in entries] - - def readlink(self, path): - result = self.bareos.readlink(Path(path)) - self.logger.debug("%s: %s" % (path, result)) - return result - - def listxattr(self, path, size): - ''' - list extended attributes - ''' - keys = self.bareos.listxattr(Path(path)) - if size == 0: - # We are asked for size of the attr list, ie. joint size of attrs - # plus null separators. - result = len("".join(keys)) + len(keys) - self.logger.debug("%s: len=%s" % (path, result)) - else: - result = keys - self.logger.debug("%s: keys=%s" % (path, ", ".join(keys))) - return result - - def getxattr(self, path, key, size): - ''' - get value of extended attribute - burpfs exposes some filesystem attributes for the root directory - (e.g. backup number, cache prefix - see FileSystem.xattr_fields_root) - and may also expose several other attributes for each file/directory - in the future (see FileSystem.xattr_fields) - ''' - value = self.bareos.getxattr(Path(path), key) - self.logger.debug("%s: %s=%s" % (path, key, str(value))) - if value == None: - result = -errno.ENODATA - elif size == 0: - # we are asked for size of the value - result = len(value) - self.logger.debug("%s: len=%s" % (path, result)) - else: - result = value - self.logger.debug("%s: %s=%s" % (path, key, result)) - return result - - def setxattr(self, path, key, value, flags): - ''' - set value of extended attribute - ''' - result = self.bareos.setxattr(Path(path), key, value, flags) - self.logger.debug("%s: %s=%s: %s" % (path, key, value, str(result))) - return result - - def rename(self, oldpath, newpath): - result = self.bareos.rename(Path(oldpath), Path(newpath)) - self.logger.debug("%s => %s: %s" % (oldpath, newpath, result)) - return result diff --git a/bareos/fuse/exceptions.py b/bareos/fuse/exceptions.py deleted file mode 100644 index 2a7eb2f..0000000 --- a/bareos/fuse/exceptions.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Bareos-Fuse specific exceptions -""" - -class ParameterMissing(Exception): - """parameter missing""" - pass - -class SocketConnectionRefused(Exception): - """network socket connection refused""" - pass - -class RestoreClientUnknown(Exception): - """given restore client is not known""" - pass - -class RestoreJobUnknown(Exception): - """given restore job is not known""" - pass - -class RestorePathInvalid(Exception): - """restore path is invalid""" - pass diff --git a/bareos/fuse/node/__init__.py b/bareos/fuse/node/__init__.py deleted file mode 100644 index a33ddfc..0000000 --- a/bareos/fuse/node/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# ls *.py | sed -r "s/(.*)\.py/'\1', /" | tr '\n' ' ' -files = ['backups', 'base', 'bvfsdir', 'bvfsfile', 'client', 'clients', 'directory', 'file', 'job', 'joblog', 'jobs', 'jobslist', 'jobsname', 'pool', 'pools', 'status', 'volume', 'volumelist', 'volumestatus'] - -for i in files: - module = __import__( "bareos.fuse.node." + i, globals(), locals(), ['*']) - for k in dir(module): - locals()[k] = getattr(module, k) - del k -del i -del files diff --git a/bareos/fuse/node/backups.py b/bareos/fuse/node/backups.py deleted file mode 100644 index 580927c..0000000 --- a/bareos/fuse/node/backups.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.job import Job - -class Backups(Directory): - def __init__(self, root, name, client): - super(Backups, self).__init__(root, name) - self.client = client - - @classmethod - def get_id(cls, name, client): - return client - - def do_update(self): - data = self.bsock.call("llist backups client=%s" % (self.client)) - backups = data['backups'] - for i in backups: - self.add_subnode(Job, i) diff --git a/bareos/fuse/node/base.py b/bareos/fuse/node/base.py deleted file mode 100644 index ac6f36d..0000000 --- a/bareos/fuse/node/base.py +++ /dev/null @@ -1,296 +0,0 @@ -""" -""" - -from datetime import datetime, timedelta -from dateutil import parser as DateParser -import errno -import fuse -import grp -import logging -import pwd -import stat - -class Base(object): - """ - Base class for filesystem nodes. - """ - def __init__(self, root, name): - self.logger = logging.getLogger() - self.root = root - self.bsock = root.bsock - self.id = None - self.set_name(name) - self.stat = fuse.Stat() - self.xattr = {} - self.content = None - self.subnodes = {} - self.subnodes_old = self.subnodes.copy() - self.subnode_count = len(self.subnodes) - self.static = False - self.lastupdate = None - self.lastupdate_stat = None - # timeout for caching - self.cache_timeout = timedelta(seconds=60) - self.cache_stat_timeout = timedelta(seconds=60) - - @classmethod - def get_id(cls, *args, **kwargs): - """ - To enable reusing instances at different places in the filesystem, - this method must be overwritten - and return a unique id (string) for this class. - The parameter of this must be identical to the parameter - of the __init__ method must be identical to the parameter of get_id, - except that the "root" parameter is excluded. - """ - return None - - def do_get_name(self, *args, **kwargs): - """ - Get name from init parameter. - Normally name is statically set, - but for some objects is it based on status data - and dependend on where the instance is locationed - in the directory tree (e.g. volumestatus). - """ - return self.name - - # Interface - # ========= - - def get_name(self, *args, **kwargs): - result = self.do_get_name(*args, **kwargs) - if isinstance(result, unicode): - result = result.encode('utf-8', 'replace') - return result - - def set_name(self, name): - if isinstance(name, unicode): - self.name = name.encode('utf-8', 'replace') - else: - # should be str or NoneType - self.name = name - - def set_static(self, value=True): - self.static = value - - def get_stat(self): - return self.stat - - def set_stat(self, stat): - try: - if stat['mode'] > 0: - self.stat.st_mode = stat['mode'] - self.stat.st_size = stat['size'] - self.stat.st_atime = stat['atime'] - self.stat.st_ctime = stat['ctime'] - self.stat.st_mtime = stat['mtime'] - except KeyError as e: - self.logger.warning(str(e)) - pass - try: - uid = pwd.getpwnam(stat['user']).pw_uid - self.stat.st_uid = uid - except KeyError as e: - self.logger.info("user %s not known on this system, fall back to uid 0" % (stat['user'])) - pass - try: - gid = grp.getgrnam(stat['group']).gr_gid - self.stat.st_gid = gid - except KeyError as e: - self.logger.info("group %s not known on this system, fall back to gid 0" % (stat['group'])) - pass - #"stat": { - #"atime": 1441134679, - #"ino": 3689524, - #"dev": 2051, - #"mode": 33256, - #"nlink": 1, - #"user": "joergs", - #"group": "joergs", - #"ctime": 1441134679, - #"rdev": 0, - #"size": 1613, - #"mtime": 1441134679 - #}, - - def add_subnode(self, classtype, *args, **kwargs): - instance = self.root.factory.get_instance(classtype, *args, **kwargs) - name = instance.get_name(*args, **kwargs) - if name not in self.subnodes: - self.subnodes[name] = instance - else: - if name in self.subnodes_old: - del(self.subnodes_old[name]) - - def update_stat(self): - # update status, content, ... - now = datetime.now() - if self.lastupdate_stat == None: - self.logger.debug("reason: first time") - self.do_update_stat() - self.lastupdate_stat = now - elif not self.static and (self.lastupdate_stat + self.cache_stat_timeout) < now: - diff = now - self.lastupdate_stat - self.logger.debug("reason: non-static and timeout (%d seconds)" % (diff.seconds)) - self.do_update_stat() - self.lastupdate_stat = datetime.now() - else: - self.logger.debug("skipped (lastupdate: %s, static: %s)" % ( str(self.lastupdate_stat), str(self.static))) - - def do_update_stat(self): - """ - if not overwritten, a full update is performed. - Only updating the stat can be more efficient. - """ - self.update() - - def update(self): - # update status, content, ... - now = datetime.now() - if self.lastupdate == None: - self.logger.debug("reason: first time") - self.do_update() - self.lastupdate = now - elif not self.static and (self.lastupdate + self.cache_timeout) < now: - diff = now - self.lastupdate - self.logger.debug("reason: non-static and timeout (%d seconds)" % (diff.seconds)) - # store current subnodes - # and delete all not updated subnodes after do_update() - self.subnodes_old = self.subnodes.copy() - self.do_update() - for i in list(self.subnodes_old.keys()): - self.logger.debug("removing outdated node %s" % (i)) - del(self.subnodes[i]) - self.subnode_count = len(self.subnodes) - self.lastupdate = datetime.now() - else: - self.logger.debug("skipped (lastupdate: %s, static: %s)" % ( str(self.lastupdate), str(self.static))) - - def do_update(self): - """ - dummy, to be overwritten (not inherited!) by inherented classes - """ - # if no node specific do_update exists, - # remove marker for nodes to be deleted after update - self.subnodes_old = {} - - - # Filesystem methods - # ================== - - def getattr(self, path): - #self.logger.debug("%s(\"%s\")" % (str(self), str(path))) - result = -errno.ENOENT - if path.len() == 0: - self.update_stat() - result = self.get_stat() - else: - if not (path.get(0) in self.subnodes): - self.update() - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].getattr(path) - return result - - - def read(self, path, size, offset): - #self.logger.debug("%s(\"%s\", %d, %d)" % (str(self), str(path), size, offset)) - result = -errno.ENOENT - if path.len() == 0: - self.logger.debug( "content: " + str(self.content) ) - self.update() - if self.content != None: - result = self.content[offset:offset+size] - else: - if not (path.get(0) in self.subnodes): - self.update() - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].read(path, size, offset) - return result - - def readlink(self, path): - #self.logger.debug("%s(\"%s\", %d, %d)" % (str(self), str(path), size, offset)) - result = -errno.ENOENT - if path.len() == 0: - pass - else: - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].readlink(path) - return result - - def listxattr(self, path): - ''' - list extended attributes - ''' - #self.logger.debug("%s(\"%s\", %s)" % (str(self.get_name()), str(path), str(size))) - result = [] - if path.len() == 0: - result = list(self.xattr.keys()) - else: - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].listxattr(path) - return result - - def getxattr(self, path, key): - ''' - get value of extended attribute - ''' - #self.logger.debug("%s(\"%s\")" % (str(self), str(path))) - result = None - if path.len() == 0: - try: - result = self.xattr[key] - if isinstance(self.xattr[key], unicode): - result = self.xattr[key].encode('utf-8','replace') - except KeyError: - pass - else: - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].getxattr(path, key) - return result - - def setxattr(self, path, key, value, flags): - ''' - set value of extended attribute - ''' - result = 0 - if path.len() == 0: - self.xattr[key] = value - else: - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].setxattr(path, key, value, flags) - return result - - def rename(self, oldpath, newpath): - self.logger.debug("%s(\"%s %s\")" % (str(self), str(oldpath), str(newpath))) - result = -errno.ENOENT - if path.len() == 0: - #self.update() - #result = self.get_stat() - pass - else: - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].rename(oldpath, newpath) - return result - - # Helpers - # ======= - - def _convert_date_bareos_unix(self, bareosdate): - unixtimestamp = 0 - try: - unixtimestamp = int(DateParser.parse(bareosdate).strftime("%s")) - #self.logger.debug( "unix timestamp: %d" % (unixtimestamp)) - except ValueError: - pass - # could happen because of timezones - if unixtimestamp < 0: - unixtimestamp = 0 - return unixtimestamp diff --git a/bareos/fuse/node/bvfscommon.py b/bareos/fuse/node/bvfscommon.py deleted file mode 100644 index b9eead3..0000000 --- a/bareos/fuse/node/bvfscommon.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.base import Base -import logging -import os -import stat - -class BvfsCommon(Base): - XattrKeyRestoreTrigger = "user.bareos.do" - XattrKeyRestoreJobId = "user.bareos.restore_job_id" - - def init(self, root, jobid, path, filename, stat): - self.jobid = jobid - self.static = True - self.restorepath = os.path.normpath("/%s/jobid=%s/" % (self.root.restorepath, self.jobid)) - if filename: - self.restorepathfull = os.path.normpath("%s%s%s" % (self.restorepath, path, filename)) - else: - self.restorepathfull = os.path.normpath("%s%s" % (self.restorepath, path)) - self.xattr = { - 'user.bareos.restorepath': self.restorepathfull, - 'user.bareos.restored': 'no', - } - if self.root.restoreclient: - # restore is only possible, if a restoreclient is given - self.xattr['user.bareos.do_options'] = 'restore' - self.xattr['user.bareos.do'] = '' - - if stat: - self.set_stat(stat) - - def listxattr(self, path): - # update xattrs - if self.xattr['user.bareos.restored'] != 'yes': - if os.access(self.restorepathfull, os.F_OK): - self.xattr['user.bareos.restored'] = 'yes' - return super(BvfsCommon, self).listxattr(path) - - # Helpers - # ======= - - def restore(self, path, pathIds, fileIds): - self.logger.debug( "start" ) - bvfs_restore_id = "b20042" - dirId='' - if pathIds: - dirId='dirid=' + ",".join(map(str, pathIds)) - fileId='' - if fileIds: - fileId='fileid=' + ",".join(map(str, fileIds)) - select = self.bsock.call( - '.bvfs_restore jobid={jobid} {dirid} {fileid} path={bvfs_restore_id}'.format( - jobid = self.jobid, - dirid = dirId, - fileid = fileId, - bvfs_restore_id = bvfs_restore_id)) - restorejob='' - if self.root.restorejob: - restorejob="restorejob={restorejob} ".format(restorejob=self.root.restorejob) - restore = self.bsock.call( - 'restore file=?{bvfs_restore_id} client={client} restoreclient={restoreclient} {restorejob} where="{where}" yes'.format( - client = self.job.job['client'], - restoreclient = self.root.restoreclient, - restorejob = restorejob, - where = self.restorepath, - bvfs_restore_id = bvfs_restore_id)) - try: - restorejobid=restore['run']['jobid'] - self.setxattr(path, self.XattrKeyRestoreJobId, str(restorejobid), 0) - except KeyError: - self.logger.debug("failed to get resulting jobid of run command (maybe old version of Bareos Director)") - cleanup = self.bsock.call('.bvfs_cleanup path={bvfs_restore_id}'.format(bvfs_restore_id = bvfs_restore_id)) - self.logger.debug( "end" ) diff --git a/bareos/fuse/node/bvfsdir.py b/bareos/fuse/node/bvfsdir.py deleted file mode 100644 index 0eb47d8..0000000 --- a/bareos/fuse/node/bvfsdir.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.exceptions import * -from bareos.fuse.node.bvfscommon import BvfsCommon -from bareos.fuse.node.bvfsfile import BvfsFile -from bareos.fuse.node.directory import Directory -from dateutil import parser as DateParser -import errno -import logging - -class BvfsDir(Directory, BvfsCommon): - def __init__(self, root, name, job, pathid, directory = None): - super(BvfsDir, self).__init__(root, name) - self.job = job - self.jobid = job.job['jobid'] - self.pathid = pathid - self.static = True - self.directory = directory - if directory: - self.set_stat(directory['stat']) - BvfsCommon.init(self, self.root, self.jobid, self.directory['fullpath'], None, self.directory['stat']) - - @classmethod - def get_id(cls, name, job, pathid, directory = None): - id = None - if pathid == None: - id = "jobid=%s" % (str(job.job['jobid'])) - else: - id = "jobid=%s_pathid=%s" % (str(job.job['jobid']),str(pathid)) - return id - - def setxattr(self, path, key, value, flags): - if path.len() == 0: - self.logger.debug("value: " + value + " ,type: " + str(type(value))) - if key == self.XattrKeyRestoreTrigger and value == b"restore": - if not self.root.restoreclient: - self.logger.warning("no restoreclient given, files can not be restored") - return -errno.ENOENT - if not self.root.restorepath: - self.logger.warning("no restorepath given, files can not be restored") - return -errno.ENOENT - else: - self.restore(path, [ self.pathid ], None) - return super(BvfsDir, self).setxattr(path, key, value, flags) - - def do_update(self): - directories = self.get_directories(self.pathid) - files = self.get_files(self.pathid) - for i in directories: - if i['name'] != "." and i['name'] != "..": - name = i['name'].rstrip('/') - pathid = i['pathid'] - self.add_subnode(BvfsDir, name, self.job, pathid, i) - self.subnode_count = len(self.subnodes) - if self.directory: - for i in files: - self.add_subnode(BvfsFile, i, self.job, self.directory['fullpath']) - - def get_directories(self, pathid): - if pathid == None: - self.bsock.call(".bvfs_update jobid=%s" % (self.jobid)) - path = 'path=""' - else: - path = 'pathid=%s' % pathid - data = self.bsock.call('.bvfs_lsdirs jobid=%s %s' % (self.jobid, path)) - directories = data['directories'] - if pathid == None: - found = False - for i in directories: - if i['name'] != "." and i['name'] != "..": - if i['name'] == "/": - self.directory = i - self.pathid = i['pathid'] - BvfsCommon.init(self, self.root, self.jobid, i['fullpath'], None, i['stat']) - else: - found = True - if not found: - # skip empty root directory - if not self.pathid: - # no pathid and no directoies found. This should not happen - return [] - return self.get_directories(self.pathid) - return directories - - def get_files(self, pathid): - if pathid: - data = self.bsock.call(".bvfs_lsfiles jobid=%s pathid=%s" % (self.jobid, pathid)) - return data['files'] - else: - return [] diff --git a/bareos/fuse/node/bvfsfile.py b/bareos/fuse/node/bvfsfile.py deleted file mode 100644 index d47a9b9..0000000 --- a/bareos/fuse/node/bvfsfile.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.bvfscommon import BvfsCommon -from bareos.fuse.node.file import File -import errno -import os - -class BvfsFile(File, BvfsCommon): - def __init__(self, root, file, job, bvfspath): - super(BvfsFile, self).__init__(root, file['name'], content = None) - self.file = file - self.job = job - self.bvfspath = bvfspath - self.id = self.get_id(file, job, bvfspath) - BvfsCommon.init(self, self.root, self.file['jobid'], self.bvfspath, self.file['name'], self.file['stat']) - - @classmethod - def get_id(cls, file, job, bvfspath): - return "jobid=%s_fileid=%s" % (str(job.job['jobid']),str(file['fileid'])) - - - # Filesystem methods - # ================== - - def readlink(self, path): - self.logger.debug( "readlink %s" % (path) ) - try: - pathname = os.readlink(self.restorepathfull) - if pathname.startswith("/"): - # Path name is absolute, sanitize it. - return str(os.path.relpath(pathname, self.restorepath)) - else: - return str(pathname) - except OSError: - return -errno.ENOENT - - def setxattr(self, path, key, value, flags): - if path.len() == 0: - if key == self.XattrKeyRestoreTrigger and value == "restore": - if not self.root.restoreclient: - self.logger.error("no restoreclient given, files can not be restored") - return -errno.EPERM - if not self.root.restorepath: - self.logger.error("no restorepath given, files can not be restored") - return -errno.EPERM - else: - self.restore(path, None, [ self.file['fileid'] ]) - return super(BvfsFile, self).setxattr(path, key, value, flags) - - - # File methods - # ============ - - def open(self, path, flags): - return os.open(self.restorepathfull, flags) - - #def create(self, path, mode, fi=None): - #full_path = self._full_path(path) - #return os.open(full_path, os.O_WRONLY | os.O_CREAT, mode) - - def read(self, path, length, offset): - try: - fh=self.open(path, os.O_RDONLY) - os.lseek(fh, offset, os.SEEK_SET) - return os.read(fh, length) - except OSError: - return -errno.ENOENT - - #def write(self, path, buf, offset, fh): - #os.lseek(fh, offset, os.SEEK_SET) - #return os.write(fh, buf) - - #def truncate(self, path, length, fh=None): - #full_path = self._full_path(path) - #with open(full_path, 'r+') as f: - #f.truncate(length) - - #def flush(self, path, fh): - #return os.fsync(fh) - - #def release(self, path, fh): - #return os.close(fh) - - #def fsync(self, path, fdatasync, fh): - #return self.flush(path, fh) diff --git a/bareos/fuse/node/client.py b/bareos/fuse/node/client.py deleted file mode 100644 index b4fead1..0000000 --- a/bareos/fuse/node/client.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.backups import Backups -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.jobslist import JobsList - -class Client(Directory): - def __init__(self, root, name): - super(Client, self).__init__(root, name) - - @classmethod - def get_id(cls, name): - return name - - def do_update(self): - self.add_subnode(Backups, "backups", client=self.name) - self.add_subnode(JobsList, "jobs", "client=%s" % (self.name)) diff --git a/bareos/fuse/node/clients.py b/bareos/fuse/node/clients.py deleted file mode 100644 index e8618e0..0000000 --- a/bareos/fuse/node/clients.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.client import Client -from bareos.fuse.node.directory import Directory - -class Clients(Directory): - def __init__(self, root, name): - super(Clients, self).__init__(root, name) - - @classmethod - def get_id(cls, name): - return "unique" - - def do_update(self): - data = self.bsock.call(".clients") - clients = data['clients'] - for i in clients: - name = i['name'] - self.add_subnode(Client, name) diff --git a/bareos/fuse/node/directory.py b/bareos/fuse/node/directory.py deleted file mode 100644 index 672b9cb..0000000 --- a/bareos/fuse/node/directory.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -""" - -from bareos.fuse.node.base import Base -import errno -import logging -from pprint import pformat -import stat - -class Directory(Base): - """ - Directory node. - """ - def __init__(self, root, name): - super(Directory, self).__init__(root, name) - self.defaultdirs = [ '.', '..' ] - self.stat.st_mode = stat.S_IFDIR | 0o755 - self.stat.st_nlink = len(self.defaultdirs) - # arbitrary default value - self.stat.st_size = 4096 - - def readdir(self, path, offset): - self.logger.debug("%s(\"%s\")" % (str(self.name), str(path))) - # copy default dirs - if path.len() == 0: - self.update() - result = list(self.defaultdirs) + list(self.subnodes.keys()) - else: - if not (path.get(0) in self.subnodes): - self.update() - if path.get(0) in self.subnodes: - topdir = path.shift() - result = self.subnodes[topdir].readdir(path, offset) - return result - - def get_stat(self): - self.stat.st_nlink = len(self.defaultdirs) + self.subnode_count - return super(Directory, self).get_stat() diff --git a/bareos/fuse/node/file.py b/bareos/fuse/node/file.py deleted file mode 100644 index 5eddd94..0000000 --- a/bareos/fuse/node/file.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -""" - -from bareos.fuse.node.base import Base -import errno -import fuse -import logging -from pprint import pformat -import stat - -class File(Base): - """ - File node. - """ - def __init__(self, root, name, content = ""): - super(File, self).__init__(root, name) - self.content = content - self.stat.st_mode = stat.S_IFREG | 0o444 - self.stat.st_nlink = 1 - - def get_stat(self): - if self.content != None: - self.stat.st_size = len(self.content) - return super(File, self).get_stat() diff --git a/bareos/fuse/node/job.py b/bareos/fuse/node/job.py deleted file mode 100644 index a9eee36..0000000 --- a/bareos/fuse/node/job.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.bvfsdir import BvfsDir -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.file import File -from bareos.fuse.node.joblog import JobLog -import errno -import logging -from pprint import pformat -import stat - -class Job(Directory): - def __init__(self, root, job): - self.job = job - super(Job, self).__init__(root, self.get_name(job)) - try: - if 'client' not in job: - job['client'] = job['clientname'] - except KeyError: - pass - try: - self.stat.st_ctime = self._convert_date_bareos_unix(self.job['starttime']) - except KeyError: - pass - - try: - endtime = self.job['realendtime'] - except KeyError: - pass - if not endtime: - try: - endtime = self.job['endtime'] - except KeyError: - pass - self.stat.st_mtime = self._convert_date_bareos_unix(endtime) - - if job['jobstatus'] == 'T' or job['jobstatus'] == 'E' or job['jobstatus'] == 'W' or job['jobstatus'] == 'f': - self.set_static() - - @classmethod - def get_id(cls, job): - return job['jobid'] - - def do_get_name(self, job): - try: - name = "jobid={jobid}_name={name}_client={client}_level={level}_status={jobstatus}".format(**job) - except KeyError: - try: - name = "jobid={jobid}_name={name}_client={clientname}_level={level}_status={jobstatus}".format(**job) - except KeyError: - name = "jobid={jobid}_level={level}_status={jobstatus}".format(**job) - return name - - def do_update_stat(self): - self.subnode_count = 3 - - def do_update(self): - self.add_subnode(File, name="info.txt", content = pformat(self.job) + "\n") - self.add_subnode(JobLog, name="job.log", job=self.job) - if self.job['jobstatus'] != 'f': - self.add_subnode(BvfsDir, "data", self, None) diff --git a/bareos/fuse/node/joblog.py b/bareos/fuse/node/joblog.py deleted file mode 100644 index ce2e32d..0000000 --- a/bareos/fuse/node/joblog.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.file import File - -class JobLog(File): - def __init__(self, root, name, job): - super(JobLog, self).__init__(root, name) - self.job = job - if job['jobstatus'] == 'T' or job['jobstatus'] == 'E' or job['jobstatus'] == 'W': - self.set_static() - - @classmethod - def get_id(cls, name, job): - return job['jobid'] - - def do_update(self): - jobid = self.job['jobid'] - data = self.bsock.call( "llist joblog jobid=%s" % (jobid) ) - self.content = "" - for i in data['joblog']: - self.content += str(i['time']) + " " - try: - self.content += str(i['logtext']) - except KeyError: - # some entries don't have logtext - pass - except UnicodeEncodeError as e: - self.logger.error("failed to convert logtext to string: %s" % (str(e))) - self.content += "BAREOFSFS SKIPPED: converting error\n" diff --git a/bareos/fuse/node/jobs.py b/bareos/fuse/node/jobs.py deleted file mode 100644 index d820ceb..0000000 --- a/bareos/fuse/node/jobs.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.jobslist import JobsList -from bareos.fuse.node.jobsname import JobsName - -class Jobs(Directory): - def __init__(self, root, name): - super(Jobs, self).__init__(root, name) - self.static = True - - @classmethod - def get_id(cls, name): - return "unique" - - def do_update(self): - data = self.bsock.call(".jobs") - jobs = data['jobs'] - for i in jobs: - self.add_subnode(JobsName, i['name']) - self.add_subnode(JobsList, "all") - self.add_subnode(JobsList, "running", "jobstatus=running") - self.add_subnode(JobsList, "each_jobname_last_run", "last") diff --git a/bareos/fuse/node/jobslist.py b/bareos/fuse/node/jobslist.py deleted file mode 100644 index 38b9188..0000000 --- a/bareos/fuse/node/jobslist.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.job import Job - -class JobsList(Directory): - def __init__(self, root, name, selector = ''): - super(JobsList, self).__init__(root, name) - self.selector = selector - - @classmethod - def get_id(cls, name, selector = ''): - return selector - - def do_update_stat(self): - data = self.bsock.call("llist jobs %s count" % (self.selector)) - self.subnode_count = int(data['jobs'][0]['count']) - - def do_update(self): - data = self.bsock.call("llist jobs %s" % (self.selector)) - jobs = data['jobs'] - for i in jobs: - self.add_subnode(Job, i) diff --git a/bareos/fuse/node/jobsname.py b/bareos/fuse/node/jobsname.py deleted file mode 100644 index bba0c20..0000000 --- a/bareos/fuse/node/jobsname.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.job import Job - -class JobsName(Directory): - def __init__(self, root, name): - super(JobsName, self).__init__(root, "job="+str(name)) - self.jobname=name - - @classmethod - def get_id(cls, name): - return name - - def do_update_stat(self): - data = self.bsock.call("llist job=%s count" % (self.jobname)) - self.subnode_count = int(data['jobs'][0]['count']) - - def do_update(self): - data = self.bsock.call("llist job=%s" % (self.jobname)) - jobs = data['jobs'] - for i in jobs: - self.add_subnode(Job, i) diff --git a/bareos/fuse/node/pool.py b/bareos/fuse/node/pool.py deleted file mode 100644 index 725f818..0000000 --- a/bareos/fuse/node/pool.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.file import File -from bareos.fuse.node.volumelist import VolumeList -from pprint import pformat - -class Pool(Directory): - def __init__(self, root, name, pool): - super(Pool, self).__init__(root, name) - self.pool = pool - - @classmethod - def get_id(cls, name, pool): - return name - - def do_update_stat(self): - self.subnode_count = 2 - - def do_update(self): - self.add_subnode(File, name="info.txt", content=pformat(self.pool) + "\n") - self.add_subnode(VolumeList, name="volumes", selector="pool=%s" % (self.name)) diff --git a/bareos/fuse/node/pools.py b/bareos/fuse/node/pools.py deleted file mode 100644 index 56626a5..0000000 --- a/bareos/fuse/node/pools.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.pool import Pool - -class Pools(Directory): - def __init__(self, root, name): - super(Pools, self).__init__(root, name) - self.static = True - - @classmethod - def get_id(cls, name): - return "unique" - - def do_update(self): - data = self.bsock.call("llist pools") - pools = data['pools'] - for i in pools: - self.add_subnode(Pool, i['name'], i) diff --git a/bareos/fuse/node/status.py b/bareos/fuse/node/status.py deleted file mode 100644 index b2acd6a..0000000 --- a/bareos/fuse/node/status.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.file import File -from datetime import datetime, timedelta - - -class Status(File): - def __init__(self, root, name): - super(Status, self).__init__(root, name) - self.cache_timeout = timedelta(seconds=0) - self.cache_stat_timeout = timedelta(seconds=60) - self.objects = 0 - - - @classmethod - def get_id(cls, name): - return "unique" - - def do_update(self): - self.objects = 0 - self.content = "#\n" - self.content += "# bareosfs cache\n" - self.content += "#\n" - self.content += self.get_status(None, self.root, -1) - self.content += "#\n" - self.content += "# number objects: %i\n" % (self.objects) - self.content += "#\n" - - - def get_status(self, name, node, indent): - result = "" - if name: - result = " " * (indent*2) + "%s\n" % (name) - self.objects += 1 - for i in sorted(node.subnodes.keys()): - result += self.get_status(i, node.subnodes[i], indent+1) - return result diff --git a/bareos/fuse/node/volume.py b/bareos/fuse/node/volume.py deleted file mode 100644 index e57215f..0000000 --- a/bareos/fuse/node/volume.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.file import File -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.jobslist import JobsList -from bareos.fuse.node.volumestatus import VolumeStatus -from pprint import pformat - -class Volume(Directory): - def __init__(self, root, name, volume): - super(Volume, self).__init__(root, name) - self.volume = volume - - @classmethod - def get_id(cls, name, volume): - return volume['volumename'] - - def do_update_stat(self): - self.subnode_count = 3 - - def do_update(self): - self.add_subnode(File, name="info.txt", content=pformat(self.volume) + "\n") - self.add_subnode(JobsList, name="jobs", selector="volume=%s" % (self.name)) - self.add_subnode(VolumeStatus, "status=", volume=self.volume) diff --git a/bareos/fuse/node/volumelist.py b/bareos/fuse/node/volumelist.py deleted file mode 100644 index 37d4e83..0000000 --- a/bareos/fuse/node/volumelist.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.directory import Directory -from bareos.fuse.node.volume import Volume -from bareos.fuse.node.volumestatus import VolumeStatus - -class VolumeList(Directory): - def __init__(self, root, name, selector = 'all'): - super(VolumeList, self).__init__(root, name) - self.selector = selector - - @classmethod - def get_id(cls, name, selector = 'all'): - return selector - - def do_update(self): - data = self.bsock.call("llist volumes %s" % (self.selector)) - volumes = data['volumes'] - for i in volumes: - volumename = i['volumename'] - self.add_subnode(Volume, name=volumename, volume=i) - self.add_subnode(VolumeStatus, "%s=" % (volumename), volume=i) diff --git a/bareos/fuse/node/volumestatus.py b/bareos/fuse/node/volumestatus.py deleted file mode 100644 index aa1eb62..0000000 --- a/bareos/fuse/node/volumestatus.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Bareos specific Fuse node. -""" - -from bareos.fuse.node.file import File -import stat - -class VolumeStatus(File): - - __volstatus2filemode = { - "Archive": 0o440, - # rw - "Append": 0o660, - # ro - "Full": 0o440, - "Used": 0o440, - # to be used - "Recycle": 0o100, - "Purged": 0o100, - # not to use - "Cleaning": 0000, - "Error": 0000, - } - - def __init__(self, root, basename, volume): - super(VolumeStatus, self).__init__(root, None, None) - self.basename = basename - self.volume = volume - self.do_update_stat() - self.set_name( "%s%s" % (self.basename, self.volume['volstatus']) ) - - @classmethod - def get_id(cls, basename, volume): - return "%s%s" % (basename, volume['mediaid']) - - def do_get_name(self, basename, volume): - return "%s%s" % (basename, volume['volstatus']) - - def do_update(self): - volumename = self.volume['volumename'] - data = self.bsock.call( "llist volume=%s" % (volumename) ) - self.volume = data['volume'] - self.do_update_stat() - - def do_update_stat(self): - try: - self.set_name( "status=%s" % (self.volume['volstatus']) ) - self.stat.st_size = int(self.volume['volbytes']) - self.stat.st_atime = self._convert_date_bareos_unix(self.volume['labeldate']) - #self.stat.st_ctime = stat['ctime'] - self.stat.st_mtime = self._convert_date_bareos_unix(self.volume['lastwritten']) - except KeyError as e: - self.logger.warning(str(e)) - pass - - # set mode dependend on if volume is in apppend mode or full - volstatus = self.volume['volstatus'] - if volstatus in self.__volstatus2filemode: - self.stat.st_mode = stat.S_IFREG | self.__volstatus2filemode[volstatus] - else: - self.logger.warning( "volume status %s unknown" % (self.volume['volstatus']) ) - self.stat.st_mode = stat.S_IFREG | 0000 diff --git a/bareos/fuse/nodefactory.py b/bareos/fuse/nodefactory.py deleted file mode 100644 index 64eccec..0000000 --- a/bareos/fuse/nodefactory.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -NodeFactory -""" - -from bareos.fuse.node import * - -class NodeFactory(object): - """ - Creates instances of nodes, when required. directory. - """ - def __init__(self, root): - self.logger = logging.getLogger() - self.root = root - self.instances = {} - - def get_instance(self, classtype, *args, **kwargs): - classname = classtype.__name__ - if not self.instances.has_key(classname): - self.instances[classname] = {} - id = classtype.get_id(*args, **kwargs) - if not id: - self.logger.debug(classname + ": no id defined. Creating without storing") - instance = classtype(self.root, *args, **kwargs) - #self.instances[classname][id] = instance - else: - if self.instances[classname].has_key(id): - self.logger.debug(classname + ": reusing " + id) - instance = self.instances[classname][id] - else: - self.logger.debug(classname + ": creating " + id) - instance = classtype(self.root, *args, **kwargs) - self.instances[classname][id] = instance - return instance diff --git a/bareos/fuse/root.py b/bareos/fuse/root.py deleted file mode 100644 index 1136388..0000000 --- a/bareos/fuse/root.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -bareosfs root node (top level directory) -""" - -import bareos.fuse.exceptions -from bareos.fuse.nodefactory import NodeFactory -from bareos.fuse.node.directory import Directory -from bareos.fuse.node import * - -class Root(Directory): - """ - Define filesystem structure of root (/) directory. - """ - def __init__(self, bsock, restoreclient, restorejob, restorepath): - self.bsock = bsock - self.restoreclient = restoreclient - if restoreclient: - data = self.bsock.call(".clients") - if not restoreclient in [item['name'] for item in data['clients']]: - raise bareos.fuse.RestoreClientUnknown(restoreclient) - self.restorejob = restorejob - if restorejob: - data = self.bsock.call(".jobs") - if not restorejob in [item['name'] for item in data['jobs']]: - raise bareos.fuse.RestoreJobUnknown(restorejob) - self.restorepath = restorepath - super(Root, self).__init__(self, None) - self.factory = NodeFactory(self) - self.add_subnode(Jobs, "jobs") - self.add_subnode(VolumeList, "volumes") - self.add_subnode(Pools, "pools") - self.add_subnode(Clients, "clients") - self.add_subnode(Status, ".bareosfs-status.txt") diff --git a/debian/control b/debian/control index a716695..7c296e7 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: python-bareos Maintainer: Joerg Steffens <joerg.steffens@bareos.com> Section: python Priority: optional -Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 7.4.3) +Build-Depends: dh-python, debhelper (>= 7.4.3), python-all (>= 2.6.6-3), python-setuptools (>= 0.6b3) Standards-Version: 3.9.1 Package: python-bareos diff --git a/debian/rules b/debian/rules index 9ef7b8b..b4fa425 100755 --- a/debian/rules +++ b/debian/rules @@ -1,7 +1,7 @@ #!/usr/bin/make -f -# This file was automatically generated by stdeb 0.6.0+git at -# Mon, 07 Sep 2015 16:46:19 +0200 +export PYBUILD_NAME = bareos %: - dh $@ --with python2 --buildsystem=python_distutils + dh $@ --with python2 --buildsystem=pybuild + diff --git a/packaging/python-bareos.changes b/packaging/python-bareos.changes index b96f3eb..1620a26 100644 --- a/packaging/python-bareos.changes +++ b/packaging/python-bareos.changes @@ -1,3 +1,9 @@ +python-bareos (0.4.0-1) unstable; urgency=low + + * move python module bareos.fuse from python-bareos to bareos-fuse. + + -- Joerg Steffens <joerg.steffens@bareos.com> Tue, 26 Jun 2017 11:45:40 +0200 + python-bareos (0.3.0-1) unstable; urgency=low * initial diff --git a/packaging/python-bareos.dsc b/packaging/python-bareos.dsc index 1137e82..96cfc4a 100644 --- a/packaging/python-bareos.dsc +++ b/packaging/python-bareos.dsc @@ -2,9 +2,9 @@ Format: 1.0 Source: python-bareos Binary: python-bareos Architecture: all -Version: 0.3-1 +Version: 0.4-1 Maintainer: Joerg Steffens <joerg.steffens@bareos.com> Standards-Version: 3.9.1 -Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 7.4.3) +Build-Depends: dh-python, debhelper (>= 7.4.3), python-all (>= 2.6.6-3), python-setuptools (>= 0.6b3) DEBTRANSFORM-RELEASE: 1 Files: diff --git a/packaging/python-bareos.spec b/packaging/python-bareos.spec index 3a4dfa5..7403ff1 100644 --- a/packaging/python-bareos.spec +++ b/packaging/python-bareos.spec @@ -18,7 +18,7 @@ %global pyXcmd python%{PYVER} Name: python-%{srcname} -Version: 0.3 +Version: 0.4 Release: 1%{?dist} Summary: Backup Archiving REcovery Open Sourced - Python module Group: Productivity/Archiving/Backup @@ -9,7 +9,7 @@ setup( author_email='joerg.steffens@bareos.com', packages=find_packages(), scripts=['bin/bconsole.py', 'bin/bconsole-json.py', 'bin/bareos-fd-connect.py'], - url='https://github.com/joergsteffens/python-bareos/', + url='https://github.com/bareos/python-bareos/', # What does your project relate to? keywords='bareos', description='Network socket connection to the Bareos backup system.', @@ -20,3 +20,4 @@ setup( 'python-dateutil', ] ) + |