Beispiel #1
0
    def _get_ses_slot(self, enclosure, element):
        enclosure_id = enclosure["id"]
        slot = element["slot"]
        if enclosure_id.startswith("mapped_enclosure_"):
            enclosure_id = element["original"]["enclosure_id"]
            slot = element["original"]["slot"]

        ses_enclosures = self.__get_enclosures()
        ses_enclosure = ses_enclosures.get_by_encid(enclosure_id)
        if ses_enclosure is None:
            raise MatchNotFound()
        ses_slot = ses_enclosure.get_by_slot(slot)
        if ses_slot is None:
            raise MatchNotFound()
        return ses_slot
Beispiel #2
0
    def _get_ses_slot(self, enclosure, element):
        if "original" in element:
            enclosure_id = element["original"]["enclosure_id"]
            slot = element["original"]["slot"]
        else:
            enclosure_id = enclosure["id"]
            slot = element["slot"]

        ses_enclosures = self.__get_enclosures()
        ses_enclosure = ses_enclosures.get_by_encid(enclosure_id)
        if ses_enclosure is None:
            raise MatchNotFound()
        ses_slot = ses_enclosure.get_by_slot(slot)
        if ses_slot is None:
            raise MatchNotFound()
        return ses_slot
Beispiel #3
0
    async def netconf(self, **kwargs):
        """
        wrapper for net(8) conf. This manages the share configuration, which is stored in
        samba's registry.tdb file.
        """
        action = kwargs.get('action')
        if action not in [
                'list', 'showshare', 'addshare', 'delshare', 'getparm',
                'setparm', 'delparm'
        ]:
            raise CallError(f'Action [{action}] is not permitted.',
                            errno.EPERM)

        ha_mode = SMBHAMODE[(await
                             self.middleware.call('smb.get_smb_ha_mode'))]
        if ha_mode == SMBHAMODE.CLUSTERED:
            ctdb_healthy = await self.middleware.call('ctdb.general.healthy')
            if not ctdb_healthy:
                raise CallError(
                    "Registry calls not permitted when ctdb unhealthy.",
                    errno.ENXIO)

        share = kwargs.get('share')
        args = kwargs.get('args', [])
        jsoncmd = kwargs.get('jsoncmd', False)
        if jsoncmd:
            cmd = [SMBCmd.NET.value, '--json', 'conf', action]
        else:
            cmd = [SMBCmd.NET.value, 'conf', action]

        if share:
            cmd.append(share)

        if args:
            cmd.extend(args)

        netconf = await run(cmd, check=False)
        if netconf.returncode != 0:
            # net_conf needs to be reworked to return errors consistently.
            if action != 'getparm':
                self.logger.trace('netconf failure for command [%s]: %s', cmd,
                                  netconf.stderr.decode())

            errmsg = netconf.stderr.decode().strip()
            if 'SBC_ERR_NO_SUCH_SERVICE' in errmsg or 'does not exist' in errmsg:
                svc = share if share else json.loads(args[0])['service']
                raise MatchNotFound(svc)

            raise CallError(
                f'net conf {action} [{cmd}] failed with error: {errmsg}')

        if jsoncmd:
            out = netconf.stdout.decode()
            if out:
                out = json.loads(out)
        else:
            out = netconf.stdout.decode()

        return out
Beispiel #4
0
    def _get_slot(self, slot_filter, enclosure_query=None):
        for enclosure in self.middleware.call_sync("enclosure.query", enclosure_query or []):
            try:
                elements = next(filter(lambda element: element["name"] == "Array Device Slot",
                                       enclosure["elements"]))["elements"]
                slot = next(filter(slot_filter, elements))
                return enclosure, slot
            except StopIteration:
                pass

        raise MatchNotFound()
Beispiel #5
0
    def _delete(self, tdb_handle, id):
        self.assert_tdb_type(tdb_handle.options['tdb_type'], ['CRUD'])

        tdb_key = f'{tdb_handle.name}_{id}'

        state = self._tdb_entries(tdb_handle)
        if not state['by_id'].get(id):
            raise MatchNotFound()

        tdb_handle.delete(tdb_key)
        tdb_handle.last_read = 0
        return
Beispiel #6
0
    def fetch(self, data):
        handle = self.get_connection(data['name'], data['tdb-options'])

        with closing(handle) as tdb_handle:
            tdb_val = self._get(tdb_handle, data['key'])

        if tdb_val is None:
            raise MatchNotFound(data['key'])

        if data['tdb-options']['data_type'] == 'JSON':
            data = json.loads(tdb_val)
        elif data['tdb-options']['data_type'] == 'STRING':
            data = tdb_val
        elif data['tdb-options']['data_type'] == 'BYTES':
            data = b64encode(tdb_val).decode()

        return data
