YANG Data Nodes
This section describes the basic design of a YANG data tree that represents instances of various object nodes that the client or the server can create.
Data Trees
A YumaPro data tree is a representation of some subset of all possible object instances that a server is maintaining within a configuration database or other structure.
Each data tree starts with a 'root' container, and any child nodes represent top-level YANG module data nodes that exist within the server.
Each configuration database maintains its own copy (and version) of the data tree. There is only one object tree, however, and all data trees use the same object tree for reference.
Not all object types have a corresponding node within a data tree. Only 'real' data nodes are present. Object nodes that are used as meta-data to organize the object tree (E.g., choice, augment) are not present. The following table lists the object types and whether each one is found in a data tree.
Object Types in the Data Tree
OBJ_TYP_ANYXML |
Yes |
OBJ_TYP_CONTAINER |
Yes |
OBJ_TYP_CONTAINER (ncx:root) |
Yes |
OBJ_TYP_LEAF |
Yes |
OBJ_TYP_LEAF_LIST |
Yes |
OBJ_TYP_LIST |
Yes |
OBJ_TYP_CHOICE |
No |
OBJ_TYP_CASE |
No |
OBJ_TYP_USES |
No |
OBJ_TYP_REFINE |
No |
OBJ_TYP_AUGMENT |
No |
OBJ_TYP_RPC |
No |
OBJ_TYP_RPCIO |
No |
OBJ_TYP_NOTIF |
No |
Child Data Nodes
The design used for the storage of child nodes has changed over time.
Old Design
New Design
The old design required the entire child list to be searched when adding a new entry.
The new design uses a queue of object headers. First the correct object header is found, then the instance of that object is found. YANG lists are now stored in an AVL tree (using libdict hb_tree).
The server will maintain the order or instances given in the <edit-config> or startup configuration. It will maintain YANG schema order so the objects will be returned in the correct order according to the YANG module.
The val_gen_index_chain function MUST be called after all key leafs have been added to a list entry and before the list entry is added to its parent.
Sometimes server or SIL code creates data. In order to create a list, the following steps must be followed
The following example illustrates how to construct the list data node with multiple children. See create_list_entry.
Create the list node, e.g., with val_new_value()
Create the key leafs and add them to the list entry with val_child_add
Create the list index chain with val_gen_index_chain
Add the list node to its parent with val_child_add
-
status_t val_child_add(val_value_t *child, val_value_t *parent)
Add a child value node to a parent value node.
Replaces val_add_child
- Parameters:
child -- node to store in the parent
parent -- complex value node with a child header Q
- Returns:
status; node is only added if status is NO_ERR
-
status_t val_child_add_force(val_value_t *child, val_value_t *parent)
-
status_t val_child_insert(val_value_t *child, val_value_t *current, val_value_t *parent, op_insertop_t insert_op)
Insert a child value node into a parent value node before or after the specified node.
- Parameters:
child -- node to store in the parent
current -- last node that was stored in the parent (may be NULL)
parent -- complex value node with a childQ
insert_op -- insert operation enum (first, last, before, after)
- Returns:
status
Data Node Types
The 'ncx_btype_t' enumeration in ncx/ncxtypes.h is used within
each val_value_t to quickly identify which variant of the data node
structure is being used.
ncx_btype_t
-
enum ncx_btype_t
enumeration of the built-in NCX types These types cannot be overridden and cannot be imported
Values:
-
enumerator NCX_BT_NONE
No type has been set yet.
The val_new_value() function has been called but no specific init function has been called to set the base type.
-
enumerator NCX_BT_ANY
The node is a YANG 1.0 'anyxml' node.
Type is deprecated. Use anydata instead.
When the client or server parses an 'anyxml' object, it will be converted to containers and strings.
This type should not be used directly.
WIll be converted to (or replaced by) a container
-
enumerator NCX_BT_BITS
YANG bits data type.
Uses ncx_list_t (string list) for implementation
-
enumerator NCX_BT_ENUM
YANG enumeration data type.
Uses enum name string in implementation
The 'value' number is not currently used
-
enumerator NCX_BT_EMPTY
YANG empty data type.
Uses boolean always set to true in implementation
If boolean set to false then may not mean not-present
-
enumerator NCX_BT_BOOLEAN
YANG boolean data type.
Uses boolean in implementation
-
enumerator NCX_BT_INT8
YANG int8 data type.
Uses int32 in implementation
-
enumerator NCX_BT_INT16
YANG int16 data type.
Uses int32 in implementation
-
enumerator NCX_BT_INT32
YANG int32 data type.
Uses int32 in implementation
-
enumerator NCX_BT_INT64
YANG int64 data type.
Uses int64 in implementation
-
enumerator NCX_BT_UINT8
YANG uint8 data type.
Uses int32 in implementation
-
enumerator NCX_BT_UINT16
YANG uint16 data type.
Uses int32 in implementation
-
enumerator NCX_BT_UINT32
YANG uint32 data type.
Uses int32 in implementation
-
enumerator NCX_BT_UINT64
YANG uint64 data type.
Uses uint64 in implementation
-
enumerator NCX_BT_DECIMAL64
YANG decimal64 data type.
Uses struct in implementation
-
enumerator NCX_BT_FLOAT64
Hidden double type, used just for XPath.
If the HAS_FLOAT define is false, then this type will be implemented as a string, not a double.
-
enumerator NCX_BT_STRING
YANG 'string' type.
There are also some YumaPro extensions that are used with this data type for special strings.
The server needs to know if a string contains XML prefixes or not, and there are several flavors to automatate processing of each one correctly.
-
enumerator NCX_BT_BINARY
YANG binary data type.
Uses struct in implementation
YANG uses base64 encoding for binary strings
-
enumerator NCX_BT_INSTANCE_ID
YANG instance-identifier data type.
Uses xpath:1.0 string in implementation
-
enumerator NCX_BT_UNION
YANG 'union' data type.
This is a meta-type.
When the client or server parses a value, it will resolve the union to one of the data types defined within the union.
An unresolved union will use the string type
-
enumerator NCX_BT_LEAFREF
YANG 'leafref' data type.
This is a meta-type.
The client or server will resolve this data type to the type of the actual 'pointed-at' leaf that is being referenced.
-
enumerator NCX_BT_IDREF
YANG identityref data type.
Uses struct in implementation, pointer to ncx_identity_t
YANG uses qualified-name encoding for identityref strings
The standards are not clear how to encode identityref in all protocols
-
enumerator NCX_BT_SLIST
ncx:xsdlist extension (internal, deprecated)
uses ncx_list_t, not limited to string sub-type
supports deprecated ncx:xsdlist string
-
enumerator NCX_BT_CONTAINER
YANG container node.
Used internally to give the val_value_t a data type
-
enumerator NCX_BT_CHOICE
YANG choice.
This is a meta-type and placeholder.
It does not appear in the data tree.
Not really used!
-
enumerator NCX_BT_CASE
YANG case.
This is a meta-type and placeholder.
It does not appear in the data tree.
Not really used!
-
enumerator NCX_BT_LIST
YANG list instance node.
Used internally to give the val_value_t a data type
-
enumerator NCX_BT_ANYDATA
The node is a YANG 1.1 'anydata' node.
When the client or server parses an 'anydata' object, it will be converted to containers and strings.
This type should not be used directly.
WIll be converted to (or replaced by) a container
-
enumerator NCX_BT_EXTERN
Internal 'external' data type, used in server and yangcli-pro.
It indicates that the content is actually in an external file.
Not a real type, points fo file for contents
-
enumerator NCX_BT_INTERN
Internal 'buffer' data type, used in server and yangcli-pro.
The content is actually stored verbatim in an internal buffer.
Not a real type, string buffer for contents.
-
enumerator NCX_BT_NONE
The following table describes the different enumeration values:
YumaPro Data Types (ncx_btype_t)
NCX_BT_NONE |
No type has been set yet. The val_new_value() function has been called but no specific init function has been called to set the base type. |
NCX_BT_ANY |
The node is a YANG 'anyxml' node. When the client or server parses an 'anyxml' object, it will be converted to containers and strings. This type should not be used directly. |
NCX_BT_BITS |
YANG 'bits' data type |
NCX_BT_ENUM |
YANG 'enumeration' data type |
NCX_BT_EMPTY |
YANG 'empty' data type |
NCX_BT_BOOLEAN |
YANG 'boolean' data type |
NCX_BT_INT8 |
YANG 'int8' data type |
NCX_BT_INT16 |
YANG 'int16' data type |
NCX_BT_INT32 |
YANG 'int32' data type |
NCX_BT_INT64 |
YANG 'int64' data type |
NCX_BT_UINT8 |
YANG 'uint8' data type |
NCX_BT_UINT16 |
YANG 'uint16' data type |
NCX_BT_UINT32 |
YANG 'uint32' data type |
NCX_BT_UINT64 |
YANG 'uint64' data type |
NCX_BT_DECIMAL64 |
YANG 'decimal64' data type |
NCX_BT_FLOAT64 |
Hidden double type, used just for XPath. If the HAS_FLOAT #define is false, then this type will be implemented as a string, not a double. |
NCX_BT_STRING |
YANG 'string' type. There are also some YumaPro extensions that are used with this data type for special strings. The server needs to know if a string contains XML prefixes or not, and there are several flavors to automate processing of each one correctly. |
NCX_BT_BINARY |
YANG 'binary' data type |
NCX_BT_INSTANCE_ID |
YANG 'instance-identifier' data type |
NCX_BT_UNION |
YANG 'union' data type. This is a meta-type. When the client or server parses a value, it will resolve the union to one of the data types defined within the union. |
NCX_BT_LEAFREF |
YANG 'leafref' data type. This is a meta-type. The client or server will resolve this data type to the type of the actual 'pointed-at' leaf that is being referenced. |
NCX_BT_IDREF |
YANG 'identityref' data type |
NCX_BT_SLIST |
XSD list data type (ncx:xsdlist extension) |
NCX_BT_CONTAINER |
YANG container |
NCX_BT_CHOICE |
YANG choice. This is a meta-type and placeholder. It does not appear in the data tree. |
NCX_BT_CASE |
YANG case. This is a meta-type and placeholder. It does not appear in the data tree. |
NCX_BT_LIST |
YANG list |
NCX_BT_EXTERN |
Internal 'external' data type, used in yangcli-pro. It indicates that the content is actually in an external file. |
NCX_BT_INTERN |
Internal 'buffer' data type, used in yangcli-pro. The content is actually stored verbatim in an internal buffer. |
YumaPro Data Node Edit Variables (val_editvars_t)
There is a temporary data structure which is attached to a data node
while editing operations are in progress, called "val_editvars_t".
This structure is used by the functions in agt/agt_val.c to
manipulate the value tree nodes during an <edit-config>,
<copy-config>, <load-config>, or <commit> operation.
The SIL callback functions may wish to refer to the fields in this data structure. There is also a SIL cookie field to allow data to be transferred from one callback stage to the later stages. For example, if an edit operation caused the device instrumentation to reserve some memory, then this cookie could store that pointer.
The following typedef is used to define the val_editvars_t structure:
-
struct val_editvars_t
one set of edit-in-progress variables for one value node
Public Members
-
struct val_value_t_ *curparent
these fields are only used in modified values before they are actually added to the config database (TBD: move into struct) curparent == parent of curnode for merge
track the real parent
-
op_insertop_t insertop
YANG insert operation.
-
xmlChar *insertstr
saved value or key attr
-
struct xpath_pcb_t_ *insertxpcb
key attr for insert
-
struct val_value_t_ *insertval
back-ptr if before or after
-
val_insert_mode_t insert_mode
insert mode requested
-
uint8 silprio
2nd SIL priority for server
-
boolean operset
nc:operation here
-
void *pcookie
user pointer cookie
-
int icookie
user integer cookie
-
boolean is_move
TRUE if YPATCH MOVE operation.
-
boolean skip_sil_partial
TRUE if skip_sil_partial needed.
-
boolean deleted
TRUE if deleted from commit_deletes.
-
struct val_value_t_ *curparent
The following fields within the 'val_editvars_t' are highlighted:
val_editvars_t Fields
curparent |
A 'new' node will use this field to remember the parent of the 'current' value. This is needed to support the YANG insert operation. |
editop |
The effective edit operation for this node. |
insertop |
The YANG insert operation, if any. |
insertstr |
The YANG 'value' or 'key' attribute value string, used to support the YANG insert operation. |
insertxpcb |
XPath parser control block for the insert 'key' expression, if needed. Used to support the YANG insert operation. |
insertval |
Back pointer to the value node to insert ahead of, or behind, if needed. Used to support the 'before' and 'after' modes of the YANG insert operation. |
iskey |
TRUE if this is a key leaf. FALSE otherwise. |
operset |
TRUE if there was an nc:operation attribute found in this node; FALSE if the 'editop' is derived from its parent. |
pcookie |
SIL user pointer cookie. Not used by the server. Reserved for SIL callback code. |
icookie |
SIL user integer cookie. Not used by the server. Reserved for SIL callback code. |
val_value_t
The val_value_t data structure is used to maintain the internal representation of all NETCONF databases, non-configuration data available with the <get> operation, all RPC operation input and output parameters, and all notification contents.
Warning
Do not access fields directly! Use val.h macros or access functions in val.h and val_util.h
The following typedef is used to define a value node:
-
struct val_value_t
one value to match one type
Public Members
-
dlq_hdr_t qhdr
queue header to insert in dlq_hdr list
-
struct obj_template_t_ *obj
common fields
bptr to object def
-
typ_def_t *typdef
bptr to typdef if leaf
-
const xmlChar *name
back pointer to elname
the dname field is moved to
val_extra_t and only used when the value is constructed from dummy objects or no objects at all
-
struct val_value_t_ *parent
back-ptr to parent if any
-
struct val_child_hdr_t_ *hdr
back-ptr to own child_hdr
-
val_extra_t *val_extra
val extra fields only needed about 25% of the time
-
uint32 flags
internal status flags
-
xmlns_id_t nsid
namespace ID for this node
-
ncx_btype_t btyp
base type of this value
-
ncx_data_class_t dataclass
config or state data
-
time_t last_modified
last_modified and etag fields used for filtered retrieval and YANG-API If-Match type of conditional editing
-
ncx_etag_t etag
ETag for RESTCONF.
-
dlq_hdr_t *metaQ
YANG does not support user-defined meta-data but NCX does.
The <edit-config>, <get> and <get-config> operations use attributes in the RPC parameters, the metaQ is still used
The ncx:metadata extension allows optional attributes to be added to object nodes for anyxml, leaf, leaf-list, list, and container nodes. The config property will be inherited from the object that contains the metadata
This is used mostly for RPC input parameters and is strongly discouraged. Full edit-config support is not provided for metadata Q of val_value_t
-
val_editvars_t *editvars
value editing variables the editvars will only be malloced while edit is in progress
edit-vars from attrs
-
op_editop_t editop
needed for all edits
-
dlq_hdr_t *indexQ
this field used in NCX_BT_LIST only ??? when is it ncx_filptr_t ???
Q of val_index_t or ncx_filptr_t
-
ncx_owner_id_t owner_id
set if the owners are being stored and this is a data node in the running config
-
ncx_nmda_origin_t nmda_origin
NMDA origin enum.
-
uint32 flags2
flags2 to replace some individual booleans inst4ead of adding more booleans
-
union val_value_t::v_ v
-
union v_
flag if NCX_BT_EXTERN and file is entire contents
flag if NCX_BT_EXTERN and file is binary contents flag indicating value->obj was replaced because of the ywx:datapath attribute and the original object was anyxml. This is needed for get-bulk processing to parse the <data> element as the parent of the list-target. Will probably be invalid if this parent node is also a list union of all the NCX-specific sub-types note that the following invisible constructs should never show up in this struct:
Public Members
-
dlq_hdr_t child_hdrQ
complex types have a Q of val_value_t representing the child nodes with values
changed from Q of val_value_t to a Q of val_child_hdr_t
-
ncx_num_t num
Numeric data types:
-
ncx_str_t str
String data types:
-
val_idref_t idref
NCX_BT_IDREF.
-
ncx_binary_t binary
NCX_BT_BINARY.
-
ncx_list_t list
NCX_BT_BITS, NCX_BT_SLIST.
-
boolean boo
NCX_BT_EMPTY, NCX_BT_BOOLEAN.
-
ncx_enum_t enu
NCX_BT_UNION, NCX_BT_ENUM.
-
xmlChar *fname
NCX_BT_EXTERN.
-
xmlChar *intbuff
NCX_BT_INTERN.
-
dlq_hdr_t child_hdrQ
-
dlq_hdr_t qhdr
The following table highlights the fields in this data structure.
val_value_t Fields
qhdr |
Internal queue header to allow a value node to be stored in a queue. A complex node maintains a child queue of val_value_t nodes. |
obj |
Back pointer to the object template for this data node |
typdef |
Back pointer to the typedef structure if this is a leaf or leaf-list node. |
name |
Back pointer to the name string for this node |
parent |
Back pointer to the parent of this node, if any |
nsid |
Namespace ID for this node. This may not be the same as the object node namespace ID, E.g., anyxml child node contents will override the generic object namespace. |
btyp |
The ncx_btype_t base type enumeration for this node. This is the final resolved value, in the event the object type is not a final resolved base type. |
flags |
Internal flags field. Do not access directly. |
dataclass |
Internal config or non-config enumeration |
metaQ |
Queue of val_value_t structures that represent any meta-variables (XML attributes) found for this data node. For example, the NETCONF filter 'type' and 'select' attributes are defined for the <filter> element in yuma-netconf.yang. |
editvars |
Pointer to the malloced edit variables structure for this data node. This node will be freed (and NULL value) when the edit variables are not in use. |
res |
Internal validation result status for this node during editing or parsing. |
indexQ |
Queue of internal data structures used during parsing and filtering streamed output. |
val_extra |
Pointer to extra data if needed |
v |
Union of different internal fields, depending on the 'btyp' field value. |
v.childQ |
Queue of val_value_t child nodes, if this is a complex node. |
v.num |
ncx_num_t for all numeric data types |
v.str |
Malloced string value for the string data type |
v.idref |
Internal data structure for the YANG identityref data type |
v.binary |
Internal data structure for the YANG binary data type |
v.list |
Internal data structure for YANG bits and NCX xsdlist data types |
v.boo |
YANG boolean data type |
v.enu |
Internal data structure for YANG enumeration data type |
v.fname |
File name for NCX 'external' data type |
v.intbuff |
Malloced buffer for 'internal' data type |
YumaPro Data Nodes (Extra) (val_extra_t)
The val_extra_t data structure is used to maintain the fields that are internal and rarely used by the server have been moved from val_value_t to a new data structure called "val_extra_t".
The following typedef is used to define the extra data used for value node processing
-
struct val_extra_t
extra information not used very often within a val_value_t
Public Members
-
xmlChar *dname
malloced value name used rarely if obj_get_name not correct this can happen if val_value_t trees are constructed by SIL code from generic objects; This is NOT RECOMMENDED Use val_init_from_template using correct obj_template_t instead
malloced name if needed
-
void *getcb
Used by Agent only: GET1 callback for virtualval if this field is non-NULL, then the entire value node is actually a placeholder for a dynamic read-only object and all read access is done via this callback function; the real data type is getcb_fn_t *.
-
struct val_value_t_ *virtualval
if this field is non-NULL, then a malloced value struct representing the real value retrieved by val_get_virtual_value, is cached here for <get>/<get-config>
-
time_t cachetime
timestamp for virtual val timeout
-
struct xpath_pcb_t_ *xpathpcb
this field is for NCX_BT_LEAFREF NCX_BT_INSTANCE_ID, or tagged ncx:xpath value stored in v union as a string
-
plock_cb_t *plock[VAL_MAX_PLOCKS]
back-ptr to the partial locks that are held against this node
-
dlq_hdr_t *dataruleQ
back-ptr to the data access control rules that reference this node
Q of obj_xpath_ptr_t
-
xmlChar *varexpr
malloced pointer to the variable expression found if this val node is part of a data template.
The actual value in union v_ MUST be ignored if varexpr string is non-NULL!!
-
struct xpath_aio_cb_t_ *aiocb
Used by xpath_aio.c and agt_xpath.c to keep track of the parent of a top AIO val_value_ tree.
This is a backptr.
-
ncx_sm_mpid_t *mpid
malloced Moint Point ID struct if found in parsing
-
struct val_value_t_ *leafrefval
malloced leafref val_value - the final leafref is pointing at
-
typ_pattern_t *regex_filter
malloced pattern struct if the with_filter_regex feature is being used.
The pattern is cached in the filter leaf to save time when testing against each data node value
-
xmlChar *dname
The following table highlights the fields in this data structure:
Note: Do not access fields directly! Use val.h macros or access functions in val.h and val_util.h
val_extra_t Fields
dname |
Malloced name string if the client or server changed the name of this node, so the object node name is not being used. This is used for anyxml processing (and other things) to allow generic objects (container, string, empty, etc.) to be used to represent the contents of an 'anyxml' node. |
getcb |
Internal server callback function pointer. Used only if this is a 'virtual' node, and the actual value node contents are generated by a SIL callback function instead of being stored in the node itself. |
virtualval |
The temporary cached virtual node value, if the getcb pointer is non-NULL. |
cachetime |
The timestamp used to cache for a virtualval |
xpathpcb |
XPath parser control block, used if this value contains some sort of XPath string or instance-identifier. For example, the XML namespace ID mappings are stored, so the XML prefix map generated for the <rpc-reply> will contain and reuse the proper namespace attributes, as needed. |
plock |
Array of [1] for supporting <partial-lock> operation |
dataruleQ |
pointer to malloced queue for NACM data rule support |
varexpr |
malloced string containing variable string for replacement |
val_value_t Access Macros
There are a set of macros defined to access the fields within a val_value_t structure.
These should be used instead of accessing the fields directly. There are also functions defined as well. These macros are provided in addition to the access functions for quick access to the actual node value. These macros must only be used when the base type ('btyp') field has been properly set and known by the SIL code. Some auto-generated SIL code uses these macros.
The following table summarized the val_value_t macros that are
defined in ncx/val.h:
VAL_OBJ(V) |
Access object template of the value |
VAL_TYPE(V) |
Access base type of the value |
VAL_HDR(V) |
Access child header pointer of the value |
VAL_TYPDEF(V) |
Access type definition of the value |
VAL_NSID(V) |
Access namespace ID value |
VAL_NAME(V) |
Access name value |
VAL_RES(V) |
Access resource enumeration value |
VAL_BOOL(V) |
Access value for NCX_BT_BOOLEAN |
VAL_EMPTY(V) |
Access value for NCX_BT_EMPTY |
VAL_DOUBLE(V) |
Access value for NCX_BT_FLOAT64 |
VAL_STRING(V) |
Access value for NCX_BT_STRING |
VAL_BINARY(V) |
Access value for NCX_BT_BINARY |
VAL_ENU(V) |
Access entire ncx_enum_t structure for NCX_BT_ENUM |
VAL_ENUM(V) |
Access enumeration integer value for NCX_BT_ENUM |
VAL_ENUM_NAME(V) |
Access enumeration name string for NCX_BT_ENUM |
VAL_FLAG(V) |
Deprecated: use VAL_BOOL instead |
VAL_LONG(V) |
Access NCX_BT_INT64 value |
VAL_INT(V) |
Access NCX_BT_INT32 value |
VAL_INT8(V) |
Access NCX_BT_INT8 value |
VAL_INT16(V) |
Access NCX_BT_INT16 value |
VAL_INT32(V) |
Access NCX_BT_INT32 value |
VAL_INT64(V) |
Access NCX_BT_INT64 value |
VAL_STR(V) |
Deprecated: use VAL_STRING instead |
VAL_INSTANCE_ID(V) |
Access NCX_BT_INSTANCE_ID value |
VAL_IDREF(V) |
Access entire val_idref_t structure for NCX_BT_IDREF |
VAL_IDREF_NSID(V) |
Access the identityref namespace ID for NCX_BT_IDREF |
VAL_IDREF_NAME(V) |
Access the identityref name string for NCX_BT_IDREF |
VAL_UINT(V) |
Access the NCX_BT_UINT32 value |
VAL_UINT8(V) |
Access the NCX_BT_UINT8 value |
VAL_UINT16(V) |
Access the NCX_BT_UINT16 value |
VAL_UINT32(V) |
Access the NCX_BT_UINT32 value |
VAL_UINT64(V) |
Access the NCX_BT_UINT64 value |
VAL_ULONG(V) |
Access the NCX_BT_UINT64 value |
VAL_DEC64(V) |
Access the ncx_dec64 structure for NCX_BT_DEC64 |
VAL_DEC64_DIGITS(V) |
Access the number of digits for NCX_BT_DEC64 |
VAL_DEC64_ZEROES(V) |
Access the number of zeros for NCX_BT_DEC64 |
VAL_LIST(V) |
Access the ncx_list_t structure for NCX_BT_LIST |
VAL_BITS |
Access the ncx_list_t structure for NCX_BT_BITS. (Same as VAL_LIST) |
val_value_t Access Functions
The file ncx/val.h contains many API functions so that object
properties do not have to be accessed directly. In addition, the file
ncx/val_util.h contains more (high-level) utility functions. The
following table highlights the most commonly used functions. Refer to
the H files for a complete definition of each API function.
val_value_t Access Functions
val_new_value |
Malloc a new value node with type NCX_BT_NONE. |
val_init_complex |
Initialize a malloced value node as one of the complex data types. |
val_init_virtual |
Initialize a malloced value node as a virtual node (provide a 'get' callback function). |
val_init_from_template |
Initialize a malloced value node using an object template. This is the most common form of the init function used by SIL callback functions. |
val_free_value |
Clean and free a malloced value node. |
val_set_name |
Set or replace the value node name. |
val_force_dname |
Set (or reset) the name of a value struct . Set all descendant nodes as well Force dname to be used, not object name backptr. |
val_set_qname |
Set or replace the value node namespace ID and name. |
val_string_ok |
Check if the string value is valid for the value node object type. |
val_string_ok_errinfo |
Check if the string value is valid for the value node object type, and provide the error information to use if it is not OK. |
val_binary_ok_errinfo |
Retrieve the YANG custom error info if any. Check a binary string to make sure the value is valid base64 if the value came from raw RPC XML, and if its CLI input then do validation as for binary sting . Retrieve the configured error info struct if any error. |
val_list_ok |
Check if the list value is valid for the value node object type. |
val_list_ok_errinfo |
Check if the list value is valid for the value node object type, and provide the error information to use if it is not OK. |
val_enum_ok |
Check if the enumeration value is valid for the value node object type. |
val_enum_ok_errinfo |
Check if the enumeration value is valid for the value node object type, and provide the error information to use if it is not OK. |
val_bit_ok |
Check if the bits value is valid for the value node object type. |
val_idref_ok |
Check if the identityref value is valid for the value node object type. |
val_parse_idref |
Convert a string to an internal QName string into its various parts and find the identity struct that is being referenced (if available). |
val_range_ok |
Check a number to see if it is in range or not . Could be a number or size range. |
val_range_ok_errinfo |
Check a number to see if it is in range or not . Could be a number or size range. Returns error info record on error exit. |
val_pattern_ok |
Check a string against all the patterns in a big AND expression. |
val_pattern_ok_errinfo |
Check a string against all the patterns in a big AND expression. Returns error info record on error exit. |
val_simval_ok |
Check if the simple value is valid for the value node object type. |
val_simval_ok_errinfo |
Check if the simple value is valid for the value node object type, and provide the error information to use if it is not OK. |
val_union_ok |
Check a union to make sure the string is valid. |
val_union_ok_errinfo |
Check a union to make sure the string is valid. Returns error info record on error exit. |
val_union_ok_ex |
Check a union to make sure the string is valid based on the specified typdef. |
val_union_ok_full |
Check a union to make sure the string is valid based on the specified typdef, and convert the string to an NCX internal format . Use context and root nodes to evaluate XPath for leafref and instance-identifier types. |
val_get_metaQ |
Get the meta Q header for the value. |
val_get_first_meta |
Get the first meta-variable (XML attribute value) for a value node. |
val_get_next_meta |
Get the next meta-variable (XML attribute value) for a value node. |
val_meta_empty |
Check if the metaQ is empty for the value node. |
val_find_meta |
Find the specified meta-variable in a value node. |
val_meta_match |
Return true if the corresponding attribute exists and has the same value. |
val_metadata_inst_count |
Get the number of instances of the specified attribute. |
val_dump_value |
Debug function to print the contents of any value node. |
val_dump_value_ex |
Debug function to print the contents of any value node, with extended parameters to control the output. |
val_dump_value_max |
Debug function to print the contents of any value node, with full control of the output parameters. |
val_set_string |
Set a malloced value node as a generic string value. Used instead of val_init_from_template. |
val_set_string2 |
Set a malloced value node as a specified string type. Used instead of val_init_from_template. |
val_set_binary |
Set and decode base64 value . Set an initialized val_value_t as a binary type. |
val_reset_empty |
Recast an already initialized value as an NCX_BT_EMPTY clean a value and set it to empty type used by yangcli-pro to delete leafs. |
val_set_simval |
Set a malloced value node as a specified simple type. Used instead of val_init_from_template. |
val_make_simval |
Create and set a val_value_t as a simple type same as val_set_simval, but malloc the value first. |
val_set_simval_str |
Set a malloced value node as a specified simple type. Used instead of val_init_from_template. Use a counted string value instead of a zero-terminated string value. |
val_make_string |
Create a complete malloced generic string value node. |
val_merge |
Merge source val into destination value (! MUST be same type !) . Any meta vars in source are also merged into destination . This function is not used to merge complex objects For simple types only! |
val_clone |
Clone a value node. |
val_clone2 |
Clone a specified val_value_t structure and sub-trees but not the editvars. |
val_clone_test |
Clone a value node with a 'test' callback function to prune certain descendant nodes during the clone procedure. |
val_clone_config_data |
Clone a value node but skip all the non-configuration descendant nodes. |
val_replace |
Replace a specified val_value_t structure and sub-trees. This can be destructive to the source 'val' parameter ! |
val_replace_str |
Replace a specified val_value_t structure with a string type. |
val_fast_replace_string |
Replace a specified val_value_t structure with a string type . Reuse everything -- just set the val->v.str field |
val_add_child |
Add a child value node to a parent value node. Simply makes a new last child!!! Does not check siblings!!! Relies on val_set_canonical_order To modify existing entries, use val_add_child_sorted instead!! |
val_add_child2 |
Add a child value node to a parent value node. |
val_add_child_sorted |
Add a child value node to a parent value node in the proper place |
val_insert_child |
Insert a child value node into a specific spot into a parent value node. |
val_remove_child |
Remove a child value node from its parent. |
val_swap_child |
Replace a child node within its parent with a different value node. |
val_first_child_match |
Match a child node name; Used for partial command completion in yangcli-pro. |
val_first_child_match_fast |
Get the first instance of the corresponding child node. Object pointers must be from the same tree!!! |
val_next_child_match |
Match the next child node name; Used for partial command completion in yangcli-pro. |
val_next_child_same |
Get the next instance of the corresponding child node. |
val_get_first_child |
Get the first child value node. |
val_get_next_child |
Get the next child value node. |
val_find_child |
Find a specific child value node. |
val_find_child_fast |
Find the first instance of the specified child node. |
val_find_child_obj |
Find the first instance of the specified child node . Use the object template pointer to match the object. |
val_find_child_que |
Find the first instance of the specified child node in the specified child Q. |
val_find_next_child |
Find the next occurrence of a specified child node. |
val_find_next_child_fast |
Find the next instance of the specified child node . Use the curchild->obj pointer to find next child of this type . Assumes childQ is sorted and all instances of 'curchild' are already grouped together. |
val_first_child_name |
Get the first corresponding child node instance, by name find first -- really for resolve index function. |
val_first_child_qname |
Get the first corresponding child node instance, by Qname. |
val_next_child_qname |
Get the next corresponding child node instance, by Qname. |
val_first_child_string |
Find first name value pair . Get the first corresponding child node instance, by name and by string value. Child node must be a base type of NCX_BT_STRING NCX_BT_INSTANCE_ID NCX_BT_LEAFREF |
val_get_first_terminal_child |
Get the child node only if obj_is_terminal(obj) is true. |
val_get_next_terminal_child |
Get the next child node only if obj_is_terminal(obj) is true. |
val_match_child |
Match a potential partial node name against the child node names, and return the first match found, if any. |
val_match_child_count |
Match the first instance of the specified child node . Return the total number of matches. |
val_child_cnt |
Get the number of child nodes within a parent node. |
val_child_inst_cnt |
Get the corresponding child instance count by name . Get instance count -- for instance qualifier checking. |
val_get_child_inst_id |
Get the instance ID for this child node within the parent context. |
val_liststr_count |
Get the number of strings within an NCX_BT_LIST value node. |
val_index_match |
Check if 2 value list nodes have the same set of key leaf values. |
val_compare |
Compare 2 value nodes |
val_compare_ex |
Compare 2 value nodes with extra parameters. |
val_compare_to_string |
Compare a value node to a string value instead of another value node. |
val_compare_to_string_len |
Compare a val_value_t structure value contents to a string . Provide a max-length to compare. |
val_sprintf_simval_nc |
Output the value node as a string into the specified buffer. |
val_make_sprintf_string |
Malloc a buffer and fill it with a zero-terminated string representation of the value node. |
val_resolve_scoped_name |
Find a descendant node within a value node, from a relative path expression. |
val_has_content |
Return TRUE if the value node has any content; FALSE if an empty XML element could represent its value. |
val_has_index |
Return TRUE if the value node is a list with a key statement. |
val_get_first_index |
Get the first index node for the specified list value node. |
val_get_next_index |
Get the next index node for the specified list value node. |
val_get_index_count |
Get the number of index nodes in this val. |
val_set_extern |
Set a malloced value node as an NCX_BT_EXTERN internal data type. |
val_set_intern |
Set a malloced value node as an NCX_BT_INTERN internal data type. |
val_fit_oneline |
Return TRUE if the value node should fit on 1 display line; Sometimes a guess is made instead of determining the exact value. XML namespace declarations generated during XML output can cause this function value to sometimes be wrong. |
val_create_allowed |
Return TRUE if the NETCONF create operation is allowed for the specified value node. |
val_delete_allowed |
Return TRUE if the NETCONF delete operation is allowed for the specified value node. |
val_is_config_data |
Return TRUE if the value node represents configuration data. |
val_is_virtual |
Check if the specified value is a virtual value , such that a 'get' callback function is required to access the real value contents. |
val_get_virtual_value |
Get the value for a virtual node from its 'get' callback function. |
val_is_default |
Return TRUE if the value node is set to its YANG default value. |
val_is_real |
Check if a value node is a real node or one of the abstract node types. |
val_get_parent_nsid |
Get the namespace ID for the parent value node of a specified child node. |
val_instance_count |
Get the number of occurrences of the specified child value node within a parent value node. |
val_instance_count_fast |
Count the number of instances of the specified object name in the parent value structure. This only checks the first level under the parent, not the entire subtree. |
val_instance_count_fast2 |
Count the number of instances of the specified object name in the parent value structure. This only checks the first level under the parent, not the entire subtree . Starts with the startval passed in. |
val_need_quotes |
Return TRUE if the printed string representation of a value node needs quotes (because it contains some whitespace or special characters). |
val_all_whitespace |
Check if a string is all whitespace. |
val_any_whitespace |
Check if a string has any whitespace chars. |
val_get_dirty_flag |
Check if a value node has been altered by an RPC operation, but this edit has not been finalized yet. |
val_get_nest_level |
Get the current numeric nest level of the specified value node. |
val_get_mod_name |
Get the module name for the specified value node. |
val_get_mod_prefix |
Get the module prefix string for the specified value node. |
val_get_nsid |
Get the namespace ID for the specified value node. |
val_change_nsid |
Change the namespace ID for the specified value node and all of its descendants. |
val_set_pcookie |
Set the SIL pointer cookie in the value node editvars structure. |
val_set_icookie |
Set the SIL integer cookie in the value node editvars structure. |
val_get_pcookie |
Get the SIL pointer cookie in the value node editvars structure. |
val_get_icookie |
Get the SIL integer cookie in the value node editvars structure. |
val_get_typdef |
Get the typedef structure for a leaf or leaf-list value node. |
val_move_children |
Move all the child nodes of one complex value node to another complex value node. |
val_set_canonical_order |
Re-order the descendant nodes of a value node so they are in YANG order. Does not change the relative order of system-ordered lists and leaf-lists. |
val_gen_index_chain |
Generate the internal key leaf lookup chain for a list value node.. |
val_add_defaults |
Generate the leafs that have default values. |
val_instance_check |
Check a value node against its template to see if the correct number of descendant nodes are present. |
val_get_choice_first_set |
Get the first real node that is present for a conceptual choice statement. |
val_get_choice_next_set |
Get the next real node that is present for a conceptual choice statement. |
val_choice_is_set |
Return TRUE if some real data node is present for a conceptual choice statement. |
val_get_first_key |
Get the first key record if this is a list with a key-stmt. |
val_get_last_key |
Get the last key record if this is a list with a key-stmt. |
val_get_next_key |
Get the next key record if this is a list with a key-stmt. |
val_get_prev_key |
Get the previous key record if this is a list with a key-stmt. |
val_remove_key |
Remove a key pointer because the key is invalid . Free the key pointer. |
val_new_child_val |
Create a child node during an edit operation. Used by the server. SIL code does not need to maintain the value tree. |
val_gen_instance_id |
Malloc and generate the YANG instance-identifier string for the value node. |
val_check_obj_when |
Check if an object node has any 'when' statements, and if so, evaluate the XPath condition(s) against the value tree to determine if the object should be considered present or not. |
val_check_child_conditional |
Check if a child object node has any FALSE 'if-feature' or 'when' statements. |
val_is_mandatory |
Check if the child object node is currently mandatory or optional. |
val_get_xpathpcb |
Access the XPath parser control block for this value node, if any. |
val_make_simval_obj |
Malloc and fill in a value node from an object template and a value string. |
val_set_simval_obj |
Fill in a value node from an object template and a value string. |
val_find_bit |
Check if a bits value contains the specified bit name |
val_find_bit_name |
Get the name of the bit from a bits value that corresponds to the specified bit position |
val_find_enum_name |
Get the name of the enum from an enumeration value that corresponds to the specified 'value' number |
val_sprintf_etag |
Write the Entity Tag for the value to the specified buffer. |
val_get_last_modified |
Get the last_modified field. |
val_has_children |
Determine if there are any child nodes for this val. |
val_delete_children |
Check if the value is a complex type and if so then delete all child nodes. |
val_clean_value |
Clean a static val_value_t structure. |
val_get_yang_typename |
Get the YANG type name for this value node if there is one. |
val_is_value_set |
Check if a value has been set by a client . It has to be initialized and not set by default to return true. |
val_idref_derived_from |
Check the specified valnode is derived from the specified identity. |
val_get_sil_priority |
Get the secondary SIL priority; zero if not found. |
val_has_conditional_value |
YANG 1.1: Check the value that must be properly set to see if it is conditional on any if-feature-stmts. |
val_convert_leafref |
Convert a value of type NCX_BT_LEAFREF to the value that the final leafref is pointing at. |
val_set_child_leaf |
Find the child in the provided parent and update the child's node value. |
Data Tree Handling Examples
YANG data trees are instances of YANG schema nodes. There are various API functions that can be used to manipulate a desired Data node. They differ widely the operation that you need to perform. This section contains simple examples that will illustrate how to manage different YANG node types.
Include Files For Data Nodes
The following H files need to be included in a C file to use APIs to access data nodes.
H File |
Description |
|---|---|
agt.h |
Core agent utilities and agt_profile |
agt_util.h |
Agent utility functions |
cfg.h |
Configuration datastore utilities |
ncx.h |
YANG module utilities |
ncxconst.h |
Global constants |
ncxtypes.h |
Module and other code data structures |
obj.h |
YANG object template APIs |
val.h |
Main val_value_t access functions |
val_child.h |
Child access functions |
val_util.h |
Additional val_value_t access functions |
These files are (and others) are automatically included in auto-generated SIL and SIL-SA code.
If using in another file, remember to include all YumaPro H files after procdefs.h.
The libxml2 file
xmlstring.his usually included before all YumaPro files.All YumaPro headers are self-compiling, so it is OK to include them in alphabetical order (after procdefs.h)
For example:
#include <xmlstring.h>
#include "procdefs.h"
#include "agt.h"
#include "agt_util.h"
#include "cfg.h"
#include "ncx.h"
#include "ncxconst.h"
#include "ncxtypes.h"
#include "obj.h"
#include "val.h"
#include "val_child.h"
#include "val_util.h"
Creating and Deleting Data Nodes
Many APIs in this section return a malloced structure representing a YANG data node. The val_value_t typedef is used for this purpose.
New Value Node
-
val_value_t *val_new_value(void)
Malloc and initialize the fields in a val_value_t.
- Returns:
pointer to the malloced and initialized struct or NULL if an error
/* create an uninitialized value node */
val_value_t *val = val_new_value();
if (val == NULL) {
return ERR_INTERNAL_MEM;
}
Initialize a Value Node
-
void val_init_from_template(val_value_t *val, struct obj_template_t_ *obj)
Initialize a value node from its object template.
This is the normal init function to call MUST CALL val_new_value FIRST
- Parameters:
val -- pointer to the initialized value struct to bind
obj -- object template to use
/* initialize a value node */
val_init_from_template(val, obj);
Free a Value Node
-
void val_free_value(val_value_t *val)
Scrub the memory in a val_value_t by freeing all the sub-fields and then freeing the entire struct itself.
The struct must be removed from any queue it is in before this function is called.
- Parameters:
val -- val_value_t to delete
/* clean and free a value node and all its descendants */
val_free_value(val);
Formatting a Value Node as a String
If a value node represents a leaf or leaf-list node, and its value can be
converted to a string, the val_make_sprintf_string function can be used to
malloc a buffer that contains the NETCONF string representation of that value.
-
xmlChar *val_make_sprintf_string(const val_value_t *val)
Malloc a buffer and then sprintf the xmlChar string NETCONF representation of a simple value.
SIL code should use this function to generate the val_value_t simple value into a buffer for external use
- Parameters:
val -- value to print
- Returns:
malloced buffer with string representation of the 'val' value node NULL if some error
Note
The returned string is malloced and must be freed with m__free.
Example: iterate through a parent node's children and print each simple value child:
/********************************************************************
* FUNCTION print_children
*
* Dump children values
*
********************************************************************/
static void
print_children (val_value_t *val,
const char *which)
{
if (val == NULL) {
return;
}
val_value_t *child = NULL;
log_debug("\n %s %s P:[%p] :",
which,
VAL_NAME(val),
val);
val_dump_value(val, 5, DBG4);
for (child = val_get_first_child(val);
child != NULL;
child = val_get_next_child(child)) {
if (VAL_TYPE(child) && typ_is_simple(VAL_TYPE(child))) {
xmlChar *valstr = val_make_sprintf_string(child);
if (valstr == NULL) {
continue;
}
log_debug4("\nchild: %s:'%s'",
VAL_NAME(child),
valstr);
m__free(valstr);
}
}
} /* print_children */
Leaf Examples
The following examples show how to construct a simple leaf data node. These APIs can all be used for a leaf-list value as well.
For data-type-specific examples (binary, bits, decimal64, enumeration, leafref, union), see How to Work with YANG Data Nodes.
String Value With Leaf Object Template
The most common and generic API function to construct the data node from a string value. The val_make_simval_obj API requires the child object template.
-
val_value_t *val_make_simval_obj(obj_template_t *obj, const xmlChar *valstr, status_t *res)
Create and set a val_value_t as a simple type from an object template instead of individual fields Calls val_make_simval with the object settings.
This API is the preferred method to create a leaf in SIL or SIL-SA code. This will use correct settings from the object template
- Parameters:
obj -- object template to use
valstr -- simple value encoded as a string
res -- [out] address of return status
*res return status
- Returns:
pointer to malloced and filled in val_value_t struct NULL if some error
obj_template_t *leafobj =
obj_find_child(parentobj, modname, objname);
if (leafobj == NULL) {
/* report an error or do not try to generate the leaf */
return ERR_NCX_DEF_NOT_FOUND;
}
/* construct a leaf val_value tree regardless of its type */
status_t res = NO_ERR;
val_value_t *any_type_leaf =
val_make_simval_obj(leafobj,
(const xmlChar *)"8",
&res);
String Value With Parent Object Template
The "agt_make_leaf" family of API functions look up the child object and make the child leaf in one step.
-
val_value_t *agt_make_leaf(obj_template_t *parentobj, const xmlChar *leafname, const xmlChar *leafstrval, status_t *res)
make a string val_value_t struct for a specified leaf or leaf-list
OK for SIL or SIL-SA
This is a main API for SIL and SIL-SA code to construct return data for GET2 callbacks.
See also
- Parameters:
parentobj -- parent object to find child leaf object
leafname -- name of leaf to find (namespace hardwired)
leafstrval -- string version of value to set for leaf
res -- [out] address of return status; *res=return status
- Returns:
malloced value struct or NULL if some error
/* construct any type leaf using string representation */
status_t res = NO_ERR;
val_value_t *string_leaf =
agt_make_leaf(parentobj,
leafname,
(const xmlChar *)"example value",
&res);
There are additional APIs that allow a leaf to be created from an internal data type other than a string, such as a numeric type.
Integer Types
In order to construct a data node with integer types, the following API function can be used:
-
val_value_t *agt_make_uint_leaf(obj_template_t *parentobj, const xmlChar *leafname, uint32 leafval, status_t *res)
make a uint32 val_value_t struct for a specified leaf or leaf-list
OK for SIL or SIL-SA
This is a main API for SIL and SIL-SA code to construct return data for GET2 callbacks.
See also
agt_make_uint_leaf2
- Parameters:
parentobj -- parent object to find child leaf object
leafname -- name of leaf to find (namespace hardwired)
leafval -- number value for leaf
res -- [out] address of return status; *res=return status
- Returns:
malloced value struct or NULL if some error
/* construct a uint32 type leaf */
status_t res = NO_ERR;
val_value_t *string_leaf =
agt_make_uint_leaf(parentobj,
leafname,
(uint32)32,
&res);
-
val_value_t *agt_make_int_leaf(obj_template_t *parentobj, const xmlChar *leafname, int32 leafval, status_t *res)
make an int32 val_value_t struct for a specified leaf or leaf-list
OK for SIL or SIL-SA
This is a main API for SIL and SIL-SA code to construct return data for GET2 callbacks.
See also
agt_make_int_leaf2
- Parameters:
parentobj -- parent object to find child leaf object
leafname -- name of leaf to find (namespace hardwired)
leafval -- integer number value for leaf
res -- [out] address of return status; *res return status
- Returns:
malloced value struct or NULL if some error
/* construct a uint32 type leaf */
status_t res = NO_ERR;
val_value_t *value =
agt_make_int_leaf(parentobj,
leafname,
(int32)32,
&res);
-
val_value_t *agt_make_uint64_leaf(obj_template_t *parentobj, const xmlChar *leafname, uint64 leafval, status_t *res)
make a uint64 val_value_t struct for a specified leaf or leaf-list
OK for SIL or SIL-SA
This is a main API for SIL and SIL-SA code to construct return data for GET2 callbacks.
See also
agt_make_uint64_leaf2
- Parameters:
parentobj -- parent object to find child leaf object
leafname -- name of leaf to find (namespace hardwired)
leafval -- uint64 number value for leaf
res -- [out] address of return status; *res return status
- Returns:
malloced value struct or NULL if some error
/* construct a uint64 type leaf */
status_t res = NO_ERR;
val_value_t *value =
agt_make_uint64_leaf(parentobj,
leafname,
(uint64)64,
&res);
-
val_value_t *agt_make_int64_leaf(obj_template_t *parentobj, const xmlChar *leafname, int64 leafval, status_t *res)
make an int64 val_value_t struct for a specified leaf or leaf-list
OK for SIL or SIL-SA
This is a main API for SIL and SIL-SA code to construct return data for GET2 callbacks.
See also
agt_make_int64_leaf2
- Parameters:
parentobj -- parent object to find child leaf object
leafname -- name of leaf to find (namespace hardwired)
leafval -- int64 number value for leaf
res -- [out] address of return status; *res return status
- Returns:
malloced value struct or NULL if some error
/* construct a int64 type leaf */
status_t res = NO_ERR;
val_value_t *value =
agt_make_int64_leaf(parentobj,
leafname,
(int64)64,
&res);
Boolean Type
In order to construct a data node with boolean type, the following API function can be used.
-
val_value_t *agt_make_boolean_leaf(obj_template_t *parentobj, const xmlChar *modname, const xmlChar *leafname, boolean boolval, status_t *res)
make a val_value_t struct for a specified leaf or leaf-list (NCX_BT_BOOL)
OK for SIL or SIL-SA
- Parameters:
parentobj -- parent object to find child leaf object
modname -- name of module defining leaf (may be NULL to pick parent)
leafname -- name of leaf to find (namespace hardwired)
boolval -- boolean value for leaf
res -- [out] address of return status; *res return status
- Returns:
malloced value struct or NULL if some error
/* construct a boolean type leaf */
status_t res = NO_ERR;
val_value_t *string_leaf =
agt_make_boolean_leaf(parentobj,
modname,
leafname,
boolval,
&res);
Empty Type
In order to construct a data node with empty type, the following API function can be used.
-
val_value_t *agt_make_empty_leaf(obj_template_t *parentobj, const xmlChar *modname, const xmlChar *leafname, boolean boolval, status_t *res)
make a val_value_t struct for a specified leaf or leaf-list (NCX_BT_EMPTY)
OK for SIL or SIL-SA
- Parameters:
parentobj -- parent object to find child leaf object
modname -- name of module defining leaf (may be NULL to pick parent)
leafname -- name of leaf to find (namespace hardwired)
boolval -- ignored boolean value for leaf
res -- [out] address of return status; *res return status
- Returns:
malloced value struct or NULL if some error
/* construct an empty type leaf */
status_t res = NO_ERR;
val_value_t *string_leaf =
agt_make_empty_leaf(parentobj,
modname,
leafname,
boolval,
&res);
Binary Type
The binary data type represents binary data (a sequence of octets). In NETCONF and RESTCONF messages, binary values are encoded using base64.
Inside the server, the binary leaf value is a byte buffer. It is not a zero-terminated printable string.
For examples and additional details, refer to How to Work with Binary Data Type.
-
val_value_t *val_make_binary_obj(obj_template_t *obj, const xmlChar *binstr, uint32 binstrlen, status_t *res)
Malloc and set a val_value_t as a NCX_BT_BINARY type.
- Parameters:
obj -- object template to use set binary object
binstr -- simple value encoded as a binary buffer
binstrlen -- length of binstr (not zero-terminated)
res -- [out] address of return status
*res return status
- Returns:
malloced val struct filled in; NULL if malloc failed
The val_make_binary_obj helper internally calls val_set_simval_binary after creating and initializing the value node. This function can also be used directly to set a binary leaf from a raw buffer and a length.
-
status_t val_set_simval_binary(val_value_t *val, const xmlChar *binstr, uint32 binstrlen)
set a binary type either in a type binary, or type within 1 or more unions
The string value will be converted to a value struct format and checked against the provided typedef
Handles the following data types:
- Parameters:
val -- [inout] value to set
*val is filled in if return NO_ERR
binstr -- binary buffer
binstrlen -- length of binstr
- Returns:
status
The following macros can be used with a binary leaf val_value_t. Refer to val.h:
VAL_BINARY(V): access the binary buffer
VAL_BINARY_LEN(V): access the length of the binary buffer
The following example creates a binary leaf with a raw byte buffer.
#include "val_util.h"
#include "val.h"
#include "status.h"
status_t res = NO_ERR;
/* raw bytes (not base64); length is required */
const xmlChar *bytes = (const xmlChar *)"yellow";
uint32 bytes_len = 6;
val_value_t *retval =
val_make_binary_obj(leafobj, bytes, bytes_len, &res);
if ((retval != NULL) && (res == NO_ERR)) {
// retval is malloced and valid, ready to use
// ...
} else {
// error status and NULL return value expected
}
The following example uses val_set_simval_binary directly:
#include "val.h"
#include "status.h"
status_t res = NO_ERR;
/* initialized value node (for example, created from an object template) */
val_value_t *val = val_new_value();
val_init_from_template(val, leafobj);
res = val_set_simval_binary(val, bytes, bytes_len);
if (res != NO_ERR) {
val_free_value(val);
val = NULL;
}
Bits Type
The easiest way to create a leaf or leaf-list with the YANG bits data type is to create a string somehow with the bit values that should be set. This example uses the leaf object template and the 'val_make_simval_obj' API.
For examples and additional details, refer to How to Work with Bits Data Type.
#include "val_util.h"
#include "status.h"
status_t res = NO_ERR;
val_value_t *retval =
val_make_simval_obj(leafobj,
(const xmlChar *)"bit1 bit3 bit5",
&res);
if ((retval != NULL) && (res == NO_ERR)) {
/* retval is malloced and valid, ready to use */
// ...
} else {
// res should be some error only if retval is also NULL
}
It is also possible to construct a bits leaf or leaf-list from the 'ncx_bits_t' which is really 'ncx_list_t'.
First the ncx_list_t is constructed or referenced from within another val_value_t or other data structure.
Next the agt_make_bits_leaf API is used with the parent object not the child leaf object.
-
val_value_t *agt_make_bits_leaf(obj_template_t *parentobj, const xmlChar *modname, const xmlChar *leafname, const ncx_list_t *listval, status_t *res)
make a val_value_t struct for a specified leaf or leaf-list (NCX_BT_BITS or NCX_BT_SLIST)
OK for SIL or SIL-SA
- Parameters:
parentobj -- parent object to find child leaf object
modname -- name of module defining leaf (may be NULL to pick parent)
leafname -- name of leaf to find (namespace hardwired)
listval -- bits value for leaf
res -- [out] address of return status; *res return status
- Returns:
malloced value struct or NULL if some error
#include "agt_util.h"
#include "ncx_list.h"
#include "status.h"
/* construct a bits leaf from another bits leaf */
status_t res = NO_ERR;
const ncx_list_t *bits = &(VAL_BITS(anotherleafval));
val_value_t *string_leaf =
agt_make_bits_leaf(parentobj,
modname,
leafname,
bits,
&res);
To check if any bit is set in a val_value_t using the bits type, refer to the following code example for a server CLI parameter:
/* Process --log-backtrace-stream */
val = val_find_child_fast(parentval, nsid, NCX_EL_LOGBT_STREAM);
if (val && val->res == NO_ERR) {
ncx_list_t *bits = &VAL_BITS(val);
if (ncx_string_in_list((const xmlChar *)"logfile", bits)) {
log_set_backtrace_logfile();
}
if (ncx_string_in_list((const xmlChar *)"stderr", bits)) {
log_set_backtrace_stderr();
}
if (ncx_string_in_list((const xmlChar *)"stdout", bits)) {
log_set_backtrace_stdout();
}
if (ncx_string_in_list((const xmlChar *)"syslog", bits)) {
log_set_backtrace_syslog();
}
if (ncx_string_in_list((const xmlChar *)"vendor", bits)) {
log_set_backtrace_vendor();
}
}
Leafref Type
The leafref data type is restricted to the value space of some other leaf or leaf-list node in the schema tree (the target node).
For examples and additional details, refer to How to Work with Leafref Data Type.
-
val_value_t *val_convert_leafref(const val_value_t *val)
Convert a value of type NCX_BT_LEAFREF to the value that the final leafref is pointing at.
- Parameters:
val -- value to convert; this must be an NCX_BT_LEAFREF
- Returns:
malloced val_value_t that must be freed with val_free_value the val->btyp and val->typdef and val->v fields will be set based on the real type; the val->obj will still point at the original object
Some server APIs used with leafref values:
val_convert_leafref: convert a leafref value to the base type that the final leafref is pointing at
Union Type
The union data type represents a value that matches one of its member types. The member types are checked in order.
When a union leaf value is parsed or created with val_make_simval_obj, the server validates the input value and stores the result using the matching member base type.
For examples and additional details, refer to How to Work with Union Data Type.
-
status_t val_union_ok_ex(typ_def_t *typdef, const xmlChar *strval, val_value_t *retval, ncx_errinfo_t **errinfo, ncx_module_t *mod, typ_def_t **match_typdef)
Check a union to make sure the string is valid based on the specified typdef, and convert the string to an NCX internal format.
- Parameters:
typdef -- typ_def_t for the designated union type
strval -- the value to check against the member typ defs (may be NULL for empty string)
retval -- [inout] pointer to output struct for converted value
retval->str or retval->num will be set with the converted value
errinfo -- [out] address of error struct
*errinfo set to the found error to use (if any)
mod -- module in progress, if any (may be NULL)
match_typdef -- [out] address of return matching typdef (may be NULL)
*match_typdef set to member typdef that matched
- Returns:
status
#include "val_util.h"
#include "status.h"
status_t res = NO_ERR;
val_value_t *retval =
val_make_simval_obj(leafobj, (const xmlChar *)"15", &res);
if ((retval != NULL) && (res == NO_ERR)) {
/* check which union member type matched */
ncx_btype_t btyp = VAL_BTYPE(retval);
(void)btyp;
// ...
}
Enumeration Type
The enumeration data type represents values from a set of assigned names.
For examples and additional details, refer to How to Work with Enumeration Data Type.
-
val_value_t *val_make_from_enumval(obj_template_t *obj, int32 enumval, status_t *res)
Create an enumeration leaf from its enum value.
- Parameters:
obj -- target object to use and create
enumval -- numeric value of the enum to set
res -- [out] == address of return status
*res == return status
- Returns:
NO_ERR: value created and must be freed with val_free_value
The following macros can be used with an enumeration leaf val_value_t. Refer to val.h:
VAL_ENUM(V): access the integer value
VAL_ENUM_NAME(V): access the enumeration name
#include "val_util.h"
#include "status.h"
status_t res = NO_ERR;
val_value_t *retval =
val_make_simval_obj(leafobj, (const xmlChar *)"lm4", &res);
if ((retval != NULL) && (res == NO_ERR)) {
int32 enum_value = VAL_ENUM(retval);
const xmlChar *enum_name = VAL_ENUM_NAME(retval);
(void)enum_value;
(void)enum_name;
// ...
}
Identityref Type
The 'identityref' data type in YANG is used to select a specific YANG identity for its value, which must match the identityref 'base' statements in the identityref type.
module foo {
// ...
prefix f;
identity idbase;
identity val1 {
base idbase;
}
identity val2 {
base idbase;
}
identity val2 {
base idbase;
}
leaf idref1 {
type identityref {
// values val1, val2, val3 allowed
base idbase;
}
}
}
The 'val_idref_t' struct is used to define a reference to an identity.
-
struct val_idref_t
one QName for the NCX_BT_IDREF value
An identityref leaf can be created in 2 steps with these APIs:
Create a val_idref_t struct
Invoke the agt_make_idref_leaf API function
-
val_value_t *agt_make_idref_leaf(obj_template_t *parentobj, const xmlChar *leafname, const val_idref_t *leafval, status_t *res)
make an identityref val_value_t struct for a specified leaf or leaf-list
OK for SIL or SIL-SA
This is a main API for SIL and SIL-SA code to construct return data for GET2 callbacks.
See also
agt_make_idref_leaf2
- Parameters:
parentobj -- parent object to find child leaf object
leafname -- name of leaf to find (namespace hardwired)
leafval -- identityref value for leaf
res -- [out] address of return status; *res return status
- Returns:
malloced value struct or NULL if some error
The following example shows how this API might be used to set the leaf 'idref1' to the identity 'val2':
#include "agt_util.h"
#include "status.h"
#include "val.h"
#include "xmlns.h"
val_idref_t idref = {0};
xmlChar buff[] = "val2";
idref.nsid = xmlns_find_ns_by_module((const xmlChar *)"foo");
idref.name = buff;
status_t res = NO_ERR;
val_value_t *newval =
agt_make_idref_leaf(parentobj,
(const xmlChar *)"idref1",
&idref,
&res);
if ((newval != NULL) && (res == NO_ERR)) {
// valid leaf OK to use; must be freed with val_free_value
} else {
// error status and NULL return value expected
}
How to Work with YANG Data Nodes
This section contains practical examples for working with common YANG built-in data types, including how to set values, what responses to expect, and how to create/access values in SIL code.
How to Work with Binary Data Type
The binary type represents binary data, for example, a sequence of octets. A binary type can be restricted with the "length" statement. The length of a binary value is the number of octets it contains.
Binary values are encoded with the base64 encoding scheme in NETCONF and RESTCONF messages.
The internal representation for the binary data type is a binary buffer. It is not a zero-terminated printable string.
Consider the following YANG module example:
module example {
namespace "http://yumaworks.com/ns/example";
prefix "example";
revision 2019-07-30 {
description "Initial example module for binary type";
}
leaf yang-binary-example {
type binary {
length "6";
}
default "SGVsbG8h"; // base64 value of "Hello!"
}
}
The default value for a binary node must be encoded in base64.
Accessing Binary Nodes
There are multiple ways to access or modify a binary node:
Using RESTCONF/HTTP for Binary
Create a new "yang-binary-example" node using RESTCONF:
POST /restconf/data/ HTTP/1.1
Host: example-server
Content-Type: application/yang-data+json
{ "yang-binary-example": "eWVsbG93" }
The value for this node is base64 encoded. If created successfully, the server response is:
HTTP/1.1 201 Created
Server: example-server
Location: https://example-server/restconf/data/yang-binary-example
If the node already exists, the server returns a "409 Conflict".
Retrieve the created node:
GET /restconf/data/yang-binary-example HTTP/1.1
Host: example-server
Accept: application/yang-data+xml
Server response:
HTTP/1.1 200 OK
Server: example-server
Content-Type: application/yang-data+xml
<yang-binary-example xmlns="http://yumaworks.com/ns/example">eWVsbG93</yang-binary-example>
Using yangcli-pro for Binary
Create a new "yang-binary-example" node using yangcli-pro:
create /yang-binary-example value=yellow
commit
yangcli-pro expects a raw string and will encode the given value to base64. If the value to be assigned is already base64 encoded, yangcli-pro encodes it again, resulting in a double encoded value.
If a length restriction is present for the binary leaf, the length restriction applies to the raw value entered.
Retrieve the created binary leaf:
sget /yang-binary-example
The server might respond with:
<rpc-reply>
<data>
<yang-binary-example xmlns="http://yumaworks.com/ns/example">eWVsbG93</yang-binary-example>
</data>
</rpc-reply>
If the value of a binary node is set by providing JSON content (inline or with @file.json), the value must be base64 encoded:
edit-config target=candidate config="""
{
"config": {
"yang-binary-example": "eWVsbG93"
}
}
"""
If the value of a binary node is set by providing XML content (inline or with @file.xml), the value must be base64 encoded:
edit-config target=candidate config="""
<config>
<yang-binary-example>eWVsbG93</yang-binary-example>
</config>
"""
Using DB-API for Binary
To set a binary leaf value using DB-API, the value must be base64 encoded:
/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function for binary node.
*
*********************************************************************/
static void
send_test_edit (void)
{
const xmlChar *path_str = (const xmlChar *)"/";
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *value_str = (const xmlChar *)
"<config>"
"<yang-binary-example xmlns='http://yumaworks.com/ns/example'>eWVsbG93</yang-binary-example>"
"</config>";
const xmlChar *patch_id_str = NULL;
boolean system_edit = FALSE;
status_t res = db_api_send_edit_ex(path_str,
operation_str,
value_str,
patch_id_str,
system_edit);
if (res != NO_ERR) {
log_error("\nSend test edit for binary node failed %s %s = %s (%s)\n",
operation_str, path_str, value_str,
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test edit for binary node OK %s %s = %s\n",
operation_str, path_str, value_str);
}
} /* send_test_edit */
Binary Node Creation Using SIL
Binary data type is treated similar to the string type, except it needs to be decoded to proceed further.
The following APIs and macros can be used with a binary val value node. Refer to val.h:
val_make_binary_obj(): create a binary leaf from a raw binary buffer.
val_make_simval_obj(): create a leaf value from a string.
VAL_BINARY(): access the binary buffer (not zero-terminated).
VAL_BINARY_LEN(): access the binary length.
val_make_sprintf_string(): create a printable string representation of a simple value for logging (type binary is encoded as base64).
Consider the following YANG module snippet:
leaf yang-binary-example-1 {
config false;
type binary;
}
leaf yang-binary-example-2 {
config false;
type binary;
}
The following code example creates the value of a binary leaf and outputs it to the standard log output using val_make_simval_obj.
/* ... */
status_t res = NO_ERR;
/* create a binary leaf val value node */
val_value_t *return_val =
val_make_simval_obj(obj,
(const xmlChar *)"yellow",
&res);
if ((return_val != NULL) && (res == NO_ERR)) {
xmlChar *valstr = val_make_sprintf_string(return_val);
if (valstr != NULL) {
log_info("\nCreated binary node; value:'%s' length:'%u'",
valstr,
VAL_BINARY_LEN(return_val));
m__free(valstr);
} else {
log_info("\nCreated binary node; length:'%u'",
VAL_BINARY_LEN(return_val));
}
}
/* ... */
The server might output the following logging information to the standard output:
Created binary node; value:'eWVsbG93' length:'6'
The following code example will create the binary node and output it to the standard output using val_make_binary_obj.
/* ... */
status_t res = NO_ERR;
/* create a new binary type node */
val_value_t *return_val =
val_make_binary_obj(obj,
(const xmlChar *)"yellow",
(uint32)6,
&res);
if ((return_val != NULL) && (res == NO_ERR)) {
/* Output the value to the standard output log */
log_info("\nCreated binary node; length:'%u'",
VAL_BINARY_LEN(return_val));
}
/* ... */
The server might output the following logging information to the standard output:
Created binary node; length:'6'
How to Work with Bits Data Type
A bits built-in type represents a bit set. A bits value is a set of flags identified by names and small integer position numbers starting at 0.
A bits type can be restricted with the "bit" statement. The "position" statement is optional and specifies the bit's position within the bit set.
Consider the following YANG module example:
module example {
namespace "http://yumaworks.com/ns/example";
prefix "example";
revision 2019-07-30 {
description "Initial example module for bits type";
}
leaf yang-bits-example {
type bits {
bit zero { position 0; }
bit one { position 1; }
bit two { position 2; }
bit ten { position 10; }
bit last;
}
default "two";
}
}
Accessing Bits Nodes
There are multiple ways to access or modify a bits node:
Using RESTCONF/HTTP for Bits
Create a new "yang-bits-example" node using RESTCONF:
POST /restconf/data/ HTTP/1.1
Host: example-server
Content-Type: application/yang-data+json
{ "yang-bits-example": "one two" }
If created successfully, the server response is:
HTTP/1.1 201 Created
Server: example-server
Location: https://example-server/restconf/data/yang-bits-example
Retrieve the created node:
GET /restconf/data/yang-bits-example HTTP/1.1
Host: example-server
Accept: application/yang-data+xml
Server response:
HTTP/1.1 200 OK
Server: example-server
Content-Type: application/yang-data+xml
<yang-bits-example xmlns="http://yumaworks.com/ns/example">one two</yang-bits-example>
Using yangcli-pro for Bits
Create a new "yang-bits-example" node using yangcli-pro:
create /yang-bits-example value='one two'
commit
Retrieve the created bits leaf:
sget /yang-bits-example
The server might respond with:
<rpc-reply>
<data>
<yang-bits-example xmlns="http://yumaworks.com/ns/example">one two</yang-bits-example>
</data>
</rpc-reply>
Set the bits leaf with inline JSON:
edit-config target=candidate config="""
{
"config": {
"yang-bits-example": "one two"
}
}
"""
Set the bits leaf with inline XML:
edit-config target=candidate config="""
<config>
<yang-bits-example>one two</yang-bits-example>
</config>
"""
Using DB-API for Bits
Example of setting a bits leaf value using DB-API:
/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function for bits node.
**********************************************************************/
static void
send_test_edit (void)
{
const xmlChar *path_str = (const xmlChar *)"/";
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *value_str = (const xmlChar *)
"<config>"
"<yang-bits-example xmlns='http://yumaworks.com/ns/example'>one two</yang-bits-example>"
"</config>";
const xmlChar *patch_id_str = NULL;
boolean system_edit = FALSE;
status_t res = db_api_send_edit_ex(path_str,
operation_str,
value_str,
patch_id_str,
system_edit);
if (res != NO_ERR) {
log_error("\nSend test edit for bits node failed %s %s = %s (%s)\n",
operation_str, path_str, value_str,
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test edit for bits node OK %s %s = %s\n",
operation_str, path_str, value_str);
}
} /* send_test_edit */
Bits Node Creation Using SIL
The following API can be used with the bits type val value node. Refer to val.h:
val_make_simval_obj(): create a leaf value from a string.
The following code example creates the value of a bits leaf and outputs it to the standard log output using val_make_simval_obj.
/* ... */
status_t res = NO_ERR;
/* Create a new bits leaf type node */
val_value_t *return_val =
val_make_simval_obj(obj, (const xmlChar *)"one two", &res);
if ((return_val != NULL) && (res == NO_ERR)) {
log_info("\nCreated node; type:'%s'",
VAL_TYPDEF(return_val)->typenamestr);
}
/* ... */
The server might output the following logging information to the standard output:
Created node; type:'bits'
It is also possible to construct a bits leaf or leaf-list from an "ncx_bits_t" value (internally, this is an "ncx_list_t") and use agt_make_bits_leaf.
How to Work with Decimal64 Data Type
The decimal64 built-in type represents a fixed-point decimal value. It is defined with a required fraction-digits value, which specifies how many digits appear after the decimal point.
In most server code, treat decimal64 values as strings:
Use val_make_simval_obj() to create the return value from a string.
Use val_make_sprintf_string() to convert an input value to a string.
Consider the following YANG module example:
module tdec {
namespace "http://netconfcentral.org/ns/tdec";
prefix tdec;
revision 2021-03-02;
container top-state {
config false;
leaf dec2 {
type decimal64 {
fraction-digits 3;
}
}
}
rpc dec-rpc {
input {
leaf dec3 {
type decimal64 {
fraction-digits 4;
}
}
}
output {
leaf dec4 {
type decimal64 {
fraction-digits 4;
}
}
}
}
}
Return a decimal64 value in a GET2 callback
The GET2 Callback template includes a loop over requested terminal nodes. To return a decimal64 value, create a val_value_t from a string and add it to the return queue:
if (!xml_strcmp(name, (const xmlChar *)"dec2")) {
/* leaf dec2 (decimal64) */
val_value_t *childval =
val_make_simval_obj(childobj,
(const xmlChar *)"12.003",
&res);
if (childval) {
getcb_add_return_val(get2cb, childval);
} else {
return res;
}
}
Example GET response:
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<data>
<top-state xmlns="http://netconfcentral.org/ns/tdec">
<dec2>12.003</dec2>
</top-state>
</data>
</rpc-reply>
Use a decimal64 value in RPC input or configuration edits
If a decimal64 leaf is used in configuration data or RPC input, the SIL code
receives the value in a val_value_t node. The decimal64 macros (for
example VAL_DEC64) access internal representation details and are usually
not useful for instrumentation code. Use val_make_sprintf_string()
instead to get the canonical string form:
val_value_t *inputval = agt_get_rpc_input(msg);
if (inputval == NULL) {
return ERR_NCX_OPERATION_FAILED;
}
val_value_t *v_dec3_val =
val_find_child(inputval,
(const xmlChar *)"tdec",
(const xmlChar *)"dec3");
if (v_dec3_val) {
/* internal representation (usually not needed) */
int64 v_dec3 = VAL_DEC64(v_dec3_val);
(void)v_dec3;
xmlChar *buff = val_make_sprintf_string(v_dec3_val);
if (buff) {
log_debug("\nGot dec3 value '%s'", buff);
m__free(buff);
}
}
Example RPC request:
<rpc message-id="1"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<dec-rpc xmlns="http://netconfcentral.org/ns/tdec">
<dec3>0.0005</dec3>
</dec-rpc>
</rpc>
Example server log output:
Start SIL validate rpc <dec-rpc> from module tdec
Start SIL invoke rpc <dec-rpc> from module tdec
Got dec3 value '0.0005'
Decode a decimal64 string into ncx_num_t
Some APIs use ncx_num_t to represent numeric types. The ncx_decode_dec64() function can be used to convert a decimal64 string to an initialized ncx_num_t value.
const xmlChar *numstr = (const xmlChar *)"4.05";
ncx_num_t decnum;
ncx_init_num(&decnum);
typ_def_t *typdef = VAL_TYPDEF(val);
res = ncx_decode_dec64(numstr,
typ_get_fraction_digits(typdef),
&decnum);
if (res == NO_ERR) {
/* do something with decnum */
}
ncx_clean_num(&decnum);
Access a decimal64 value as a string
A decimal64 value can be accessed as a string without using the internal representation.
xmlChar buff[64];
uint32 len = 0;
/* val is the val_value_t pointer passed from the server */
status_t res = val_sprintf_simval_nc(buff, val, &len);
if (res == NO_ERR) {
/* OK to use buff */
}
Internal representation (for reference)
The internal server numeric type ncx_num_t contains a ncx_dec64_t structure for decimal64 values:
typedef struct ncx_dec64_t_ {
int64 val; /* adjusted number to fit in 64 bits */
uint8 digits; /* number of decimal digits 1 .. 18 */
uint8 zeroes; /* number of leading zeroes 0 .. 17 */
} YPACK ncx_dec64_t;
The internal macros that access these fields should not be used in instrumentation code:
#define VAL_DEC64(V) (V)->v.num.dec.val
#define VAL_DEC64_DIGITS(V) (V)->v.num.dec.digits
#define VAL_DEC64_ZEROES(V) (V)->v.num.dec.zeroes
How to Work with Enumeration Data Type
The enumeration built-in type represents values from a set of assigned names.
Enumeration definition rules
The "enum" statement MUST be present if the type is "enumeration".
All assigned names in an enumeration MUST be unique.
When an existing enumeration type is restricted, the set of assigned names in the new type MUST be a subset of the base type's set of assigned names.
When an existing enumeration type is restricted, the value of an assigned name MUST NOT be changed.
The "value" statement is optional and associates an integer value with an assigned name. If set, it MUST be in the range -2147483648 to 2147483647 and MUST be unique within the enumeration.
If a "value" is not specified, it will be assigned automatically. If the "enum" substatement is the first one defined, the assigned value is zero (0). Otherwise, the assigned value is one greater than the current highest enum value.
The presence of an "if-feature" statement does not affect the automatically assigned value.
Usage example: enum-test.yang
module enum-test {
namespace "http://netconfcentral.org/ns/enum-test";
prefix "et";
revision 2020-05-15 {
description "Init example module.";
}
typedef slot-index {
type enumeration {
enum zero {
value 0;
description
"Reserved slot number.";
}
enum lm1 {
value 1;
description
"Slot 1.";
}
enum lm2 {
value 2;
description
"Slot 2.";
}
enum lm3 {
value 3;
description
"Slot 3.";
}
enum lm4 {
value 4;
description
"Slot 4.";
}
enum lm5 {
value 5;
description
"Slot 5.";
}
enum lm6 {
value 6;
description
"Slot 6.";
}
}
description
"Example enumeration";
}
container conf-cont {
presence "testing one";
leaf index {
type slot-index;
}
}
container state-conf {
config false;
leaf index {
type slot-index;
}
}
}
Example files used for this page:
Accessing Enumeration Nodes
Using RESTCONF/HTTP for Enumeration
Create a new "/conf-cont/index" node:
PUT /restconf/data/enum-test:conf-cont HTTP/1.1
Host: example-server
Content-Type: application/yang-data+json
{
"enum-test:conf-cont": {
"index": "lm4"
}
}
Retrieve the created node:
GET /restconf/data/enum-test:conf-cont/index HTTP/1.1
Host: example-server
Accept: application/yang-data+xml
Using yangcli-pro for Enumeration
Create a new "/conf-cont/index" node:
create /conf-cont index=lm4
commit
Retrieve the created enumeration leaf:
sget /conf-cont/index
Using DB-API for Enumeration
Set "/conf-cont/index" using DB-API:
/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function for enumeration node.
**********************************************************************/
static void
send_test_edit (void)
{
const xmlChar *path_str = (const xmlChar *)"/";
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *value_str = (const xmlChar *)
"<config>"
"<conf-cont xmlns='http://netconfcentral.org/ns/enum-test'>"
"<index>lm4</index>"
"</conf-cont>"
"</config>";
const xmlChar *patch_id_str = NULL;
boolean system_edit = FALSE;
status_t res = db_api_send_edit_ex(path_str,
operation_str,
value_str,
patch_id_str,
system_edit);
if (res != NO_ERR) {
log_error("\nSend test edit for enumeration node failed %s %s = %s (%s)\n",
operation_str, path_str, value_str,
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test edit for enumeration node OK %s %s = %s\n",
operation_str, path_str, value_str);
}
} /* send_test_edit */
Enumeration Node Creation Using SIL
SIL code generation command used in this example:
make_sil_dir_pro enum-test --sil-get2 --sil-edit2
The following macros and functions can be used with an enumeration leaf val_value_t. Refer to val.h:
VAL_ENUM(): access the integer value.
VAL_ENUM_NAME(): access the enumeration name.
val_make_simval_obj(): create a leaf value using an enumeration name.
EDIT2 callback example
The following code snippet illustrates how to access an enumeration leaf value node in an EDIT2 callback:
case AGT_CB_COMMIT:
/* device instrumentation done here */
switch (editop) {
case OP_EDITOP_LOAD:
break;
case OP_EDITOP_MERGE:
break;
case OP_EDITOP_REPLACE:
break;
case OP_EDITOP_CREATE:
{
val_value_t *child_enum =
val_find_child(newval,
y_enum_test_M_enum_test,
(const xmlChar *)"index");
if (child_enum) {
int32 value = VAL_ENUM(child_enum);
const xmlChar *name = VAL_ENUM_NAME(child_enum);
log_debug2("\nENUM value:%u; ENUM name:%s",
value,
name);
}
/**** process other child edits here if needed ****/
}
break;
case OP_EDITOP_DELETE:
break;
default:
res = SET_ERROR(ERR_INTERNAL_VAL);
}
Example edit-config RPC:
<edit-config>
<target>
<candidate/>
</target>
<default-operation>merge</default-operation>
<test-option>set</test-option>
<config>
<conf-cont xmlns="http://netconfcentral.org/ns/enum-test">
<index
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
nc:operation="create">lm4</index>
</conf-cont>
</config>
</edit-config>
Example commit log output:
***** start commit phase on running for session 3, transaction 96209 *****
Start full commit of transaction 96209: 1 edit on running config
Start invoking commit SIL callback for create on enum-test:conf-cont
Enter enum_test_conf_cont_edit callback for commit phase
VAL name:index; ENUM value:4; ENUM name:lm4
Finished invoking user callback on enum-test:conf-cont
edit-transaction 96209: on session 3 by tony@127.0.0.1
time: 2020-05-15T17:33:35Z
message-id: 3
trace-id: --
datastore: running
operation: create
target: /et:conf-cont
comment: none
GET2 callback example
The following code snippet illustrates how to create an enumeration leaf value node in a GET2 callback:
/* go through all the requested terminal child objects */
obj_template_t *childobj =
getcb_first_requested_child(get2cb, obj);
for (; childobj; childobj =
getcb_next_requested_child(get2cb, childobj)) {
const xmlChar *name = obj_get_name(childobj);
val_value_t *retval = NULL;
/* Retrieve the value of this terminal node and
* add with getcb_add_return_val */
if (!xml_strcmp(name, y_enum_test_N_index)) {
/* leaf index (enumeration) */
retval =
val_make_simval_obj(childobj,
(const xmlChar *)"lm5",
&res);
if (retval) {
getcb_add_return_val(get2cb, retval);
} else {
return res;
}
}
}
Example <get> RPC:
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<state-conf xmlns="http://netconfcentral.org/ns/enum-test"/>
</filter>
</get>
</rpc>
Example <rpc-reply>:
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<data>
<state-conf xmlns="http://netconfcentral.org/ns/enum-test">
<index>lm5</index>
</state-conf>
</data>
</rpc-reply>
The download file enum-test.tar.xz contains an example enum-test directory with generated SIL code for this module, including enum-test/src/enum-test.c.
How to Work with Leafref Data Type
The leafref type is restricted to the value space of a leaf or leaf-list node in the schema tree, and optionally further constrained by corresponding instance nodes in the data tree. The "path" sub-statement identifies the referred node. The value space of the referring leafref node matches exactly that of the target node.
If the "require-instance" property is "true", the target node must exist in the data tree with the value matching the leafref for the configuration to be valid.
Consider the following example YANG module:
module example {
namespace "http://yumaworks.com/ns/example";
prefix "example";
revision 2019-07-30 {
description "Initial example module for leafref type";
}
leaf yang-boolean-example {
type boolean;
}
leaf yang-leafref-example-1 {
type leafref {
path "../yang-boolean-example";
require-instance "false";
}
}
leaf yang-leafref-example-2 {
type leafref {
path "../yang-boolean-example";
require-instance "true";
}
}
}
Accessing Leafref Nodes
There are multiple ways to access or modify a leafref node:
Using RESTCONF/HTTP for Leafref
Create a new "yang-leafref-example-1" node using RESTCONF:
POST /restconf/data/ HTTP/1.1
Host: example-server
Content-Type: application/yang-data+json
{ "yang-leafref-example-1": "false" }
If created successfully, the server response is:
HTTP/1.1 201 Created
Server: example-server
Location: https://example-server/restconf/data/yang-leafref-example-1
If the node already exists, the server returns a "409 Conflict".
Retrieve the created node:
GET /restconf/data/yang-leafref-example-1 HTTP/1.1
Host: example-server
Accept: application/yang-data+xml
Server response:
HTTP/1.1 200 OK
Server: example-server
Content-Type: application/yang-data+xml
<yang-leafref-example-1 xmlns="http://yumaworks.com/ns/example">false</yang-leafref-example-1>
Attempting to create "yang-leafref-example-2" without an existing instance of "yang-boolean-example":
POST /restconf/data/ HTTP/1.1
Host: example-server
Content-Type: application/yang-data+json
{ "yang-leafref-example-2": "false" }
Server response with error:
{
"ietf-restconf:errors": {
"error": [{
"error-type": "application",
"error-tag": "data-missing",
"error-app-tag": "instance-required",
"error-path": "/example:yang-leafref-example-2",
"error-message": "required value instance not found",
"error-info": {
"bad-value": "yang-leafref-example-2",
"error-number": 310
}
}]
}
}
Using yangcli-pro for Leafref
To create a new "yang-leafref-example-1" node using yangcli-pro client, the client might send the following commands:
create /yang-leafref-example-1 value=false
commit
To retrieve the created leafref leaf, the following request might be sent:
sget /yang-leafref-example-1
The server might respond with rpc-reply, containing below information:
<rpc-reply>
<data>
<yang-leafref-example-1
xmlns="http://yumaworks.com/ns/example">false
</yang-leafref-example-1>
</data>
</rpc-reply>
The following example illustrates how the leafref leaf can be set by using inline JSON file:
edit-config target=candidate config="""
{
"config": {
"yang-leafref-example-1": "false"
}
}
"""
The following example illustrates how the leafref leaf can be set by using inline XML file:
edit-config target=candidate config="""
<config>
<yang-leafref-example-1>false</yang-leafref-example-1>
</config>
"""
To retrieve the newly created leafref leaf, the following request might be sent:
sget /yang-leafref-example-1
The server might respond with rpc-reply, containing below information:
<rpc-reply>
<data>
<yang-leafref-example-1
xmlns="http://yumaworks.com/ns/example">false
</yang-leafref-example-1>
</data>
</rpc-reply>
yangcli-pro might parse the above message as:
rpc-reply {
data {
yang-leafref-example-1 false
}
}
Using DB-API for Leafref
Example of setting a leafref leaf value using DB-API:
/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function for leafref node.
**********************************************************************/
static void
send_test_edit (void)
{
const xmlChar *path_str = (const xmlChar *)"/";
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *value_str = (const xmlChar *)
"<config>"
"<yang-leafref-example-1 xmlns='http://yumaworks.com/ns/example'>false</yang-leafref-example-1>"
"</config>";
const xmlChar *patch_id_str = NULL;
boolean system_edit = FALSE;
status_t res = db_api_send_edit_ex(path_str,
operation_str,
value_str,
patch_id_str,
system_edit);
if (res != NO_ERR) {
log_error("\nSend test edit for leafref node failed %s %s = %s (%s)\n",
operation_str, path_str, value_str,
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test edit for leafref node OK %s %s = %s\n",
operation_str, path_str, value_str);
}
} /* send_test_edit */
Leafref Node Creation Using SIL
The following APIs and macros can be used with a leafref val value node. Refer to val.h:
val_make_simval_obj(): creates a val node.
val_get_leafref_typdef(): retrieves the base type.
VAL_STR(): retrieves the string value.
Following code example creates the value of leafref node and outputs it to the standard log output using val_make_simval_obj().
This is just an example of how the following API can be used:
...
/* create a new leafref type node */
val_value_t *return_val =
val_make_simval_obj(obj, (const xmlChar *)"false", &res);
if (return_val) {
typ_def_t *typdef = NULL;
/* As an example, get the value's type:
* If the value has LEAFREF base type, to get its actual type
* definition would required to call val_get_leafref_typdef()
* API that will return leafref actual type that it points at.
* Otherwise, regular macros could be used VAL_TYPDEF()
*/
if (VAL_BTYPE(return_val) == NCX_BT_LEAFREF) {
typdef = val_get_leafref_typdef(return_val);
} else {
typdef = VAL_TYPDEF(return_val);
}
/* Output the value to the standard output log */
log_info("\nCreated leafref node; type: %s; value: %s",
typdef->typenamestr,
VAL_STR(return_val));
}
...
Server logging example:
...
Created leafref node; type: boolean; value: false
...
Converting Leafref Values in RPC Input
In some RPC input scenarios, a leafref value may be presented to SIL code as a
leafref node where the val->v fields are not set for the leafref base type.
In that case, use val_convert_leafref() to convert the leafref node
to a "real" value node first.
/* inputval is the RPC input container passed to the SIL code */
val_value_t *child_newval =
val_find_child(inputval,
(const xmlChar *)"module_name",
(const xmlChar *)"leafref_node_name");
if ((child_newval != NULL) &&
(child_newval->res == NO_ERR)) {
val_value_t *realval = NULL;
val_value_t *useval = child_newval;
if (VAL_BTYPE(child_newval) == NCX_BT_LEAFREF) {
/* must be freed with val_free_value */
realval = val_convert_leafref(child_newval);
if (realval != NULL) {
useval = realval;
}
}
/* use base-type macros on useval (example shows uint32) */
log_debug("\nleafref_node_name='%u'", VAL_UINT32(useval));
val_free_value(realval);
}
How to Work with Union Data Type
The union built-in type represents a value that matches one of its member types. The "type" statement is repeatedly used to specify each member type of the union.
A union cannot be restricted directly. However, each member type can be restricted.
Determining Which Member Type Matched
The server checks union member types in order until a match is found.
If the value base type is union (NCX_BT_UNION), the typdef pointer in the val_value_t struct is set to the member type that matched. This is the most reliable way to determine which member type matched.
In addition, the YANG type name is available with val_get_yang_typename(). This is useful when multiple union member types share the same base type (for example, both members use base type string).
-
const xmlChar *val_get_yang_typename(val_value_t *val)
Get the YANG type name for this value node if there is one.
- Parameters:
val -- pointer to the value node to check
- Returns:
pointer to YANG type name, NULL if some error
#include "val.h"
#include "val_util.h"
if ((val != NULL) &&
(VAL_BTYPE(val) == NCX_BT_UNION)) {
/* The member typdef that matched. */
const typ_def_t *member_typdef = VAL_TYPDEF(val);
(void)member_typdef;
/* YANG type name for the matched member typdef. */
const xmlChar *type_name = val_get_yang_typename(val);
(void)type_name;
}
For example:
type union {
type ipv4-address;
type ipv6-address;
}
In this case, both member types have base type string, but val_get_yang_typename() returns "ipv4-address" or "ipv6-address" depending on which member type matched.
Accessing Union Nodes
There are multiple ways to access or modify a union node:
Consider the following YANG module example:
module example {
namespace "http://yumaworks.com/ns/example";
prefix "example";
revision 2019-07-30 {
description "Initial example module for union type";
}
leaf yang-union-example {
type union {
type int8;
type decimal64 { fraction-digits 3; }
}
}
}
Using RESTCONF/HTTP for Union
Create a new "yang-union-example" node using RESTCONF:
POST /restconf/data/ HTTP/1.1
Host: example-server
Content-Type: application/yang-data+json
{ "yang-union-example": "15" }
If created successfully, the server response is:
HTTP/1.1 201 Created
Server: example-server
Location: https://example-server/restconf/data/yang-union-example
Retrieve the created node:
GET /restconf/data/yang-union-example HTTP/1.1
Host: example-server
Accept: application/yang-data+xml
Server response:
HTTP/1.1 200 OK
Server: example-server
Content-Type: application/yang-data+xml
<yang-union-example xmlns="http://yumaworks.com/ns/example">15</yang-union-example>
Using yangcli-pro for Union
Create a new "yang-union-example" node using yangcli-pro:
create /yang-union-example value=15
commit
Retrieve the created union leaf:
sget /yang-union-example
Example server rpc-reply:
<rpc-reply>
<data>
<yang-union-example xmlns="http://yumaworks.com/ns/example">15</yang-union-example>
</data>
</rpc-reply>
Set the union leaf using inline JSON:
edit-config target=candidate config="""
{"config": {
"yang-union-example" : "15"
}
}
"""
Set the union leaf using inline XML:
edit-config target=candidate config="""
<config>
<yang-union-example>15</yang-union-example>
</config>
"""
yangcli-pro output example:
rpc-reply {
data {
yang-union-example 15
}
}
Using DB-API for Union
Example of setting a union leaf value using DB-API:
/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function for union node.
**********************************************************************/
static void
send_test_edit (void)
{
const xmlChar *path_str = (const xmlChar *)"/";
const xmlChar *operation_str = (const xmlChar *)"merge";
const xmlChar *value_str = (const xmlChar *)
"<config>"
"<yang-union-example xmlns='http://yumaworks.com/ns/example'>15</yang-union-example>"
"</config>";
const xmlChar *patch_id_str = NULL;
boolean system_edit = FALSE;
status_t res = db_api_send_edit_ex(path_str,
operation_str,
value_str,
patch_id_str,
system_edit);
if (res != NO_ERR) {
log_error("\nSend test edit for union node failed %s %s = %s (%s)\n",
operation_str, path_str, value_str,
get_error_string(res));
} else if (LOGDEBUG) {
log_debug("\nSend test edit for union node OK %s %s = %s\n",
operation_str, path_str, value_str);
}
} /* send_test_edit */
Union Node Creation Using SIL
The following API can be used with the union type val_value_t. Refer to val.h:
val_make_simval_obj(): create a leaf value using a string.
Following code example creates the value of a union node and outputs it to the standard log output using val_make_simval_obj().
status_t res = NO_ERR;
val_value_t *return_val =
val_make_simval_obj(obj, (const xmlChar *)"1.2", &res);
if (return_val) {
xmlChar *valstr = val_make_sprintf_string(return_val);
if (valstr == NULL) {
return;
}
log_info("\nCreated '%s' node; type:%s; value:%s",
VAL_NAME(return_val),
VAL_TYPDEF(return_val)->typenamestr,
valstr);
m__free(valstr);
}
The server might output the following logging information to the standard output:
Created 'yang-union-example' node; type:decimal64; value:1.2
YANG Tips
Additional topics about YANG constraints and metadata:
Leaf-list Examples
The following examples show how to construct a simple leaf-list data node and how to access and search leaf-list nodes.
These nodes are constructed the same way as regular leaf nodes. The only difference is that the "val_child_add" APIs allow multiple instances to be created, instead of just one.
Construct Leaf-list Example
The following example code shows how to construct 3 leaf-list siblings using the same API functions as for leaf node:
/* construct 3 leaf-list entries */
status_t res = NO_ERR;
val_value_t *leaflist_value1 =
val_make_simval_obj(leaflist_obj, (const xmlChar *)"53", &res);
if (!leaflist_value1) {
return res;
}
val_value_t *leaflist_value2 =
val_make_simval_obj(leaflist_obj, (const xmlChar *)"30", &res);
if (!leaflist_value2) {
return res;
}
val_value_t *leaflist_value3 =
val_make_simval_obj(leaflist_obj, (const xmlChar *)"80", &res);
if (!leaflist_value3) {
return res;
}
Alternatively, the following example can be used in order to construct multiple leaf-list entries of int32 type node and add them to the existent container parent:
status_t res = NO_ERR;
int32 value = 0;
for (value = 10; (value <= 15) && (res==NO_ERR); value++) {
val_value_t *leaflist_value =
agt_make_int_leaf(parentobj,
leaflistname,
value,
&res);
if (!leaflist_value) {
return res;
}
/* add a new leaf-list entry into target container */
res = val_child_add(leaflist_value, container_value);
if (res != NO_ERR) {
val_free_value(leaflist_value);
}
}
As a result, all the leaf-list entries will be added to the parent container.
Traverse Leaf-list Example
The following sample YANG module is used in this example:
module foo {
// ...
prefix f;
container test-cont {
leaf-list ll-node {
type string;
}
}
}
The following example code shows how to traverse leaf-list nodes the most efficient way:
/* parent -> is a "test-cont" container value */
val_value_t *chval =
val_child_find(parent,
(const xmlChar *)"foo",
(const xmlChar *)"ll-node");
for (; chval; chval = val_child_next_same(chval)) {
/* Check if the leaf-list value is not set to 'red';
* if so -> report an error.
*/
status_t res = NO_ERR;
int32 ret =
val_compare_to_string(chval,
(const xmlChar *)"red",
&res);
if (res == NO_ERR && !ret) {
/* Force an error if the value if the node is 'red' */
log_error("\nError: ll-node value 'red' is NOT supported");
res = ERR_NCX_OPERATION_NOT_SUPPORTED;
}
}
Important functions from the example:
-
val_value_t *val_child_find(const val_value_t *parent, const xmlChar *child_modname, const xmlChar *child_name)
Find the child node for the specified child name and modname.
- Parameters:
parent -- parent val to search
child_modname -- modname of child node (NULL to match any module)
child_name -- local-name of child node
- Returns:
pointer to found child or NULL
-
val_value_t *val_child_next_same(val_value_t *curnode)
Get the next node of the same type.
- Parameters:
curnode -- current list entry
- Returns:
pointer to next list entry or NULL if none
-
int32 val_compare_to_string(const val_value_t *val1, const xmlChar *strval2, status_t *res)
Compare a val_value_t struct value contents to a string.
Handles NCX_CL_BASE and NCX_CL_SIMPLE data classes by comparing the simple value.
!!!! Meta-value contents are ignored for this test !!!!
- Parameters:
val1 -- first value to check
strval2 -- second value to check
res -- [out] address of return status
*res return status
- Returns:
compare result
Adding Child Leafy Nodes
YANG data trees are usually constructed top-down. The parent is created and then children are created and added to the new parent. This procedure can be done in a nested fashion, to create any data tree.
The following APIs should be used to add leaf or leaf-list nodes to a parent in one step. These simplified APIs replace the older 2 step APIs that first create a child node and then use val_child_add to add the child to its parent.
API Function |
Description |
|---|---|
agt_add_leafy |
Add any data node using a string value |
agt_add_uint_leafy |
Add a numeric data node using a uint32 value |
agt_add_int_leafy |
Add a numeric data node using a int32 value |
agt_add_uint64_leafy |
Add a numeric data node using a uint64 value |
agt_add_int64_leafy |
Add a numeric data node using a int64 value |
agt_add_boolean_leafy |
Add a boolean data node using a boolean value |
agt_add_idref_leafy |
Add an identityref data node using a val_idref_t value |
agt_add_bits_leafy |
Add a bits data node using a ncx_list_t value |
Example:
The previous leaf-list example can be simplified using the agt_add_int_leafy API.
-
status_t agt_add_int_leafy(val_value_t *parentval, const xmlChar *modname, const xmlChar *leafname, int32 leafval)
make a child node from a int32 and add to parent.
OK for SIL or SIL-SA
Add a simple child to a container or list
Combines agt_make_int_leaf2 and val_child_add
Can be used for leaf or leaf-list
- Parameters:
parentval -- parent value node to get new child
modname -- name of module defining leaf (may be NULL to pick parent)
leafname -- name of leaf to find (namespace hardwired)
leafval -- number value for leaf
- Returns:
status
status_t res = NO_ERR;
int32 value = 0;
for (value = 10; value <= 15 && res==NO_ERR; value++) {
res = agt_add_int_leafy(container_value,
NULL, // modname
leaflistname,
value);
if (res != NO_ERR) {
// handle error, nothing to clean up here
}
}
Container Examples
The following examples shows how to construct a container data node.
Container nodes can be constructed in different ways depending on whether the container is top-level or nested.
If the container is not top-level, the following example shows how to find the object template for the target container and create an empty container value node:
/* get the container obj template */
obj_template_t *cont_obj =
obj_find_child(parentobj, modname, objname);
if (!cont_obj) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* make a container value */
val_value_t *cont_value = val_new_value();
if (!cont_value) {
return ERR_INTERNAL_MEM;
}
val_init_from_template(cont_value, cont_obj);
To ensure a top-level container exists in the running datastore (for example,
to add static configuration data during SIL init2), use
agt_add_top_node_if_missing.
Operational state data is normally returned using GET/GET2 callbacks rather than being inserted into the running datastore.
-
val_value_t *agt_add_top_node_if_missing(ncx_module_t *mod, const xmlChar *objname, boolean *added, status_t *res)
status_t res = NO_ERR;
boolean added = FALSE;
/* Ensure the top-level container exists in running; no defaults are added. */
const xmlChar *objname = (const xmlChar *)"my-top-container";
val_value_t *cont_value =
agt_add_top_node_if_missing(mod, objname, &added, &res);
if ((cont_value == NULL) || (res != NO_ERR)) {
return res;
}
The following example shows how multiple leaf-list entries can be added to the parent container:
status_t res = NO_ERR;
int32 value = 0;
for (value = 10; value <= 15 && res==NO_ERR; value++) {
val_value_t *leaflist_value =
agt_make_int_leaf(parentobj,
leaflistname,
value,
&res);
if (!leaflist_value) {
return res;
}
/* add a new leaf-list entry to parent container */
res = val_child_add(leaflist_value, cont_value);
if (res != NO_ERR) {
val_free_value(leaflist_value);
return res;
}
}
In order to construct nested containers, first ensure the top-level container exists, then create the child container and add it to the parent.
status_t res = NO_ERR;
boolean added = FALSE;
/* Ensure the top-level container exists in running */
const xmlChar *topname = (const xmlChar *)"my-top-container";
const xmlChar *childname = (const xmlChar *)"my-child-container";
val_value_t *cont_value =
agt_add_top_node_if_missing(mod, topname, &added, &res);
if ((cont_value == NULL) || (res != NO_ERR)) {
return res;
}
obj_template_t *topobj = VAL_OBJ(cont_value);
/* get the child container object template */
obj_template_t *cont_obj =
obj_find_child(topobj, NULL, childname);
if (!cont_obj) {
return ERR_NCX_DEF_NOT_FOUND;
}
/* construct the child container */
val_value_t *next_cont_value = val_new_value();
if (!next_cont_value) {
return ERR_INTERNAL_MEM;
}
val_init_from_template(next_cont_value, cont_obj);
res = val_child_add(next_cont_value, cont_value);
if (res != NO_ERR) {
val_free_value(next_cont_value);
return res;
}
List Example
The following example shows how to construct a list data node. Refer to the Child Data Nodes section for details on this procedure.
static val_value_t *
create_list_entry (obj_template_t *parentobj,
int32 key,
status_t *res)
{
/* get the obj template for the list obj */
obj_template_t *list_obj =
obj_find_child(parentobj,
(const xmlChar *)"my-module",
(const xmlChar *)"my-list");
if (!list_obj) {
*res = ERR_NCX_DEF_NOT_FOUND;
return NULL;
}
/* make the list node */
val_value_t *list_value = val_new_value();
if (!list_value) {
*res = ERR_INTERNAL_MEM;
return NULL;
}
val_init_from_template(list_value, list_obj);
/* add key leaf entry */
*res = agt_add_int_leafy(list_value, NULL, keyname, key);
if (*res != NO_ERR) {
val_free_value(list_value);
return NULL;
}
/* add an extra leaf entry */
*res = agt_add_uint_leafy(list_value,
NULL, // modname
(const xmlChar *)"my-other-leaf",
(uint32)32);
if (*res != NO_ERR) {
val_free_value(list_value);
return NULL;
}
/* generate the internal index Q chain */
*res = val_gen_index_chain(list_obj, list_value);
if (*res != NO_ERR) {
val_free_value(list_value);
return NULL;
}
return list_value;
}
In the next example, 5 list entries are created and added to the parent container 'contvalue'.
/* malloc and construct list values and add to container parent */
status_t res = NO_ERR;
int32 key = 0;
for (key = 10; key <= 15; key++) {
val_value_t *list_value =
create_list_entry(container_obj, key, &res);
if (!list_value) {
return res;
}
/* add a new list entry into target container */
res = val_child_add(list_value, contvalue);
if (res != NO_ERR) {
val_free_value(list_value);
return res;
}
}
Iterate Through List Entries
The following simple YANG module is used for illustration:
module example {
namespace "http://www.yumaworks.com/ns/example";
prefix example;
revision "2019-01-01";
container cont1 {
list list1 {
key "name";
leaf name {
type string;
}
}
}
}
The following example shows how to iterate through list entry values that have already been constructed under a parent value node.
/* parentval is a container value that has list entry children */
val_value_t *parentval;
val_value_t *chval = val_get_first_child(parentval);
for (; chval != NULL; chval = val_get_next_child(chval)) {
/* Find the key leaf (example name "name") in this list entry */
val_value_t *key_val =
val_find_child(chval,
(const xmlChar *)"example",
(const xmlChar *)"name");
if (key_val == NULL) {
/* list entries should always have keys */
continue;
}
/* process this list entry */
/* ... */
}