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
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
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
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()
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
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
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)
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
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
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
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
async def object(self, name): try: return self.SERVICES[name] except KeyError: raise MatchNotFound(name) from None