Beispiel #7
0
    def _get_ses_slot_for_disk(self, disk):
        # This can also return SES slot for disk that is not present in the system
        try:
            enclosure, element = self._get_slot_for_disk(disk)
        except MatchNotFound:
            disk = self.middleware.call_sync(
                "disk.query",
                [["devname", "=", disk]],
                {"get": True, "extra": {"include_expired": True}, "order_by": ["expiretime"]},
            )
            if disk["enclosure"]:
                enclosure, element = self._get_slot(lambda element: element["slot"] == disk["enclosure"]["slot"],
                                                    [["number", "=", disk["enclosure"]["number"]]])
            else:
                raise MatchNotFound()

        return self._get_ses_slot(enclosure, element)
Beispiel #8
0
    def _update(self, tdb_handle, id, payload):
        self.assert_tdb_type(tdb_handle.options['tdb_type'], ['CRUD'])

        tdb_key = f'{tdb_handle.name}_{id}'
        vers = payload['version']
        new = payload['data']

        self._version_check(tdb_handle, vers)
        state = self._tdb_entries(tdb_handle)

        old = state['by_id'].get(id)
        if not old:
            raise MatchNotFound()

        old.update(new)
        old_id = old.pop('id')
        tdb_val = json.dumps(old)
        tdb_handle.store(tdb_key, tdb_val)
        tdb_handle.last_read = 0
        return old_id
Beispiel #9
0
def filter_list(_list, filters=None, options=None):

    opmap = {
        '=': lambda x, y: x == y,
        '!=': lambda x, y: x != y,
        '>': lambda x, y: x > y,
        '>=': lambda x, y: x >= y,
        '<': lambda x, y: x < y,
        '<=': lambda x, y: x <= y,
        '~': lambda x, y: re.match(y, x),
        'in': lambda x, y: x in y,
        'nin': lambda x, y: x not in y,
        'rin': lambda x, y: x is not None and y in x,
        'rnin': lambda x, y: x is not None and y not in x,
        '^': lambda x, y: x is not None and x.startswith(y),
        '!^': lambda x, y: x is not None and not x.startswith(y),
        '$': lambda x, y: x is not None and x.endswith(y),
        '!$': lambda x, y: x is not None and not x.endswith(y),
    }

    if filters is None:
        filters = {}
    if options is None:
        options = {}

    select = options.get('select')

    rv = []
    if filters:

        def filterop(f):
            if len(f) != 3:
                raise ValueError(f'Invalid filter {f}')
            name, op, value = f
            if op not in opmap:
                raise ValueError('Invalid operation: {}'.format(op))
            if isinstance(i, dict):
                source = get(i, name)
            else:
                source = getattr(i, name)
            if opmap[op](source, value):
                return True
            return False

        for i in _list:
            valid = True
            for f in filters:
                if len(f) == 2:
                    op, value = f
                    if op == 'OR':
                        for f in value:
                            if filterop(f):
                                break
                        else:
                            valid = False
                            break
                    else:
                        raise ValueError(f'Invalid operation: {op}')
                elif not filterop(f):
                    valid = False
                    break

            if not valid:
                continue
            if select:
                entry = {}
                for s in select:
                    if s in i:
                        entry[s] = i[s]
            else:
                entry = i
            rv.append(entry)
            if options.get('get') is True:
                return entry
    elif select:
        rv = []
        for i in _list:
            entry = {}
            for s in select:
                if s in i:
                    entry[s] = i[s]
            rv.append(entry)
    else:
        rv = _list

    if options.get('count') is True:
        return len(rv)

    if options.get('order_by'):
        for o in options.get('order_by'):
            if o.startswith('-'):
                o = o[1:]
                reverse = True
            else:
                reverse = False
            rv = sorted(rv, key=lambda x: x[o], reverse=reverse)

    if options.get('get') is True:
        try:
            return rv[0]
        except IndexError:
            raise MatchNotFound()

    if options.get('offset'):
        rv = rv[options['offset']:]

    if options.get('limit'):
        return rv[:options['limit']]

    return rv
