Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bareos/python-bareos.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoerg Steffens <joerg.steffens@bareos.com>2015-09-24 20:35:17 +0300
committerJoerg Steffens <joerg.steffens@bareos.com>2015-09-27 18:32:28 +0300
commit54c03c0cb90d43f6e26d483114eaaf4dd1ca6439 (patch)
tree47056850c00474dba78eb331867819cea991b8af
parent2240426e91472380abd8f611980cc2bc3595beab (diff)
implement restore for the bareos.fuse
-rw-r--r--bareos/fuse/bareosfuse.py83
-rw-r--r--bareos/fuse/node/base.py142
-rw-r--r--bareos/fuse/node/bvfscommon.py67
-rw-r--r--bareos/fuse/node/bvfsdir.py21
-rw-r--r--bareos/fuse/node/bvfsfile.py80
-rw-r--r--bareos/fuse/node/directory.py2
-rw-r--r--bareos/fuse/root.py4
7 files changed, 350 insertions, 49 deletions
diff --git a/bareos/fuse/bareosfuse.py b/bareos/fuse/bareosfuse.py
index aa23733..cda79a1 100644
--- a/bareos/fuse/bareosfuse.py
+++ b/bareos/fuse/bareosfuse.py
@@ -46,36 +46,97 @@ class BareosFuse(fuse.Fuse):
self.logger.debug( "%s: %s" %(i, getattr(self,i)))
parameter[i] = getattr(self,i)
else:
- self.logger.debug( "%s: missing" %(i))
+ self.logger.debug( "%s: missing, default: %s" %(i, str(getattr(self,i,None))))
self.logger.debug('options: %s' % (parameter))
- password = bareos.bsock.Password(self.password)
- parameter['password']=password
+ if not hasattr(self, 'password'):
+ raise RuntimeError("missing parameter password")
+ else:
+ password = bareos.bsock.Password(self.password)
+ parameter['password']=password
self.bsock = bareos.bsock.BSockJson(**parameter)
- self.bareos = Root(self.bsock)
+ if not hasattr(self, 'restoreclient'):
+ self.restoreclient = None
+ #raise RuntimeError("missing parameter restoreclient")
+ # TODO: check if restoreclient is valid
+ if not hasattr(self, 'restorepath'):
+ self.restorepath = '/var/cache/bareosfs/'
+ self.bareos = Root(self.bsock, self.restoreclient, 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("fuse %s: nlink=%i" % (path, stat.st_nlink))
+ self.logger.debug("%s: nlink=%i" % (path, stat.st_nlink))
else:
- self.logger.debug("fuse %s: (int) %i" % (path, stat))
+ 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))
+ self.logger.debug("%s" % (path))
+ return result
def readdir(self, path, offset):
entries = self.bareos.readdir(Path(path), offset)
- self.logger.debug("fuse %s: %s" % (path, entries))
+ 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 read(self, path, size, offset):
- result = self.bareos.read(Path(path), size, offset)
- self.logger.debug("fuse %s: %s" % (path, 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/node/base.py b/bareos/fuse/node/base.py
index 7b61fe0..f859b0b 100644
--- a/bareos/fuse/node/base.py
+++ b/bareos/fuse/node/base.py
@@ -18,8 +18,10 @@ class Base(object):
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()
@@ -40,6 +42,9 @@ class Base(object):
"""
return None
+ # Interface
+ # =========
+
def get_name(self):
return self.name
@@ -49,18 +54,6 @@ class Base(object):
def set_static(self, value=True):
self.static = value
- def getattr(self, path):
- self.logger.debug("%s(\"%s\")" % (str(self), str(path)))
- result = -errno.ENOENT
- if path.len() == 0:
- self.update()
- result = self.get_stat()
- else:
- if path.get(0) in self.subnodes:
- topdir = path.shift()
- result = self.subnodes[topdir].getattr(path)
- return result
-
def get_stat(self):
return self.stat
@@ -101,20 +94,6 @@ class Base(object):
#"mtime": 1441134679
#},
- 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 path.get(0) in self.subnodes:
- topdir = path.shift()
- result = self.subnodes[topdir].read(path, size, offset)
- return result
-
def add_subnode(self, classtype, *args, **kwargs):
instance = self.root.factory.get_instance(classtype, *args, **kwargs)
name = instance.get_name()
@@ -153,7 +132,114 @@ class Base(object):
# 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()
+ result = self.get_stat()
+ else:
+ 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 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 = 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]
+ 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
+ '''
+ #self.logger.debug("%s(\"%s\")" % (str(self), str(path)))
+ #result = -errno.EOPNOTSUPP
+ 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 = int(DateParser.parse(bareosdate).strftime("%s"))
- self.logger.debug( "unix timestamp: %d" % (unixtimestamp))
+ unixtimestamp = 0
+ try:
+ unixtimestamp = int(DateParser.parse(bareosdate).strftime("%s"))
+ self.logger.debug( "unix timestamp: %d" % (unixtimestamp))
+ except ValueError:
+ pass
return unixtimestamp
diff --git a/bareos/fuse/node/bvfscommon.py b/bareos/fuse/node/bvfscommon.py
new file mode 100644
index 0000000..ad84e86
--- /dev/null
+++ b/bareos/fuse/node/bvfscommon.py
@@ -0,0 +1,67 @@
+"""
+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': str(self.restorepathfull),
+ 'user.bareos.restored': 'no',
+ 'user.bareos.do_options': "mark | restore",
+ '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( "%s: start", self.get_name() )
+ 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))
+ restore = self.bsock.call(
+ 'restore file=?{bvfs_restore_id} restoreclient={restoreclient} where="{where}" yes'.format(
+ restoreclient = self.root.restoreclient,
+ 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( "%s: end", self.get_name() )
diff --git a/bareos/fuse/node/bvfsdir.py b/bareos/fuse/node/bvfsdir.py
index b4537bd..40b7cc6 100644
--- a/bareos/fuse/node/bvfsdir.py
+++ b/bareos/fuse/node/bvfsdir.py
@@ -3,20 +3,23 @@ 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):
+class BvfsDir(Directory, BvfsCommon):
def __init__(self, root, name, jobid, pathid, directory = None):
super(BvfsDir, self).__init__(root, name)
self.jobid = 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, jobid, pathid, directory = None):
@@ -27,6 +30,19 @@ class BvfsDir(Directory):
id = "pathid=%s" % (str(pathid))
return id
+ 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.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)
@@ -36,7 +52,7 @@ class BvfsDir(Directory):
pathid = i['pathid']
self.add_subnode(BvfsDir, name, self.jobid, pathid, i)
for i in files:
- self.add_subnode(BvfsFile, i)
+ self.add_subnode(BvfsFile, i, self.directory['fullpath'])
def get_directories(self, pathid):
if pathid == None:
@@ -52,6 +68,7 @@ class BvfsDir(Directory):
if i['name'] != "." and i['name'] != "..":
if i['name'] == "/":
self.pathid = i['pathid']
+ BvfsCommon.init(self, self.root, self.jobid, i['fullpath'], None, i['stat'])
else:
found = True
if not found:
diff --git a/bareos/fuse/node/bvfsfile.py b/bareos/fuse/node/bvfsfile.py
index 13e1191..06e7fc4 100644
--- a/bareos/fuse/node/bvfsfile.py
+++ b/bareos/fuse/node/bvfsfile.py
@@ -2,16 +2,84 @@
Bareos specific Fuse node.
"""
+from bareos.fuse.node.bvfscommon import BvfsCommon
from bareos.fuse.node.file import File
-import stat
+import errno
+import os
-class BvfsFile(File):
- def __init__(self, root, file):
+class BvfsFile(File, BvfsCommon):
+ def __init__(self, root, file, bvfspath):
super(BvfsFile, self).__init__(root, file['name'], content = None)
self.file = file
- self.static = True
- self.set_stat(self.file['stat'])
+ self.bvfspath = bvfspath
+ self.id = self.get_id(file, bvfspath)
+ BvfsCommon.init(self, self.root, self.file['jobid'], self.bvfspath, self.file['name'], self.file['stat'])
@classmethod
- def get_id(cls, file):
+ def get_id(cls, file, bvfspath):
return 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/directory.py b/bareos/fuse/node/directory.py
index 8438a3b..3559d0c 100644
--- a/bareos/fuse/node/directory.py
+++ b/bareos/fuse/node/directory.py
@@ -20,7 +20,7 @@ class Directory(Base):
self.stat.st_size = 4096
def readdir(self, path, offset):
- self.logger.debug("%s(\"%s\")" % (str(self), str(path)))
+ self.logger.debug("%s(\"%s\")" % (str(self.get_name()), str(path)))
# copy default dirs
if path.len() == 0:
self.update()
diff --git a/bareos/fuse/root.py b/bareos/fuse/root.py
index a6f8735..6cd7ec3 100644
--- a/bareos/fuse/root.py
+++ b/bareos/fuse/root.py
@@ -10,8 +10,10 @@ class Root(Directory):
"""
Define filesystem structure of root (/) directory.
"""
- def __init__(self, bsock):
+ def __init__(self, bsock, restoreclient, restorepath):
self.bsock = bsock
+ self.restoreclient = restoreclient
+ self.restorepath = restorepath
super(Root, self).__init__(self, None)
self.factory = NodeFactory(self)
self.add_subnode(Jobs, "jobs")