Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/builtin plugins only #107

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cffi/cdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct ly_ctx;
#define LY_CTX_SET_PRIV_PARSED ...
#define LY_CTX_LEAFREF_EXTENDED ...
#define LY_CTX_LEAFREF_LINKING ...
#define LY_CTX_BUILTIN_PLUGINS_ONLY ...


typedef enum {
Expand Down Expand Up @@ -264,6 +265,7 @@ struct lyd_node {
LY_ERR lys_set_implemented(struct lys_module *, const char **);

#define LYD_NEW_VAL_OUTPUT ...
#define LYD_NEW_VAL_STORE_ONLY ...
#define LYD_NEW_VAL_BIN ...
#define LYD_NEW_VAL_CANON ...
#define LYD_NEW_META_CLEAR_DFLT ...
Expand Down Expand Up @@ -898,9 +900,13 @@ struct lyd_value {
...;
};

struct lyd_value_union {
struct lyd_value value;
...;
};

const char * lyd_get_value(const struct lyd_node *);
struct lyd_node* lyd_child(const struct lyd_node *);
LY_ERR lyd_value_validate(const struct ly_ctx *, const struct lysc_node *, const char *, size_t, const struct lyd_node *, const struct lysc_type **, const char **);
LY_ERR lyd_find_path(const struct lyd_node *, const char *, ly_bool, struct lyd_node **);
void lyd_free_siblings(struct lyd_node *);
struct lyd_node* lyd_first_sibling(const struct lyd_node *);
Expand Down
14 changes: 13 additions & 1 deletion libyang/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def __init__(
explicit_compile: Optional[bool] = False,
leafref_extended: bool = False,
leafref_linking: bool = False,
builtin_plugins_only: bool = False,
yanglib_path: Optional[str] = None,
yanglib_fmt: str = "json",
cdata=None, # C type: "struct ly_ctx *"
Expand All @@ -50,6 +51,8 @@ def __init__(
options |= lib.LY_CTX_LEAFREF_EXTENDED
if leafref_linking:
options |= lib.LY_CTX_LEAFREF_LINKING
if builtin_plugins_only:
options |= lib.LY_CTX_BUILTIN_PLUGINS_ONLY
# force priv parsed
options |= lib.LY_CTX_SET_PRIV_PARSED

Expand Down Expand Up @@ -244,6 +247,7 @@ def create_data_path(
parent: Optional[DNode] = None,
value: Any = None,
update: bool = True,
store_only: bool = False,
rpc_output: bool = False,
force_return_value: bool = True,
) -> Optional[DNode]:
Expand All @@ -254,7 +258,9 @@ def create_data_path(
value = str(value).lower()
elif not isinstance(value, str):
value = str(value)
flags = newval_flags(update=update, rpc_output=rpc_output)
flags = newval_flags(
update=update, store_only=store_only, rpc_output=rpc_output
)
dnode = ffi.new("struct lyd_node **")
ret = lib.lyd_new_path(
parent.cdata if parent else ffi.NULL,
Expand Down Expand Up @@ -348,6 +354,7 @@ def parse_data(
strict: bool = False,
validate_present: bool = False,
validate_multi_error: bool = False,
store_only: bool = False,
) -> Optional[DNode]:
if self.cdata is None:
raise RuntimeError("context already destroyed")
Expand All @@ -358,6 +365,7 @@ def parse_data(
opaq=opaq,
ordered=ordered,
strict=strict,
store_only=store_only,
)
validation_flgs = validation_flags(
no_state=no_state,
Expand Down Expand Up @@ -415,6 +423,7 @@ def parse_data_mem(
strict: bool = False,
validate_present: bool = False,
validate_multi_error: bool = False,
store_only: bool = False,
) -> Optional[DNode]:
return self.parse_data(
fmt,
Expand All @@ -429,6 +438,7 @@ def parse_data_mem(
strict=strict,
validate_present=validate_present,
validate_multi_error=validate_multi_error,
store_only=store_only,
)

def parse_data_file(
Expand All @@ -444,6 +454,7 @@ def parse_data_file(
strict: bool = False,
validate_present: bool = False,
validate_multi_error: bool = False,
store_only: bool = False,
) -> Optional[DNode]:
return self.parse_data(
fmt,
Expand All @@ -458,6 +469,7 @@ def parse_data_file(
strict=strict,
validate_present=validate_present,
validate_multi_error=validate_multi_error,
store_only=store_only,
)

def __iter__(self) -> Iterator[Module]:
Expand Down
81 changes: 47 additions & 34 deletions libyang/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def data_format(fmt_string: str) -> int:
# -------------------------------------------------------------------------------------
def newval_flags(
rpc_output: bool = False,
store_only: bool = False,
bin_value: bool = False,
canon_value: bool = False,
meta_clear_default: bool = False,
Expand All @@ -91,6 +92,8 @@ def newval_flags(
flags = 0
if rpc_output:
flags |= lib.LYD_NEW_VAL_OUTPUT
if store_only:
flags |= lib.LYD_NEW_VAL_STORE_ONLY
if bin_value:
flags |= lib.LYD_NEW_VAL_BIN
if canon_value:
Expand All @@ -112,6 +115,7 @@ def parser_flags(
opaq: bool = False,
ordered: bool = False,
strict: bool = False,
store_only: bool = False,
) -> int:
flags = 0
if lyb_mod_update:
Expand All @@ -126,6 +130,8 @@ def parser_flags(
flags |= lib.LYD_PARSE_ORDERED
if strict:
flags |= lib.LYD_PARSE_STRICT
if store_only:
flags |= lib.LYD_PARSE_STORE_ONLY
return flags


Expand Down Expand Up @@ -314,8 +320,10 @@ def meta_free(self, name):
break
item = item.next

def new_meta(self, name: str, value: str, clear_dflt: bool = False):
flags = newval_flags(meta_clear_default=clear_dflt)
def new_meta(
self, name: str, value: str, clear_dflt: bool = False, store_only: bool = False
):
flags = newval_flags(meta_clear_default=clear_dflt, store_only=store_only)
ret = lib.lyd_new_meta(
ffi.NULL,
self.cdata,
Expand Down Expand Up @@ -391,13 +399,15 @@ def new_path(
opt_opaq: bool = False,
opt_bin_value: bool = False,
opt_canon_value: bool = False,
opt_store_only: bool = False,
):
flags = newval_flags(
update=opt_update,
rpc_output=opt_output,
opaq=opt_opaq,
bin_value=opt_bin_value,
canon_value=opt_canon_value,
store_only=opt_store_only,
)
ret = lib.lyd_new_path(
self.cdata, ffi.NULL, str2c(path), str2c(value), flags, ffi.NULL
Expand Down Expand Up @@ -1062,9 +1072,10 @@ def create_path(
path: str,
value: Any = None,
rpc_output: bool = False,
store_only: bool = False,
) -> Optional[DNode]:
return self.context.create_data_path(
path, parent=self, value=value, rpc_output=rpc_output
path, parent=self, value=value, rpc_output=rpc_output, store_only=store_only
)

def children(self, no_keys=False) -> Iterator[DNode]:
Expand Down Expand Up @@ -1108,38 +1119,37 @@ def cdata_leaf_value(cdata, context: "libyang.Context" = None) -> Any:
return None

val = c2str(val)
term_node = ffi.cast("struct lyd_node_term *", cdata)
val_type = ffi.new("const struct lysc_type **", ffi.NULL)

# get real value type
ctx = context.cdata if context else ffi.NULL
ret = lib.lyd_value_validate(
ctx,
term_node.schema,
str2c(val),
len(val),
ffi.NULL,
val_type,
ffi.NULL,
)

if ret in (lib.LY_SUCCESS, lib.LY_EINCOMPLETE):
val_type = val_type[0].basetype
if val_type in Type.STR_TYPES:
return val
if val_type in Type.NUM_TYPES:
return int(val)
if val_type == Type.BOOL:
return val == "true"
if val_type == Type.DEC64:
return float(val)
if val_type == Type.LEAFREF:
return DLeaf.cdata_leaf_value(cdata.value.leafref, context)
if val_type == Type.EMPTY:
return None
if cdata.schema == ffi.NULL:
# opaq node
return val

raise TypeError("value type validation error")
if cdata.schema.nodetype == SNode.LEAF:
snode = ffi.cast("struct lysc_node_leaf *", cdata.schema)
elif cdata.schema.nodetype == SNode.LEAFLIST:
snode = ffi.cast("struct lysc_node_leaflist *", cdata.schema)

# find the real type used
cdata = ffi.cast("struct lyd_node_term *", cdata)
curr_type = snode.type
while curr_type.basetype in (Type.LEAFREF, Type.UNION):
if curr_type.basetype == Type.LEAFREF:
curr_type = cdata.value.realtype
if curr_type.basetype == Type.UNION:
curr_type = cdata.value.subvalue.value.realtype

val_type = curr_type.basetype
if val_type in Type.STR_TYPES:
return val
if val_type in Type.NUM_TYPES:
return int(val)
if val_type == Type.BOOL:
return val == "true"
if val_type == Type.DEC64:
return float(val)
if val_type == Type.EMPTY:
return None
return val


# -------------------------------------------------------------------------------------
Expand Down Expand Up @@ -1188,6 +1198,7 @@ def dict_to_dnode(
rpc: bool = False,
rpcreply: bool = False,
notification: bool = False,
store_only: bool = False,
) -> Optional[DNode]:
"""
Convert a python dictionary to a DNode object given a YANG module object. The return
Expand All @@ -1214,6 +1225,8 @@ def dict_to_dnode(
Data represents RPC or action output parameters.
:arg notification:
Data represents notification parameters.
:arg store_only:
Data are being stored regardless of type validation (length, range, pattern, etc.)
"""
if not dic:
return None
Expand All @@ -1235,7 +1248,7 @@ def _create_leaf(_parent, module, name, value, in_rpc_output=False):
value = str(value)

n = ffi.new("struct lyd_node **")
flags = newval_flags(rpc_output=in_rpc_output)
flags = newval_flags(rpc_output=in_rpc_output, store_only=store_only)
ret = lib.lyd_new_term(
_parent,
module.cdata,
Expand Down Expand Up @@ -1273,7 +1286,7 @@ def _create_container(_parent, module, name, in_rpc_output=False):

def _create_list(_parent, module, name, key_values, in_rpc_output=False):
n = ffi.new("struct lyd_node **")
flags = newval_flags(rpc_output=in_rpc_output)
flags = newval_flags(rpc_output=in_rpc_output, store_only=store_only)
ret = lib.lyd_new_list(
_parent,
module.cdata,
Expand Down
20 changes: 20 additions & 0 deletions tests/test_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) 2020 6WIND S.A.
# SPDX-License-Identifier: MIT

import gc
import json
import os
import unittest
Expand Down Expand Up @@ -1092,3 +1093,22 @@ def test_dnode_leafref_linking(self):
self.assertIsInstance(dnode4, DLeaf)
self.assertEqual(dnode4.cdata, dnode2.cdata)
dnode1.free()

def test_dnode_store_only(self):
MAIN = {"yolo-nodetypes:test1": 50}
module = self.ctx.load_module("yolo-nodetypes")
dnode = dict_to_dnode(MAIN, module, None, validate=False, store_only=True)
self.assertIsInstance(dnode, DLeaf)
self.assertEqual(dnode.value(), 50)
dnode.free()

def test_dnode_builtin_plugins_only(self):
MAIN = {"yolo-nodetypes:ip-address": "test"}
self.tearDown()
gc.collect()
self.ctx = Context(YANG_DIR, builtin_plugins_only=True)
module = self.ctx.load_module("yolo-nodetypes")
dnode = dict_to_dnode(MAIN, module, None, validate=False, store_only=True)
self.assertIsInstance(dnode, DLeaf)
self.assertEqual(dnode.value(), "test")
dnode.free()
13 changes: 12 additions & 1 deletion tests/yang/yolo/yolo-nodetypes.yang
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ module yolo-nodetypes {
namespace "urn:yang:yolo:nodetypes";
prefix sys;

import ietf-inet-types {
prefix inet;
revision-date 2013-07-15;
}

description
"YOLO Nodetypes.";

Expand Down Expand Up @@ -91,7 +96,9 @@ module yolo-nodetypes {
}

leaf test1 {
type uint8;
type uint8 {
range "2..20";
}
}

grouping grp1 {
Expand Down Expand Up @@ -124,4 +131,8 @@ module yolo-nodetypes {
anydata any1 {
when "../cont2";
}

leaf ip-address {
type inet:ipv4-address;
}
}
Loading