Beispiel #10
0
    async def query(self, name, filters, options):
        """
        Query for items in a given collection `name`.

        `filters` is a list which each entry can be in one of the following formats:

            entry: simple_filter | conjuntion
            simple_filter: '[' attribute_name, OPERATOR, value ']'
            conjunction: '[' CONJUNCTION, '[' simple_filter (',' simple_filter)* ']]'

            OPERATOR: ('=' | '!=' | '>' | '>=' | '<' | '<=' | '~' | 'in' | 'nin')
            CONJUNCTION: 'OR'

        e.g.

        `['OR', [ ['username', '=', 'root' ], ['uid', '=', 0] ] ]`

        `[ ['username', '=', 'root' ] ]`

        .. examples(websocket)::

          Querying for username "root" and returning a single item:

            :::javascript
            {
              "id": "d51da71b-bb48-4b8b-a8f7-6046fcc892b4",
              "msg": "method",
              "method": "datastore.query",
              "params": ["account.bsdusers", [ ["username", "=", "root" ] ], {"get": true}]
            }
        """
        table = self._get_table(name)

        # We do not want to make changes to original options
        # which might happen with "prefix"
        options = options.copy()

        aliases = {}
        if options['count']:
            qs = select([func.count(self._get_pk(table))])
        else:
            columns = list(table.c)
            from_ = table
            if options['relationships']:
                aliases = self._get_queryset_joins(table)
                for foreign_key, alias in aliases.items():
                    columns.extend(list(alias.c))
                    from_ = from_.outerjoin(alias, alias.c[foreign_key.column.name] == foreign_key.parent)

            qs = select(columns).select_from(from_)

        prefix = options['prefix']

        if filters:
            qs = qs.where(and_(*self._filters_to_queryset(filters, table, prefix, aliases)))

        if options['count']:
            return (await self.middleware.call("datastore.fetchall", qs))[0][0]

        order_by = options['order_by']
        if order_by:
            # Do not change original order_by
            order_by = order_by[:]
            for i, order in enumerate(order_by):
                if order.startswith('nulls_first:'):
                    wrapper = nullsfirst
                    order = order[len('nulls_first:'):]
                elif order.startswith('nulls_last:'):
                    wrapper = nullslast
                    order = order[len('nulls_last:'):]
                else:
                    wrapper = lambda x: x  # noqa

                if order.startswith('-'):
                    order_by[i] = self._get_col(table, order[1:], prefix).desc()
                else:
                    order_by[i] = self._get_col(table, order, prefix)

                order_by[i] = wrapper(order_by[i])

            # FIXME: remove this after switching to SQLite 3.30
            changed = True
            while changed:
                changed = False
                for i, v in enumerate(order_by):
                    if isinstance(v, UnaryExpression) and v.modifier in (nullsfirst_op, nullslast_op):
                        if isinstance(v.element, UnaryExpression) and v.element.modifier == desc_op:
                            root_element = v.element.element
                        else:
                            root_element = v.element

                        order_by = order_by[:i] + [
                            {
                                nullsfirst_op: root_element != None,  # noqa
                                nullslast_op: root_element == None,  # noqa
                            }[v.modifier],
                            v.element,
                        ] + order_by[i + 1:]
                        changed = True
                        break

            qs = qs.order_by(*order_by)

        if options['offset']:
            qs = qs.offset(options['offset'])

        if options['limit']:
            qs = qs.limit(options['limit'])

        result = await self.middleware.call("datastore.fetchall", qs)

        relationships = [{} for row in result]
        if options['relationships']:
            # This will only fetch many-to-many relationships for primary table, not for joins, but that's enough
            relationships = await self._fetch_many_to_many(table, result)

        result = await self._queryset_serialize(
            result,
            table, aliases, relationships, options['extend'], options['extend_context'], options['prefix'],
            options['select'], options['extra'],
        )

        if options['get']:
            try:
                return result[0]
            except IndexError:
                raise MatchNotFound() from None

        return result
Beispiel #11
0
    def query(self, name, filters=None, options=None):
        """Query for items in a given collection `name`.

        `filters` is a list which each entry can be in one of the following formats:

            entry: simple_filter | conjuntion
            simple_filter: '[' attribute_name, OPERATOR, value ']'
            conjunction: '[' CONJUNTION, '[' simple_filter (',' simple_filter)* ']]'

            OPERATOR: ('=' | '!=' | '>' | '>=' | '<' | '<=' | '~' | 'in' | 'nin')
            CONJUNCTION: 'OR'

        e.g.

        `['OR', [ ['username', '=', 'root' ], ['uid', '=', 0] ] ]`

        `[ ['username', '=', 'root' ] ]`

        .. examples(websocket)::

          Querying for username "root" and returning a single item:

            :::javascript
            {
              "id": "d51da71b-bb48-4b8b-a8f7-6046fcc892b4",
              "msg": "method",
              "method": "datastore.query",
              "params": ["account.bsdusers", [ ["username", "=", "root" ] ], {"get": true}]
            }
        """
        model = self.__get_model(name)
        if options is None:
            options = {}
        else:
            # We do not want to make changes to original options
            # which might happen with "prefix"
            options = options.copy()

        qs = model.objects.all()

        extra = options.get('extra')
        if extra:
            qs = qs.extra(**extra)

        prefix = options.get('prefix')

        if filters:
            qs = qs.filter(*self._filters_to_queryset(filters, prefix))

        order_by = options.get('order_by')
        if order_by:
            if prefix:
                # Do not change original order_by
                order_by = order_by[:]
                for i, order in enumerate(order_by):
                    if order.startswith('-'):
                        order_by[i] = '-' + prefix + order[1:]
                    else:
                        order_by[i] = prefix + order
            qs = qs.order_by(*order_by)

        if options.get('count') is True:
            return qs.count()

        if options.get('offset'):
            qs = qs[options['offset']:]

        if options.get('limit'):
            qs = qs[:options['limit']]

        result = []
        for i in self.__queryset_serialize(
                qs,
                options.get('extend'),
                options.get('extend_context'),
                options.get('prefix'),
                options.get('select'),
        ):
            result.append(i)

        if options.get('get') is True:
            try:
                return result[0]
            except IndexError:
                raise MatchNotFound()

        return result
Beispiel #12
0
 async def object(self, name):
     try:
         return self.SERVICES[name]
     except KeyError:
         raise MatchNotFound(name) from None