Ejemplo n.º 1
0
 def free(self, with_siblings: bool = True) -> None:
     try:
         if with_siblings:
             lib.lyd_free_withsiblings(self.cdata)
         else:
             lib.lyd_free(self.cdata)
     finally:
         self.cdata = None
Ejemplo n.º 2
0
 def free(self, with_siblings=True):
     try:
         if with_siblings:
             lib.lyd_free_withsiblings(self._node)
         else:
             lib.lyd_free(self._node)
     finally:
         self._node = None
Ejemplo n.º 3
0
def dict_to_dnode(
    dic: Dict[str, Any],
    module: Module,
    parent: Optional[DNode] = None,
    data: bool = False,
    config: bool = False,
    get: bool = False,
    getconfig: bool = False,
    edit: bool = False,
    rpc: bool = False,
    rpcreply: bool = False,
    strict: bool = False,
    no_yanglib: bool = False,
    validate: bool = True,
) -> Optional[DNode]:
    """
    Convert a python dictionary to a DNode object given a YANG module object. The return
    value is the first created node. If parent is not set, a top-level node is returned.

    :arg dic:
        The python dictionary to convert.
    :arg module:
        The libyang Module object associated with the dictionary.
    :arg parent:
        Optional parent to update. If not specified a new top-level DNode will be
        created.
    :arg data:
        Complete datastore content with configuration as well as state data. To handle
        possibly missing (but by default required) ietf-yang-library data, use
        no_yanglib=True.
    :arg config:
        Complete datastore without state data.
    :arg get:
        Data content from a reply message to the NETCONF <get> operation.
    :arg getconfig:
        Data content from a reply message to the NETCONF <get-config> operation.
    :arg edit:
        Content of the NETCONF <edit-config> config element.
    :arg rpc:
        Data represents RPC or action input parameters.
    :arg rpcreply:
        Data represents RPC or action output parameters.
    :arg strict:
        Instead of ignoring (with a warning message) data without schema definition,
        raise an error.
    :arg no_yanglib:
        Ignore (possibly) missing ietf-yang-library data. Applicable only with
        data=True.
    :arg validate:
        If False, do not validate the modified tree before returning. The validation is
        performed on the top of the data tree.
    """
    if not dic:
        return None

    if not isinstance(dic, dict):
        raise TypeError("dic argument must be a python dict")
    if not isinstance(module, Module):
        raise TypeError("module argument must be a Module object")
    if parent is not None and not isinstance(parent, DNode):
        raise TypeError("parent argument must be a DNode object or None")

    created = []

    def _create_leaf(_parent, module, name, value, in_rpc_output=False):
        if value is not None:
            if isinstance(value, bool):
                value = str(value).lower()
            elif not isinstance(value, str):
                value = str(value)
        if in_rpc_output:
            n = lib.lyd_new_output_leaf(_parent, module.cdata, str2c(name),
                                        str2c(value))
        else:
            n = lib.lyd_new_leaf(_parent, module.cdata, str2c(name),
                                 str2c(value))
        if not n:
            if _parent:
                parent_path = repr(DNode.new(module.context, _parent).path())
            else:
                parent_path = "module %r" % module.name()
            raise module.context.error(
                "failed to create leaf %r as a child of %s", name, parent_path)
        created.append(n)

    def _create_container(_parent, module, name, in_rpc_output=False):
        if in_rpc_output:
            n = lib.lyd_new_output(_parent, module.cdata, str2c(name))
        else:
            n = lib.lyd_new(_parent, module.cdata, str2c(name))
        if not n:
            if _parent:
                parent_path = repr(DNode.new(module.context, _parent).path())
            else:
                parent_path = "module %r" % module.name()
            raise module.context.error(
                "failed to create container/list/rpc %r as a child of %s",
                name,
                parent_path,
            )
        created.append(n)
        return n

    schema_cache = {}

    def _find_schema(schema_parent, name, prefix):
        cache_key = (schema_parent.cdata, name, prefix)
        snode, module = schema_cache.get(cache_key, (None, None))
        if snode is not None:
            return snode, module
        if isinstance(schema_parent, SRpc):
            if rpc:
                schema_parent = schema_parent.input()
            elif rpcreply:
                schema_parent = schema_parent.output()
            else:
                raise ValueError("rpc or rpcreply must be specified")
            if schema_parent is None:
                # there may not be any input or any output node in the rpc
                return None, None
        for s in schema_parent:
            if s.name() != name:
                continue
            mod = s.module()
            if prefix is not None and mod.name() != prefix:
                continue
            snode = s
            module = mod
            break
        schema_cache[cache_key] = (snode, module)
        return snode, module

    keys_cache = {}

    def _dic_keys(_dic, _schema):
        if isinstance(_schema, SList):
            # list keys must be first and in the order specified in the schema
            list_keys = keys_cache.get(_schema.cdata, None)
            if list_keys is None:
                list_keys = tuple(k.name() for k in _schema.keys())
                keys_cache[_schema.cdata] = list_keys
            keys = []
            for k in list_keys:
                if k in _dic:
                    keys.append(k)
            keys.extend(_dic.keys() - list_keys)
            return keys
        return _dic.keys()

    def _to_dnode(_dic, _schema, _parent=ffi.NULL, in_rpc_output=False):
        for key in _dic_keys(_dic, _schema):
            if ":" in key:
                prefix, name = name.split(":")
            else:
                prefix, name = None, key

            s, module = _find_schema(_schema, name, prefix)
            if not s:
                if isinstance(_schema, Module):
                    path = _schema.name()
                elif isinstance(_schema, SNode):
                    path = _schema.schema_path()
                else:
                    path = str(_schema)
                if strict:
                    raise LibyangError("%s: unknown element %r" % (path, key))
                LOG.warning("%s: skipping unknown element %r", path, key)
                continue

            value = _dic[key]

            if isinstance(s, SLeaf):
                _create_leaf(_parent, module, name, value, in_rpc_output)

            elif isinstance(s, SLeafList):
                if not isinstance(value, (list, tuple)):
                    raise TypeError(
                        "%s: python value is not a list/tuple: %r" %
                        (s.schema_path(), value))
                for v in value:
                    _create_leaf(_parent, module, name, v, in_rpc_output)

            elif isinstance(s, SRpc):
                n = _create_container(_parent, module, name, in_rpc_output)
                _to_dnode(value, s, n, rpcreply)

            elif isinstance(s, SContainer):
                n = _create_container(_parent, module, name, in_rpc_output)
                _to_dnode(value, s, n, in_rpc_output)

            elif isinstance(s, SList):
                if not isinstance(value, (list, tuple)):
                    raise TypeError(
                        "%s: python value is not a list/tuple: %r" %
                        (s.schema_path(), value))
                for v in value:
                    if not isinstance(v, dict):
                        raise TypeError("%s: list element is not a dict: %r" %
                                        (_schema.schema_path(), v))
                    n = _create_container(_parent, module, name, in_rpc_output)
                    _to_dnode(v, s, n, in_rpc_output)

    result = None

    try:
        if parent is not None:
            _parent = parent.cdata
            _schema_parent = parent.schema()
        else:
            _parent = ffi.NULL
            _schema_parent = module
        _to_dnode(
            dic,
            _schema_parent,
            _parent,
            in_rpc_output=rpcreply and isinstance(parent, DRpc),
        )
        if created:
            result = DNode.new(module.context, created[0])
            if validate:
                result.root().validate(
                    data=data,
                    config=config,
                    get=get,
                    getconfig=getconfig,
                    edit=edit,
                    rpc=rpc,
                    rpcreply=rpcreply,
                    no_yanglib=no_yanglib,
                )
    except:
        for c in reversed(created):
            lib.lyd_free(c)
        raise

    return result