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

../_images/old_val_tree.png

New Design

../_images/new_val_tree.png

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.

  1. Create the list node, e.g., with val_new_value()

  2. Create the key leafs and add them to the list entry with val_child_add

  3. Create the list index chain with val_gen_index_chain

  4. 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.

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.

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.

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.

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

status_t res

validation result

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.

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

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.h is 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

agt_make_leaf2

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

Public Members

xmlns_id_t nsid

namespace ID

xmlChar *name

if nsid == INV_ID then this is entire QName

ncx_identity_t *identity

ID back-ptr if found.

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.

create_list_entry
   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 */
    /* ... */
}