Exemple #1
0
class VDFS(Operations):
    def __init__(self, query=None, router=None):
        self._events = {}
        self._results = {}
        self._query = query
        if not query:
            self._shadow = True
            manager = Manager()
            self._edge = Edge(core=manager.core)
            self._vrtx = Vrtx(core=manager.core)
            self._attr = Attr(core=manager.core)
            self._data = Data(self._vrtx, self._edge, self._attr, core=manager.core)

            from lib.link import Uplink
            self._link = Uplink(manager)
        else:
            manager = None
            self._shadow = False
            self._edge = Edge(router=router)
            self._vrtx = Vrtx(router=router)
            self._attr = Attr(router=router, rdonly=False)
            self._data = Data(self._vrtx, self._edge, self._attr, router=router, rdonly=False)

            from lib.link import Downlink
            link = Downlink(query)
            self._query.link = link
            self._link = link

        self._manager = manager
        self._lock = NamedLock()
        if manager:
            manager.start()

    def _log(self, text):
        if LOG_VDFS:
            log_debug(self, text)

    def _check_name(self, name):
        try:
            return uuid.UUID(name).hex
        except:
            return

    def _check_uid(self, uid):
        try:
            return uuid.UUID(uid).hex
        except:
            return

    def _parse(self, path):
        if len(path) > PATH_MAX:
            self._log('failed to parse')
            raise FuseOSError(EINVAL)

        if path == '/' or path[:2] == '/.':
            return (None, None, None)

        field = path[1:].split('/')
        uid = self._check_uid(field[0])
        if not uid:
            self._log('failed to parse, invalid uid, path=%s' % str(path))
            raise FuseOSError(EINVAL)

        name = ''
        total = len(field)
        if total == 1:
            obj = self._data
        else:
            if field[1] == FIELD_VRTX:
                obj = self._vrtx
            elif field[1] == FIELD_EDGE:
                obj = self._edge
            elif field[1] == FIELD_ATTR:
                obj = self._attr
            else:
                if total != 2:
                    self._log('failed to parse, invalid path, path=%s' % str(path))
                    raise FuseOSError(EINVAL)

                name = self._check_name(field[1])
                if not name:
                    self._log('failed to parse, invalid path, path=%s' % str(path))
                    raise FuseOSError(EINVAL)

                obj = self._data

            if total > 2:
                if obj == self._attr:
                    if total > 4:
                        self._log('failed to parse, invalid path, path=%s' % str(path))
                        raise FuseOSError(EINVAL)

                    name = self._check_name(field[2])
                    if not name:
                        self._log('failed to parse, invalid path, path=%s' % str(path))
                        raise FuseOSError(EINVAL)

                    if total == 4:
                        name = os.path.join(name, field[3])
                else:
                    name = self._check_name(field[-1])
                    if not name:
                        self._log('failed to parse, invalid path, path=%s' % str(path))
                        raise FuseOSError(EINVAL)

                    if total >= 4:
                        parent = self._check_name(field[-2])
                        name = os.path.join(parent, name)

        return (obj, uid, name)

    def _launch(self, func, **args):
        cnt = RETRY_MAX
        while cnt >= 0:
            try:
                ret = func(**args)
                if ret:
                    return ret
            except:
                pass
            cnt -= 1
            if cnt >= 0:
                time.sleep(RETRY_INTERVAL)

    def _init(self, uid, name, mode, vrtx, parent, freq, prof, hndl, filt, disp, typ, timeout):
        if prof:
            if not typ:
                typ = prof.get('type')
            elif typ != prof.get('type'):
                log_err(self, 'failed to initialize, invalid type, type=%' % str(typ))
                raise FuseOSError(EINVAL)

        if not typ:
            log_err(self, 'failed to initialize, no type, name=%s' % str(name))
            raise FuseOSError(EINVAL)

        link = mode & MODE_LINK
        if link:
            mode &= ~MODE_LINK

        if mode & MODE_CLONE and not parent:
            log_err(self, 'failed to initialize, no parent, name=%s' % str(name))
            raise FuseOSError(EINVAL)

        if not mode & MODE_VIRT:
            timeout = None

        if not prof:
            if self._shadow:
                driver = load_driver(typ)
                if not driver:
                    log_err(self, 'failed to initialize, cannot load driver %s, name=%s' % (typ, str(name)))
                    raise FuseOSError(EINVAL)

                if mode & MODE_CLONE:
                    mode = driver.get_mode() | MODE_CLONE
                else:
                    mode = driver.get_mode()
                freq = driver.get_freq()
                prof = driver.get_profile()
            else:
                prof = {'type':typ}

        self._data.initialize(uid, name)
        self._attr.initialize(uid, name, ATTR_MODE, mode)

        if freq:
            self._attr.initialize(uid, name, ATTR_FREQ, freq)

        if filt:
            self._attr.initialize(uid, name, ATTR_FILTER, filt)

        if hndl:
            self._attr.initialize(uid, name, ATTR_HANDLER, hndl)

        if prof:
            self._attr.initialize(uid, name, ATTR_PROFILE, prof)

        if disp:
            self._attr.initialize(uid, name, ATTR_DISPATCHER, disp)

        if timeout:
            self._attr.initialize(uid, name, ATTR_TIMEOUT, timeout)

        if mode & MODE_CLONE:
            self._attr.initialize(uid, name, ATTR_PARENT, parent)

        if vrtx:
            if mode & MODE_CLONE:
                log_err(self, 'failed to initialize, cannot set vertex for a cloned device, name=%s' % str(name))
                raise FuseOSError(EINVAL)

            self._vrtx.initialize(uid, name, vrtx)
            for v in vrtx:
                self._edge.initialize(uid, (v, name), hidden=True)

        if not self._shadow:
            if not link:
                if not self._launch(self._link.mount, uid=uid, name=name, mode=mode, vrtx=vrtx, typ=typ, parent=parent, timeout=timeout):
                    log_err(self, 'failed to initialize, cannot mount, name=%s' % str(name))
                    raise FuseOSError(EINVAL)
        else:
            if link and not mode & MODE_CLONE:
                self._manager.create(device_name(typ, name, mode), init=False)

    def _do_mount(self, uid, name, mode, vrtx, parent, freq=None, prof=None, hndl=None, filt=None, disp=None, typ=None, timeout=None):
        if not name:
            name = uuid.uuid4().hex

        if self._shadow and (mode == None or not mode & MODE_LINK):
            if mode != None:
                mode |= MODE_LINK

            if not self._launch(self._link.put, name=name, op=OP_ADD, mode=mode, freq=freq, prof=prof):
                log_err(self, 'failed to mount, link error, name=%s' % str(name))
                raise FuseOSError(EINVAL)

        if mode != None:
            self._init(uid, name, mode, vrtx, parent, freq, prof, hndl, filt, disp, typ, timeout)

        return name

    def _mount(self, path, value):
        if not value:
            log_err(self, 'failed to mount')
            raise FuseOSError(EINVAL)

        uid = self._get_uid(path)
        if not uid:
            log_err(self, 'failed to mount')
            raise FuseOSError(EINVAL)

        args = ast.literal_eval(value)
        if type(args) != dict:
            log_err(self, 'failed to mount')
            raise FuseOSError(EINVAL)

        typ = args.get('type')
        freq = args.get('freq')
        name = args.get('name')
        mode = args.get('mode')
        prof = args.get('prof')
        filt = args.get('filter')
        vrtx = args.get('vertex')
        hndl = args.get('handler')
        parent = args.get('parent')
        timeout = args.get('timeout')
        disp = args.get('dispatcher')

        if freq and float(freq) > FREQ_MAX:
            log_err(self, 'failed to mount, invalid frequency, name=%s' % str(name))
            raise FuseOSError(EINVAL)

        if prof and type(prof) != dict:
            log_err(self, 'failed to mount, invalid profile, name=%s' % str(name))
            raise FuseOSError(EINVAL)

        if vrtx:
            if type(vrtx) != list:
                log_err(self, 'failed to mount, invalid vertex, name=%s' % str(name))
                raise FuseOSError(EINVAL)

            for i in vrtx:
                if not self._check_uid(i):
                    log_err(self, 'failed to mount, name=%s' % str(name))
                    raise FuseOSError(EINVAL)

        if timeout and float(timeout) > TIMEOUT_MAX:
            log_err(self, 'failed to mount, invalid timeout, name=%s' % str(name))
            raise FuseOSError(EINVAL)

        self._do_mount(uid, name, mode, vrtx, parent, freq, prof, hndl, filt, disp, typ, timeout)

    def getattr(self, path, fh=None):
        obj, uid, name = self._parse(path)
        if not name:
            return STAT_DIR
        if not obj:
            log_err(self, 'failed to getattr, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        return obj.getattr(uid, name)

    def _check_file(self, obj, name, flags=0, create=False):
        if not self._shadow:
            if obj.can_invalidate():
                if create or obj.may_update(flags):
                    self._log('start to invalidate, name=%s' % name)
                    if not self._launch(self._link.put, name=obj.parent(name), op=OP_INVALIDATE, path=obj.real(name)):
                        log_err(self, 'failed to check, cannot invalidate %s' % str(name))
                        raise FuseOSError(EINVAL)
            elif obj.can_touch():
                self._log('start to touch, name=%s' % name)
                if not self._launch(self._link.put, name=obj.parent(name), op=OP_TOUCH, path=obj.real(name)):
                    log_err(self, 'failed to check, cannot touch %s' % str(name))
                    raise FuseOSError(EINVAL)

    @named_lock_lock
    def _do_create(self, path, obj, uid, name):
        return obj.create(uid, name)

    def create(self, path, mode):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to create, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._check_file(obj, name, create=True)
        self._log('create, path=%s' % path)
        return self._do_create(path, obj, uid, name)

    def _update(self, obj, uid, name):
        if self._shadow:
            if obj.is_expired(uid, name):
                buf = self._launch(self._link.put, name=obj.parent(name), op=OP_GET, field=obj.field, item=obj.child(name), buf=obj.signature(uid, name))
                if buf:
                    obj.patch(uid, name, buf)

    @named_lock_lock
    def _do_open(self, path, flags, obj, uid, name):
        self._update(obj, uid, name)
        return obj.open(uid, name, flags)

    def open(self, path, flags):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to open, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._check_file(obj, name, flags=flags)
        self._log('open, path=%s' % path)
        return self._do_open(path, flags, obj, uid, name)

    @named_lock_unlock
    def _do_release(self, path, fh, obj, uid, name):
        if obj.release(uid, name, fh):
            try:
                obj.commit(uid, name)
            except:
                obj.discard(uid, name)

    def release(self, path, fh):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to release, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._do_release(path, fh, obj, uid, name)
        self._log('release, path=%s' % path)

    @show_path
    def readdir(self, path, fh):
        obj, uid, name = self._parse(path)
        res = []
        if obj:
            res = obj.readdir(uid, name)
        return res

    def write(self, path, buf, offset, fh):
        os.lseek(fh, offset, 0)
        return os.write(fh, buf)

    def read(self, path, size, offset, fh):
        os.lseek(fh, offset, 0)
        return os.read(fh, size)

    def _do_unlink(self, obj, uid, name):
        if not self._shadow:
            if obj.can_invalidate() or obj.can_unlink():
                if not self._launch(self._link.put, name=obj.parent(name), op=OP_INVALIDATE, path=obj.real(name)):
                    log_err(self, 'failed to unlink, link error, name=%s' % str(name))
                    raise FuseOSError(EINVAL)

    @show_path
    def unlink(self, path):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to unlink, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._do_unlink(obj, uid, name)
        obj.unlink(uid, name)

    def _create_device(self, path, op, attr):
        if self._shadow:
            return

        uid = self._get_uid(path)
        if not uid:
            log_err(self, 'failed to create device, no uid')
            raise FuseOSError(EINVAL)

        args = ast.literal_eval(attr)
        if type(args) != dict:
            log_err(self, 'failed to create device, invalid attr')
            raise FuseOSError(EINVAL)

        typ = args.get('type')
        mode = args.get('mode')
        parent = args.get('parent')
        timeout = args.get('timeout')

        if None == mode:
            mode = MODE_VIRT

        if op == OP_CLONE:
            if not parent:
                log_err(self, 'failed to create device, no parent')
                raise FuseOSError(EINVAL)

            prof = Loader(uid).get_profile(parent)
            typ = prof['type']
            mode |= MODE_CLONE

        if not typ:
            typ = VDEV

        vrtx = args.get('vertex')
        if vrtx:
            if mode & MODE_CLONE:
                log_err(self, 'failed to create device, invalid mode')
                raise FuseOSError(EINVAL)

            if type(vrtx) != list:
                log_err(self, 'failed to create device, invalid vertex')
                raise FuseOSError(EINVAL)

            for i in vrtx:
                if not self._check_uid(i):
                    log_err(self, 'failed to create device, invalid vertex')
                    raise FuseOSError(EINVAL)

        if parent and not self._check_uid(parent):
            log_err(self, 'failed to create device, invalid parent, parent=%s' % str(parent))
            raise FuseOSError(EINVAL)

        return self._do_mount(uid, None, mode, vrtx, parent, typ=typ, timeout=timeout)

    def _do_enable(self, obj, uid, name):
        if not self._shadow:
            if obj.can_enable():
                if not self._launch(self._link.put, name=name, op=OP_ENABLE, path=name):
                    log_err(self, 'failed to enable, link error, name=%s' % str(name))
                    raise FuseOSError(EINVAL)

    def _enable(self, path):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to enable, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._do_enable(obj, uid, name)

    def _do_disable(self, obj, uid, name):
        if not self._shadow:
            if obj.can_disable():
                if not self._launch(self._link.put, name=name, op=OP_DISABLE, path=name):
                    log_err(self, 'failed to disable, link error, name=%s' % str(name))
                    raise FuseOSError(EINVAL)

    def _disable(self, path):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to disable, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._do_disable(obj, uid, name)

    def _join(self, path, target):
        if not self._shadow:
            return
        obj, _, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to join, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._manager.guest.join(name, target)

    def _accept(self, path, target):
        if not self._shadow:
            return
        obj, _, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to accept, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        self._manager.guest.accept(name, target)

    @named_lock
    def _invalidate(self, path):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to invalidate, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        obj.invalidate(uid, name)

    def setxattr(self, path, name, value, options, position=0):
        if name == OP_INVALIDATE:
            self._invalidate(path)
        elif name == OP_MOUNT:
            self._mount(path, value)
        elif name == OP_ENABLE:
            self._enable(path)
        elif name == OP_DISABLE:
            self._disable(path)
        elif name == OP_JOIN:
            self._join(path, value)
        elif name == OP_ACCEPT:
            self._accept(path, value)

    @named_lock
    def _get_event(self, uid):
        ret = self._events.get(uid)
        if ret:
            del self._events[uid]
            return ret
        else:
            return ''

    @named_lock
    def _set_event(self, uid, event):
        self._events[uid] = event

    def _scan(self, path):
        result = ''
        if not HISTORY or self._shadow:
            return result
        obj, uid, name = self._parse(path)
        if not obj or not obj.can_scan():
            return result
        if name:
            result = self._query.history.get(uid, name)
        return result

    def _get_uid(self, path):
        if len(path) > PATH_MAX:
            log_err(self, 'failed to get uid')
            raise FuseOSError(EINVAL)
        if path[0] != '/':
            return
        field = path[1:].split('/')
        if field[0]:
            return self._check_uid(field[0])

    def _poll(self, path):
        event = ''
        if self._shadow:
            return event
        uid = self._get_uid(path)
        if uid:
            event = self._get_event(uid)
            if not event:
                event = self._query.event.get(uid)
                self._set_event(uid, event)
        return event

    @named_lock
    def _get_result(self, path, op):
        if self._results.has_key(op):
            ret = self._results[op].get(path)
            if ret:
                del self._results[op][path]
                return ret
        return ''

    @named_lock
    def _set_result(self, path, op, result):
        if self._results.has_key(op):
            self._results[op].update({path:result})
        else:
            self._results.update({op:{path:result}})

    def getxattr(self, path, name, position=0):
        if name.startswith(OP_POLL):
            op = OP_POLL
        elif name.startswith(OP_SCAN):
            op = OP_SCAN
        elif name.startswith(OP_CLONE):
            op = OP_CLONE
        elif name.startswith(OP_CREATE):
            op = OP_CREATE
        elif name.startswith(OP_COMBINE):
            op = OP_COMBINE
        else:
            return ''
        res = self._get_result(path, op)
        if res:
            return res
        if op == OP_POLL:
            res = self._poll(path)
        elif op == OP_SCAN:
            res = self._scan(path)
        elif op == OP_CLONE:
            res = self._create_device(path, OP_CLONE, name[len(OP_CLONE) + 1:])
        elif op == OP_CREATE:
            res = self._create_device(path, OP_CREATE, name[len(OP_CREATE) + 1:])
        elif op == OP_COMBINE:
            res = self._create_device(path, OP_COMBINE, name[len(OP_COMBINE) + 1:])
        self._set_result(path, op, res)
        return res

    @show_path
    def truncate(self, path, length, fh=None):
        if fh:
            os.ftruncate(fh, length)
        else:
            obj, uid, name = self._parse(path)
            if not obj:
                log_err(self, 'failed to truncate, no object, name=%s' % str(name))
                raise FuseOSError(EINVAL)
            obj.truncate(uid, name, length)

    @show_path
    def readlink(self, path):
        obj, uid, name = self._parse(path)
        if not obj:
            log_err(self, 'failed to read link, no object, name=%s' % str(name))
            raise FuseOSError(EINVAL)
        return obj.readlink(uid, name)