../_images/logo.png

YumaPro Server Overview

SDK Introduction

This document describes the development of server instrumentation for YANG data models, using the YumaPro SDK programming environment.

  • Any YANG model not already implemented by the server can be added by a vendor to the server.

  • YANG data models are loaded into the server individually or in pre-defined bundles.

  • The server will automatically support the syntax and protocol mechanics from information in the YANG modules.

  • The SDK allows YANG model instrumentation to be added to the netconfd-pro server by a developer.

  • The semantics of the YANG models must be implemented by the developer.

    • The module-specific functionality comes from the underlying system, not the server.

    • System interfaces, system resource management, and "description statement" semantics are not automatically implemented by the server.

    • This instrumentation code is the boundary between the YANG model and the implementation-specific system.

  • The YumaPro Tools suite provides automated support for development and usage of network management information.

Terminology

Data Tree

  • The 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.

  • This definition is consistent with the term "data tree" defined in RFC 7950.

Object Tree

  • The Object Tree is a tree representation of all the YANG module RPC, data definition, and notification statements. It starts with a Root container. This is defined with a YANG container statement which has an ncx:root extension statement within it.

  • The <config> parameter within the <edit-config> operation is an example of an object node which is treated as a Root container. Each configuration database maintained by the server (E.g., <candidate> and <running>) has a Root container value node as its top-level object.

  • This is also called a "schema tree" in RFC 7950, but that is a more restrictive term than used here.

Root Container

  • The Root container is defined with a YANG container statement which has an ncx:root extension statement within it. The Root container does not have any child nodes defined in it within the YANG file. However, the YumaPro tools will treat this special container as if any top-level YANG data node is allowed to be a child node of the Root container type.

Schema Tree

  • The Schema Tree is a conceptual set of schema nodes derived from all YANG modules that a server is maintaining within a configuration database or other structure.

  • Each Schema Tree starts with a Root container, and any child nodes represent top-level YANG module data nodes that exist within the server.

  • The ncx:root extension is used to identify a YANG object that is being used as a root node of a schema tree.

  • This definition is consistent with the term "schema tree" defined in RFC 7950.

Server

  • Usually refers to software running in the netconfd-pro process.

  • Sometimes refers to the entire server, including all subsystems.

SIL

  • Server Instrumentation Library

  • Usually YANG module or bundle specific

  • Executed in the netconfd-pro process

  • Contains the 'glue code' that binds YANG content (managed by the netconfd-pro server), to a platform (networking device)

SIL-SA

  • Server Instrumentation Library : Sub Agent

  • Implicitly YANG module or bundle specific

  • Executed on a Subsystem

Subsystem

  • Refers to a YControl subsystem

  • Usually limited to one of SIL-SA, DB-API, or YP-HA

API Overview

API Map

YumaPro tools provide numerous APIs that can be used to effectively perform desired tasks, improve efficiency, refine auto-generated code, and extend default functionality.

../_images/API-map_11162021.png

YumaPro netconfd-pro API map key:

(1) Utility APIs

  • Notification functions provide control over notifications.

  • XPATH Handling/Validation functions can be used to manipulate XPATH expressions.

  • Error Handling functions provide various alternative ways to record an error.

  • Transaction Management functions provide control on the transaction and can be invoked within, before, and after the transaction.

  • Timer Service functions allow some SIL code that may need to be called at periodic intervals to check system status, update counters, and/or send notifications.

  • Object Tree manipulation functions let you retrieve object properties without accessing them directly.

  • Extension Access functions allow to manipulate with custom YANG extensions.

  • YANG Data Tree manipulation functions provide access functions to the data nodes.

(2) Server Instrumentation Library (SIL) Utility Functions

  • Provide control on SIL libraries and allows them to be used more efficiently.

(3) YControl and DB-API Interface Functions

  • Provide control on subsystem and DB-API interfaces.

(4) System Callback Functions

  • Allow the creation and use of general system SIL libraries.

(5) Database Access Functions

  • Allow validation, manipulation, and management of the configuration database.

(6) Access Control Functions

  • Allow configuration and enforcement of a vendor specific access control model (ACM).

(7) SYSLOG and Log Functions

  • Allow customized logging interface and preferences.

API Types

The YumaPro SDK provides several types of server APIs:

  • Transaction Management

    • Functions that provide control on the transaction and can be invoked within, before, and after the transaction.

  • Object Tree Manipulation

    • Functions to access the YANG Schema Tree

  • YANG Data Tree Manipulation

    • Functions that provide access functions to YANG data nodes within the NETCONF datastores.

  • Error Handling

    • Functions provide various alternative ways to record an error.

  • Notification Delivery

    • Functions to provide control of notifications.

  • Event Stream Callbacks

    • Provide info on whether any clients are listening to an event stream.

  • XPATH Handling/Validation

    • Functions can be used to manipulate XPATH expressions.

  • YANG Extension Access

    • Functions to allow to manipulate with custom YANG extensions.

  • Timer Service

    • Functions to allow SIL code to be called at periodic intervals to check system status, update counters, and/or perhaps send notifications.

  • SIL Utility

    • Functions to provide control on SIL libraries and allows to use them more efficiently.

  • System Callback

    • Functions allow to creating and use the general system library, not specific to one YANG module or bundle.

  • Access Control

    • Functions to allow to configure and enforce client permissions for YANG-based protocol access.

  • SYSLOG and Log Control

    • Functions to allow to customize logging.

    • Support for Linux SYSLOG or Vendor SYSLOG

  • Database Access

    • Functions to validate, manipulate, and manage the configuration data.

  • YCONTROL Interface

    • Functions to provide communication service between sub-agents and the main server.

  • DB-API Interface

    • Provides database interface functions to edit the internal database from another internal process.

Warning

YUMAWORKS RESERVES THE RIGHT TO CHANGE INTERNAL DATA STRUCTURES AT ANY TIME WITHOUT NOTICE! API FUNCTIONS ARE STABLE, NOT DATA STRUCTURES!

API Services

All of the major server functions are supported by service layers in the 'agt' or 'ncx' libraries:

  • Memory Management

    • Macros in platform/procdefs.h are used instead of using direct heap functions.

    • The macros 'm__getMem' or 'm__getObj' are used by YumaPro code to allocate memory.

    • Both of these functions increment a global counter called 'malloc_count'.

    • The macro 'm__free' is used to delete all malloced memory.

    • This macro increments a global counter called 'free_count'.

    • When a YumaPro program exits, it checks if 'malloc_count' equals 'free_count', and if not, generates an error message. If this occurs, the MEMTRACE=1 parameter can be added to the make command to activate 'mtrace' debugging.

  • Queue Management

    • APIs in ncx/dlq.h are used for all double-linked queue management.

  • XML Namespace Management

    • XML namespaces (including YANG module namespaces) are managed with functions in ncx/xmlns.h.

    • An internal "namespace ID" is used internally instead of the actual URI.

  • XML and JSON Parsing

    • XML and JSON input processing data structures and functions can be found in ncx/xml_util.h

  • XML and JSON Message Processing

    • XML and JSON message support (data structures and functions) can be found in ncx/xml_msg.h

  • XML and JSON Message Generation with Access Control

    • XML message generation is controlled through API functions located in ncx/xml_wr.h.

    • High level (value tree output) and low-level (individual tag output) XML output functions are provided, which hide all namespace, indentation, and other details.

    • Access control is integrated into XML message output to enforce the configured data access policies uniformly for all RPC operations and notifications.

    • The access control model is built-in and cannot be bypassed by any vendor-provided server instrumentation code.

  • XPath Services

    • All NETCONF XPath filtering, and all YANG XPath-based constraint validation, is handled with common data structures and API functions.

    • The XPath 1.0 implementation is native to the server, and uses the object and value trees directly to generate XPath results for NETCONF and YANG purposes.

    • NETCONF uses XPath differently than XSLT, and libxml2 XPath processing can be memory intensive. These functions are located in ncx/xpath.h, ncx/xpath1.h, and ncx/xpath_yang.h.

    • XPath filtered <get> responses are generated in agt/agt_xpath.c.

  • Logging Service

    • Encapsulates server output to a log file or to the standard output, filtered by a configurable log level.

    • Located in ncx/log.h.

    • The macro SET_ERROR in ncx/status.h is used to report programming errors to the log.

  • Session Management

    • All server activity is associated with a session.

    • The session control block and API functions are located in ncx/ses.h.

    • All input, output, access control, and protocol operation support is controlled through the session control block (ses_cb_t).

  • Timer Service

    • A periodic timer service is available to SIL modules for performing background maintenance within the main service loop.

    • These functions are located in agt/agt_timer.h.

  • Connection Management

    • All TCP connections to the netconfd-pro server are controlled through a main service loop, located in agt/agt_ncxserver.c.

    • The default netconfd-pro server actually listens for local <ncx-connect> connections on an AF_LOCAL socket.

    • The openSSH server listens for connections on port 830 (or other configured TCP ports), and the netconf-subsystem-pro thin client acts as a conduit between the SSH server and the server.

  • Database Management

    • All configuration databases use a common configuration template, defined in ncx/cfg.h.

    • Locking and other generic database functions are handled in this module.

    • The actual manipulation of the value tree is handled by API functions in

      • ncx/val.h

      • ncx/val_util.h

      • agt/agt_val_parse.h

      • agt/agt_val.h

      • agt/agt_child.h

  • NETCONF Operations

    • Most standard NETCONF RPC callback functions are located in agt/agt_ncx.c.

    • All operations are completely automated, so these operations cannot be replaced by vendor instrumentation.

  • NETCONF Request Processing

    • All <rpc> requests and replies use common data structures and APIs, found in ncx/rpc.h and agt/agt_rpc.h.

    • Automated reply generation, automatic filter processing, and message state data is contained in the RPC message control block.

  • NETCONF Error Reporting

    • All <rpc-error> elements use common data structures defined in ncx/rpc_err.h and agt/agt_rpcerr.h.

    • Most errors are handled automatically, but 'description statement' semantics need to be enforced by the SIL callback functions.

    • These functions use the API functions in agt/agt_util.h (such as agt_record_error) to generate data structures that will be translated to the proper <rpc-error> contents when a reply is sent.

  • YANG Library Management

    • All YANG modules are loaded into a common data structure (ncx_module_t) located in ncx/ncxtypes.h.

    • The API functions in ncx/ncxmod.h (such as 'ncxmod_load_module') are used to locate YANG modules, parse them, and store the internal data structures in a central library.

  • Availability Monitoring Service

    • The ypwatcher is a program that provides monitoring mechanism to netconfd-pro process and its state.

    • The ypwatcher program periodically checks the server state to determine if the server is still running.

    • If the server is no longer running it cleans up the state, restarts the server, and generates a syslog message.

YumaPro Doxygen Browser

The YumaPro server code now supports doxygen. The H files in the 20.10 release train have been redone so they conform to the appropriate doxygen and markdown commands. The HTML generation is supported.

The doxygen output is available online and can also be generated on a local machine if an SDK package or source code package is installed.

The SIL and SIL-SA code generated by yangdump-sdk now has built-in doxygen browser support.

It is strongly recommended that the browser be used to learn the APIs and access additional technical support resources.

The doxygen program is a widely available open-source program for generating source code documentation.

Online Version

The output for the latest YumaPro release is available online

Local Version

If the source code or a binary SDK package is installed, then the doxygen browser can be built and viewed is a browser as a file. The following scripts now support doxygen output.

The following message is printed after the script runs, showing the steps needed to access the local doxygen browser. (E.g. module

> make_sil_dir_pro test

   . . .

Run the following commands to get started:

  cd test
  make doc
  make opendoc

>

The doxygen files can be generated after this step is done.

The URL can also be access locally. The “index.html” file will be in the directory “output/html” under the module root created (E.g. “test” in this example).

Install doxygen

This step is not required to generate any server source code. It is only required to use doxygen.

The doxygen and graphviz packages are used to generate all the doxygen documentation, from your source code and the installed YumaPro H files.

These programs are usually already installed, but if not then try these commands:

Ubuntu:

sudo apt-get install doxygen graphviz

Fedora:

sudo dnf install doxygen graphviz

Centos:

sudo dnf --enablerepo=powertools install doxygen graphviz

Doxygen Group Structure

Doxygen uses a simple hierarchy to create the ‘Modules’ section of the WEB pages for the source code.

  • This hierarchy is hard-wired at this time.

  • The top-level group is called “yang-library” and the brief description is simply “YANG Library”.

    • The “ingroup” command for the auto-generated code will use this value

  • The 2nd-level group depends on the code that is being generated. There are parts to the group name

    • “sil-” or “silsa-” depending on code for SIL or SIL-SA

    • module name or bundle name

  • If the generated source file is for a module within a bundle, then there will be a 3rd level of grouping.

    • Each module will have a grouping within the bundle grouping

Example tree for SIL code for “module1”

+-- yang-library
    +
    |
    +-- sil-module1

Example tree for SIL-SA code for “bundle1”

+-- yang-library
    +
    |
    +-- silsa-bundle1

Example tree for SIL-SA code for “module1” and “module2” within “bundle1”

+-- yang-library
    +
    |
    +-- silsa-bundle1
        +
        |
        + silsa-bundle1-module1
        + silsa-bundle1-module2

Example Doxygen Headers

New GET2 callback for interface statistics:

/**
 * @brief Get database object callback for container statistics (getcb_fn2_t)\n
 * Path: container /interfaces/interface/statistics\n
 *
 * Fill in 'get2cb' response fields.
 *
 * @param get2cb GET2 control block for the callback.
 * @param k_if_name Ancestor key leaf 'name' in list 'interface'\n
 * Path: /if:interfaces/if:interface/if:name
 * @return return status of the callback.
 */
extern status_t u_if_statistics_get (
    getcb_get2_t *get2cb,
    const xmlChar *k_if_name);

New EDIT2 callback for interface:

/**
 * @brief Edit database object callback (agt_cb_fn_t)\n
 * Path: list /interfaces/interface
 *
 * @param scb session control block making the request
 * @param msg message in progress for this edit request
 * @param cbtyp callback type for this callback
 * @param editop the parent edit-config operation type,
 * which is also used for all other callbacks
 * that operate on objects.
 * @param newval container object holding the proposed changes
 * to apply to the current config, depending on
 * the editop value. Will not be NULL.
 * @param curval current container values from the "<running>"
 * or "<candidate>" configuration, if any. Could be NULL
 * for create and other operations.
 * @param k_if_name Local key leaf 'name' in list 'interface'\n
 * Path: /if:interfaces/if:interface/if:name
 * @return return status for the phase.
 */
extern status_t u_if_interface_edit (
    ses_cb_t *scb,
    rpc_msg_t *msg,
    agt_cbtyp_t cbtyp,
    op_editop_t editop,
    val_value_t *newval,
    val_value_t *curval,
    const xmlChar *k_if_name);

Software Overview

Server Design

This section describes the basic design used in the netconfd-pro server.

../_images/server_design_block.png

Initialization:

The netconfd-pro server will launch ypwatcher monitoring program, process the YANG modules, CLI parameters, config file parameters, and startup device NETCONF database, then wait for NETCONF sessions.

ncxserver Loop:

The SSH2 server will listen for incoming connections which request the 'netconf' subsystem.

When a new SSH session request is received, the netconf-subsystem-pro program is called, which opens a local connection to the netconfd-pro server, via the ncxserver loop.

NETCONF <rpc> requests are processed by the internal NETCONF stack. The module-specific callback functions (blue boxes) can be loaded into the system at build-time or run-time. This is the device instrumentation code, also called a server implementation library (SIL). For example, for 'libtoaster', this is the code that controls the toaster hardware.

Cleanup:

If the <shutdown> or <reboot> operations are invoked, then the server will cleanup. For a reboot, the init cycle is started again, instead of exiting the program.

YANG Native Operation

../_images/yang_native_operation.png

YumaPro uses YANG source modules directly to implement NETCONF protocol operations automatically within the server. The same YANG parser is used by all YumaPro programs. It is located in the 'ncx' source directory (libncx.so). There are several different parsing modes, which is set by the application.

In the 'server mode', the descriptive statements, such as 'description' and 'reference' are discarded upon input. Only the machine-readable statements are saved. All possible database validation, filtering, processing, initialization, NV-storage, and error processing is done, based on these machine readable statements.

For example, in order to set the platform-specific default value for some leaf, instead of hard-coded it into the server instrumentation, the default is stored in YANG data instead. The YANG file can be altered, either directly (by editing) or indirectly (via deviation statements), and the new or altered default value specified there.

In addition, range statements, patterns, XPath expressions, and all other machine-readable statements are all processed automatically, so the YANG statements themselves are like server source code.

YANG also allows vendor and platform-specific deviations to be specified, which are like generic patches to the common YANG module for whatever purpose needed. YANG also allows annotations to be defined and added to YANG modules, which are specified with the 'extension' statement. YumaPro uses some extensions to control some automation features, but any module can define extensions, and module instrumentation code can access these annotation during server operation, to control device behavior.

There are CLI parameters that can be used to control parser behavior such as warning suppression, and protocol behavior related to the YANG content, such as XML order enforcement and NETCONF protocol operation support. These parameters are stored in the server profile, which can be customized for each platform.

YANG Object Tree

../_images/yang_object_tree.png

The YANG statements found in a module are converted to internal data structures.

For NETCONF and database operations, a single tree of obj_template_t data structures is maintained by the server. This tree represents all the NETCONF data that is supported by the server. It does not represent any actual data structure instances. It just defines the data instances that are allowed to exist on the server.

Raw YANG vs. Cooked YANG:

Some of the nodes in this tree represent the exact YANG statements that the data modeler has used, such as 'augment', 'refine', and 'uses', but these nodes are not used directly in the object tree. They exist in the object tree, but they are processed to produce a final set of YANG data statements, translated into 'cooked' nodes in the object tree. If any deviation statements are used by server implementation of a YANG data node (to change it to match the actual platform implementation of the data node), then these are also 'patched' into the cooked YANG nodes in the object tree.

YANG Data Tree

../_images/yang_data_tree.png

A YANG data tree represents the instances of 1 or more of the objects in the object tree.

Each NETCONF database is a separate data tree. A data tree is constructed for each incoming message as well. The server has automated functions to process the data tree, based on the desired NETCONF operation and the object tree node corresponding to each data node.

Every NETCONF node (including database nodes) are distinguished with XML Qualified Names (QName). The YANG module namespace is used as the XML namespace, and the YANG identifier is used as the XML local name.

Each data node contains a pointer back to its object tree schema node. The value tree is comprised of the val_value_t structure. Only real data is actually stored in the value tree. For example, there are no data tree nodes for choices and cases. These are conceptual layers, not real layers, within the data tree.

The NETCONF server engine accesses individual SIL callback functions through the data tree and object tree. Each data node contains a pointer to its corresponding object node.

Each data node may have several different callback functions stored in the object tree node. Usually, the actual configuration value is stored in the database, However, virtual data nodes are also supported. These are simply placeholder nodes within the data tree, and usually used for non-configuration nodes, such as counters. Instead of using a static value stored in the data node, a callback function is used to retrieve the instrumentation value each time it is accessed.

Session Control Block

Once a NETCONF session is started, it is assigned a session control block for the life of the session. All NETCONF and system activity in driven through this interface, so the 'ncxserver' loop can be replaced in an embedded system.

Each session control block (ses_scb_t) controls the input and output for one session, which is associated with one SSH user name. Access control (see ietf-netconf-acm.yang) is enforced within the context of a session control block. Unauthorized return data is automatically removed from the response. Unauthorized <rpc> or database write requests are automatically rejected with an 'access-denied' error-tag.

The user preferences for each session are also stored in this data structure. They are initially derived from the server default values, but can be altered with the <set-my-session> operation and retrieved with the <get-my-session> operation.

Server Message Flows

../_images/main_server_components.png

The netconfd-pro server provides the following type of components:

  • NETCONF session management

  • NETCONF/YANG database management

  • NETCONF/YANG protocol operations

  • Access control configuration and enforcement

  • RPC error reporting

  • Notification subscription management

  • Default data retrieval processing

  • Database editing

  • Database validation

  • Subtree and XPath retrieval filtering

  • Dynamic and static capability management

  • Conditional object management (if-feature, when)

  • Memory management

  • Logging management

  • Timer services

All NETCONF and YANG protocol operation details are handled automatically within the netconfd-pro server. All database locking and editing is also handled by the server. There are callback functions available at different points of the processing model for your module specific instrumentation code to process each server request, and/or generate notifications. Everything except the 'description statement' semantics are usually handled

The server instrumentation stub files associated with the data model semantics are generated automatically with the yangdump-pro program. The developer fills in server callback functions to activate the networking device behavior represented by each YANG data model.

Main ncxserver Loop

../_images/netconf_server_io_loop.png

The agt_ncxserver loop does very little, and it is designed to be replaced in an embedded server that has its own SSH server:

  • A client request to start an SSH session results in an SSH channel being established to an instance of the netconf-subsystem-pro program.

  • The netconf-subsystem-pro program will open a local socket /tmp/ncxserver.sock and send a proprietary <ncx-connect> message to the netconfd-pro server, which is listening on this local socket with a select loop (in agt/agt_ncxserver.c).

  • When a valid <ncx-connect> message is received by netconfd-pro, a new NETCONF session is created.

  • After sending the <ncx-connect> message, the netconf-subsystem-pro program goes into 'transfer mode', and simply passes input from the SSH channel to the netconfd-pro server, and passes output from the netconfd-pro server to the SSH server.

  • The agt_ncxserver loop simply waits for input on the open connections, with a quick timeout. Each timeout, the server checks if a reboot, shutdown, signal, or other event occurred that needs attention.

  • Notifications may also be sent during the timeout check, if any events are queued for processing. The --max-burst configuration parameter controls the number of notifications sent to each notification subscription, during this timeout check.

  • Input <rpc> messages are buffered, and when a complete message is received (based on the NETCONF End-of-Message marker), it is processed by the server and any instrumentation module callback functions that are affected by the request.

When the agt_ncxserver_run function in agt/agt_ncxserver.c is replaced within an embedded system, the replacement code must handle the following tasks:

  • Call 'agt_ses_new_session' in agt/agt_ses.c when a new NETCONF session starts.

  • Call 'ses_accept_input' in ncx/ses.c with the correct session control block when NETCONF data is received.

  • Call 'agt_ses_process_first_ready' in agt/agt_ses.c after input is received. This should be called repeatedly until all serialized NETCONF messages have been processed.

  • Call 'agt_ses_kill_session' in agt/agt_ses.c when the NETCONF session is terminated.

  • The 'ses_msg_send_buffs' in ncx/ses_msg.c is used to output any queued send buffers.

  • The following functions need to be called periodically:

    • 'agt_shutdown_requested' in agt/agt_util.c to check if the server should terminate or reboot

    • 'agt_ses_check_timeouts' in agt/agt_ses.c to check for idle sessions or sessions stuck waiting for a NETCONF <hello> message.

    • 'agt_timer_handler' in agt/agt_timer.c to process server and SIL periodic callback functions.

    • 'send_some_notifications' in agt/agt_ncxserver.c to process some outgoing notifications.

SIL Callback Functions

../_images/netconf_callback_points.png
  • Top Level: The top-level incoming messages are registered, not hard-wired, in the server message processing design. The agt_ncxserver module accepts the <ncx-connect> message from netconf-subsystem-pro. The 'agt_rpc' module accepts the NETCONF <rpc> message. Additional messages can be supported by the server using the 'top_register_node' function.

  • All RPC operations are implemented in a data-driven fashion by the server. Each NETCONF operation is handled by a separate function in agt/agt_ncx.c. Any proprietary operation can be automatically supported, using the 'agt_rpc_register_method' function.

    Note

    Note: Once the YANG module is loaded into the server, all RPC operations defined in the module are available. If no SIL code is found, these will be dummy 'no-op' functions.

  • All database operations are performed in a structured manner, using special database access callback functions. Not all database nodes need callback functions. One callback function can be used for each 'phase', or the same function can be used for multiple phases. The 'agt_cb_register_callback' function in agt/agt_cb.c is used by SIL code to hook into NETCONF database operations.

SIL Callback Interface

This section briefly describes the SIL code that a developer will need to create to handle the data-model specific details. SIL functions access internal server data structures, either directly or through utility functions. Database mechanics and XML processing are done by the server engine, not the SIL code. A more complete reference can be found in section 5.

When a <rpc> request is received, the NETCONF server engine will perform the following tasks before calling any SIL:

  • parse the RPC operation element, and find its associated YANG rpc template

  • if found, check if the session is allowed to invoke this RPC operation

  • if the RPC is allowed, parse the rest of the XML message, using the 'rpc_template_t' for the RPC operation to determine if the basic structure is valid.

  • if the basic structure is valid, construct an rpc_msg_t data structure for the incoming message.

  • check all YANG machine-readable constraints, such as must, when, if-feature, min-elements, etc.

  • if the incoming message is completely 'YANG valid', then the server will check for an RPC validate function, and call it if found. This SIL code is only needed if there are additional system constraints to check. For example:

    • need to check if a configuration name such as <candidate> is supported

    • need to check if a configuration database is locked by another session

    • need to check description statement constraints not covered by machine-readable constraints

    • need to check if a specific capability or feature is enabled

  • If the validate function returns a NO_ERR status value, then the server will call the SIL invoke callback, if it is present. This SIL code should always be present, otherwise the RPC operation will have no real affect on the system.

  • At this point, an <rpc-reply> is generated, based on the data in the rpc_msg_t.

    • Errors are recorded in a queue when they are detected.

    • The server will handle the error reply generation for all errors it detects.

    • For SIL detected errors, the 'agt_record_error' function in agt/agt_util.h is usually used to save the error details.

    • Reply data can be generated by the SIL invoke callback function and stored in the rpc_msg_t structure.

    • Replay data can be streamed by the SIL code via reply callback functions. For example, the <get> and <get-config> operations use callback functions to deal with filters, and stream the reply by walking the target data tree.

  • After the <rpc-reply> is sent, the server will check for an RPC post reply callback function. This is only needed if the SIL code allocated some per-message data structures. For example, the rpc_msg_t contains 2 SIL controlled pointers ('rpc_user1' and 'rpc_user2'). The post reply callback is used by the SIL code to free these pointers, if needed.

The database edit SIL callbacks are only used for database operations that alter the database. The validate and invoke callback functions for these operations will in turn invoke the data-model specific SIL callback functions, depending on the success or failure of the edit request.

Server Initialization

This section describes the server initialization sequence in some detail. There are several phases in the initialization process, and various CLI parameters are defined to control the initialization behavior.

The file netconfd-pro/netconfd-pro.c contains the initial 'main' function that is used to start the server.

The platform-specific initialization code should be located in /usr/lib/yumapro/libyp_system.so. An example can be found in libsystem/src/example-system.c

The YANG module or bundle specific SIL or SIL-SA code is also initialized at startup if there are CLI parameters set to load them at boot-time.

Set the Server Profile

The agt_profile_t Struct defined in agt/agt.h is used to hold the server profile parameters.

The function 'init_server_profile' in agt/agt.c is called to set the factory defaults for the server behavior.

The function 'load_extern_system_code' in agt/agt.c is called to load the external system library. If found, the agt_system_init_profile_fn_t callback is invoked. This allows the external system code to modify the server profile settings.

Refer to the yp-system section of the YumaPro yp-system API Guide for more details.

Bootstrap CLI and Load Core YANG Modules

The 'ncx_init' function is called to setup core data structures. This function also calls the 'bootstrap_cli' function in ncx/ncx.c, which processes some key configuration parameters that need to be set right away, such as the logging parameters and the module search path.

The following common parameters are supported:

After the search path and other key parameters are set, the server loads some core YANG modules, including netconfd-pro.yang, which has the YANG definitions for all the CLI and configuration parameters.

External System Init Phase I : Pre CLI

The system init1 callback is invoked with the 'pre_cli' parameter set to 'TRUE'.

This callback phase can be used to register external ACM or logging callbacks.

Refer to yp_system_init1 for details on this callback.

Load CLI Parameters

Any parameters entered at the command line have precedence over values set later in the configuration file(s).

The following parameters can only be entered from the command line

  • The --config parameter specifies the configuration file to load.

  • The --no-config parameter specifies that the default configuration file /etc/yumapro/netconfd-pro.conf should be skipped.

  • The --help parameter causes help to be printed, and then the server will exit.

  • The --version parameter causes the version to be printed, and then the server will exit.

The command line parameters are loaded but not activated yet.

Load Main Configuration File

The --config parameter specifies a file or default configuration file is loaded.

The command line parameters are loaded but not activated yet. Any leaf parameters already entered at the command line are skipped.

The --confdir parameter specifies the location of a secondary configuration directory. This parameter can be set at the command line or the main configuration file.

The command line parameters are loaded but not activated yet.

Load Secondary Configuration Files

The --confdir parameter specifies the directory to check for extra configuration files.

Warning

DO NOT SET THIS PARAMETER TO THE SAME DIRECTORY AS THE MAIN CONFIGURATION FILE.

The default value is /etc/yumapro/netconfd-pro.d. This directory is not created by default when the program is installed. It must be created manually with the “mkdir” command.

All files ending in “.conf” will be loaded from this directory, if found.

The command line parameters are loaded but not activated yet.

Configuration Parameter Activation

After all CLI and configuration parameter files have been loaded they are applied to the agt_profile_t Struct and possibly other parameters within the server.

If the --version or --help parameters are entered, the server will exit after this step.

There are a small number of parameters that can be changed at run-time. Load the yumaworks-server.yang module to change these parameters at run-time.

External System Init Phase I : Post CLI

The system init1 callback is invoked with the 'pre_cli' parameter set to 'FALSE'.

This callback phase can be used to register most system callbacks and other preparation steps.

Load SIL Libraries and Invoke Phase I Callbacks

The --module CLI parameter specifies a module name to use for a SIL or SIL-SA library for a single module.

The --bundle CLI parameter specifies a bundle name to use for SIL or SIL-SA library for a set of modules.

The --loadpath CLI parameter specifies a set of directories to search for YANG modules, to load SIL and SIL-SA libraries from. Only modules not already loaded with --module or --bundle parameters will be loaded, if duplicate modules are found.

The init1 callbacks are invoked at this point. The SIL or SIL-SA code usually loads the appropriate YANG modules and registers callback functions.

External System Init Phase II : Pre-Load

The system init2 callback is invoked with the 'pre_load' parameter set to 'TRUE'.

This callback phase can be used to register an external NACM group callback and other preparation steps.

Refer to yp_system_init2 for details on this callback.

Load <running> Configuration

The --startup parameter specifies the XML file containing the YANG configuration data to load into the <running> datastore.

The --no-startup parameter specifies that this step will be skipped.

The --no-nvstore parameter specifies that this step will be skipped.

The --factory-startup parameter will cause the server to use the --startup-factory-file parameter value, or the default factory-startup-cfg.xml, or an empty configuration if none of these files are found.

If none of these parameters is entered, the default location is checked.

If an external handler is configured by registering an 'agt_nvload_fn' callback, this this function will be called to get the startup XML file, instead of the --startup of default locations.

If --fileloc-fhs=true, then the default file is /var/lib/netconfd-pro/startup-cfg.xml. If false, then the default file is $HOME/.yumapro/startup-cfg.xml.

If the default file startup-cfg.xml is not found, the the file factory-startup-cfg.xml will be checked, unless the --startup-factory-file parameter is set.

If the --startup-prune-ok parameter is set to TRUE, then unknown data nodes will be pruned from the <running> configuration. Otherwise unknown data nodes will cause the server to terminate with an error.

If the --startup-error parameter is set to continue, then errors in the parsing of the configuration data do not cause the server to terminate, if possible.

If --startup-error is set to fallback then the server will attempt to reboot with the factory default configuration.

If the --running-error parameter is set to continue, then errors in the root-check validation of the configuration data do not cause the server to terminate, if possible.

Note

Some OpenConfig (and other) modules have top-level mandatory nodes. For these modules use the following parameters:

  • startup-error=continue

  • running-error=continue

Invoke SIL Phase II Callbacks

The phase II callbacks for SIL and SIL-SA libraries are invoked.

This callback usually checks the <running> datastore and activates and configuration found.

Operational data is initialized in this phase as well.

External System Init Phase II : Post-Load

The system init2 callback is invoked with the 'pre_load' parameter set to 'FALSE'.

This callback phase can be used to load data into the server.

Initialize SIL-SA Subsystems

At this point, the server is able to accept control sessions from subsystems such as DB-API and SIL-SA. The init1 and init2 callbacks will be invoked for SIL-SA libraries. The SIL-SA must register callbacks in the init1 callback so the main server knows to include the subsystem in edit transactions. Phase II initialization for SIL-SA libraries is limited to activating the startup configuration data.

Server Ready

At this point the server is ready to accept management sessions for the configured northbound protocols.

Server Operation

This section briefly describes the server internal behavior for some basic NETCONF operations.

Ypwatcher processing

../_images/yumapro_ypwatcher_processing.png

After modules definition and configuration parameters are loaded successfully, the ypwatcher program is called to start the monitoring process.

After 'agt_cli_process_input' function process netconfd-pro input parameters. In order to process ypwatcher specific parameters this function checks for the existence of the watcher parameters in the input.

The ypwatcher program will be called by the 'agt_init1' function by default unless --no-watcher parameter will be specified or the program is already running.

It is often useful in debugging to turn off the ypwatcher so it does not interfere with intentional stoppage (e.g., stop at breakpoint).

To disable yp-watcher:

netconfd-pro --no-watcher

The ypwatcher program is running continuously and attempting to restart the server any time it exits unexpectedly.

Unexpected exit can be interpreted as a server's shut down process due to severe error, such as Segmentation fault, core dump, bus error, and invalid memory reference. The ypwatcher program will restart the server only if any of these termination actions causing the server to shut down.

Loading Modules and SIL Code

YANG modules and their associated device instrumentation can be loaded dynamically with the --module configuration parameter. Some examples are shown below:

module=foo
module=bar
module=baz@2009-01-05
module=~/mymodules/myfoo.yang
xmlChar *ncxmod_find_sil_file(const xmlChar *fname, boolean generrors, status_t *res)

Determine the location of the specified server instrumentation library file.

Parameters
  • fname -- SIL file name with extension

  • generrors --

    TRUE if error message should be generated

    FALSE if no error message

  • res -- address of status result

Return values

*res -- status

Returns

pointer to the malloced and initialized string containing the complete filespec or NULL if not found It must be freed after use!!!

  • The 'ncxmod_find_sil_file' function in ncx/ncxmod.c is used to find the library code associated with the each module name. The following search sequence is followed:

  • If the module parameter contains any sub-directories or a file extension, then it is treated as a file, and the module search path will not be used. Instead the absolute or relative file specification will be used.

  • If the first term starts with an environment variable or the tilde (~) character, and will be expanded first

  • If the 'at sign' (@) followed by a revision date is present, then that exact revision will be loaded.

  • If no file extension or directories are specified, then the module search path is checked for YANG and YIN files that match. The first match will be used, which may not be the newest, depending on the actual search path sequence.

  • The $YUMAPRO_MODPATH environment variable or --modpath configuration parameter can be used to configure one or more directory sub-trees to be searched.

  • The $YUMAPRO_HOME environment variable or --yumapro-home configuration parameter can be used to specify the YumaPro project tree to use if nothing is found in the correct directory or the module search path.

  • The $YUMAPRO_INSTALL environment variable or default YumaPro install location (/usr/share/yumapro/modules) will be used as a last resort to find a YANG or YIN file.

The server processes --module parameters by first checking if a dynamic library can be found which has an 'soname' that matches the module name. If so, then the SIL phase 1 initialization function is called, and that function is expected to call the 'ncxmod_load_module' function.

If no SIL file can be found for the module, then the server will load the YANG module anyway, and support database operations for the module, for provisioning purposes. Any RPC operations defined in the module will also be accepted (depending on access control settings), but the action will not actually be performed. Only the input parameters will be checked, and <or> or some <rpc-error> returned.

Core Module Initialization

The 'agt_init2' function in agt/agt.c is called after the configuration parameters have been collected.

  • Initialize the core server code modules

  • Static device-specific modules can be added to the agt_init2 function after the core modules have been initialized

  • Any 'module' parameters found in the CLI or server configuration file are processed.

  • The 'agt_cap_set_modules' function in agt/agt_cap.c is called to set the initial module capabilities for the ietf-netconf-monitoring.yang module

Startup Configuration Processing

After the static and dynamic server modules are loaded, the --startup (or --no-startup) parameter is processed by 'agt_init2' in agt/agt.c:

  • If the --startup parameter is used and includes any sub-directories, it is treated as a file and must be found, as specified.

  • Otherwise, the $YUMAPRO_DATAPATH environment variable or --datapath configuration parameter can be used to determine where to find the startup configuration file.

  • If neither the --startup or --no-startup configuration parameter is present, then the data search path will be used to find the default startup-cfg.xml

  • The $YUMAPRO_HOME environment variable or --yumapro-home configuration parameter is checked if no file is found in the data search path. The $YUMAPRO_HOME/data directory is checked if this parameter is set.

  • The $YUMAPRO_INSTALL environment variable is checked next, if the startup configuration is still not found.

  • The default location if none is found is the $HOME/.yumapro/startup_cfg.xml file.

It is a fatal error if a startup config is specified and it cannot be found.

As the startup configuration is loaded, any SIL callbacks that have been registered will be invoked for the association data present in the startup configuration file.. The edit operation will be OP_EDITOP_LOAD during this callback.

After the startup configuration is loaded into the running configuration database, all the stage 2 initialization routines are called. These are needed for modules which add read-only data nodes to the tree containing the running configuration. SIL modules may also use their 'init2' function to create factory default configuration nodes (which can be saved for the next reboot).

Process an Incoming <rpc> Request

../_images/rpc_processing_model.png
  • PARSE Phase

    • The incoming buffer is converted to a stream of XML nodes, using the xmlTextReader functions from libxml2.

    • The 'agt_val_parse' function is used to convert the stream of XML nodes to a val_value_t structure, representing the incoming request according to the YANG definition for the RPC operation.

    • An rpc_msg_t structure is also built for the request.

  • VALIDATE Phase

    • If a message is parsed correctly, then the incoming message is validated according to the YANG machine-readable constraints.

    • Any description statement constraints need to be checked with a callback function.

    • The 'agt_rpc_register_method' function in agt/agt_rpc.c is used to register callback functions.

  • INVOKE Phase

    • If the message is validated correctly, then the invoke callback is executed.

    • This is usually the only required callback function.

    • Without an invoke callback, the RPC operation will no affect.

    • This callback will set fields in the rpc_msg_t header that will allow the server to construct or stream the <rpc-reply> message back to the client.

  • REPLY Phase

    • Unless some catastrophic error occurs, the server will generate an <rpc-reply> response.

    • If the session is dropped before this phase then no reply will be sent.

    • If any <rpc-error> elements are needed, they are generated first.

    • If there is any response data to send, that is generated or streamed (via callback function provided earlier) at this time.

    • Any unauthorized data (according to the access control configuration) will be silently dropped from the message payload.

    • If there were no errors and no data to send, then an <ok> response is generated.

  • POST_REPLY Phase

    • After the response has been sent, a rarely-used callback function can be invoked to cleanup any memory allocation or other data-model related tasks.

    • For example, if the 'rpc_user1' or 'rpc_user2' pointers in the message header contain allocated memory then they need to be freed at this time.

Edit the Database

../_images/database_editing_model.png
  • Validate Phase

    • The server will determine the edit operation and the actual nodes in the target database (candidate or running) that will be affected by the operation.

    • All of the machine-readable YANG statements which apply to the affected node(s) are tested against the incoming PDU and the target database.

    • If there are no errors, the server will search for a SIL validate callback function for the affected node(s).

    • If the SIL code has registered a database callback function for the node or its local ancestors, it will be invoked.

    • This SIL callback function usually checks additional constraints that are contained in the YANG description statements for the database objects.

  • Apply Phase

    • If the validate phase completes without errors, then the requested changes are applied to the target database.

    • This phase is used for the internal data tree manipulation and validation only.

    • It is not used to alter device behavior.

    • Resources may need to be reserved during the SIL apply callback, but the database changes are not activated at this time.

  • Commit or Rollback Phase

    • If the validate and apply phases complete without errors, then then the server will search for SIL commit callback functions for the affected node(s) in the target database.

    • This SIL callback phase is used to apply the changes to the device and/or network.

    • It is only called when a commit procedure is attempted.

    • This can be due to a <commit> operation, or an <edit-config> or <copy-config> operation on the running database.

    • If there are errors during the commit phase, then the backup configuration will be applied, and the server will search for a SIL callback to invoke with a 'rollback operation'.

    • The same procedure is used for confirmed commit operations which timeout or canceled by the client.

Save the Database

The following bullets describe how the server saves configuration changes to non-volatile storage:

  • If the --with-startup=true parameter is used, then the server will support the :startup capability. In this case, the <copy-config> command needs to be used to cause the running configuration to be saved.

  • If the --with-startup=false parameter is used, then the server will not support the :startup capability. In this case, the database will be saved each time the running configuration is changed.

  • The <copy-config> or <commit> operations will cause the startup configuration file to be saved, even if nothing has changed. This allows an operator to replace a corrupted or missing startup configuration file at any time.

  • The database is saved with the 'agt_ncx_cfg_save' function in agt/agt_ncx.c.

  • The 'with-defaults' 'explicit' mode is used during the save operation to filter the database contents.

    • Any values that have been set by the client will be saved in NV-storage.

    • Any value set by the server to a YANG default value will not be saved in the database.

    • If the server create a node that does not have a YANG default value (E.g., containers, lists, keys), then this node will be saved in NV storage.

  • If the --startup parameter is used, then the server will save the database by overwriting that file. The file will be renamed to backup-cfg.xml first.

  • If the --no-startup parameter is used, or no startup file is specified and no default is found, then the server will create a file called 'startup-cfg.xml', in the following manner:

    • If the $YUMAPRO_HOME variable is set, the configuration will be saved in $YUMAPRO_HOME/data/startup-cfg.xml.

    • Otherwise, the configuration will be saved in $HOME/.yumapro/startup-cfg.xml.

  • The database is saved as an XML instance document, using the <config> element in the NETCONF 'base' namespace as the root element. Each top-level YANG module supported by the server, which contains some explicit configuration data, will be saved as a child node of the <nc:config> element. There is no particular order to the top-level data model elements.

Server YANG Modules

Refer to the YumaPro YANG Reference document for details on the server YANG modules.

Built-in Server Modules

There are several YANG modules which are implemented within the server, and not loaded at run-time like a dynamic SIL module. Some of them are IETF standard modules and some are YumaPro extension modules.

Optional Server Modules

There are YANG modules which are implemented outside the server in order to improve modularity and provide a choice of modules that can supplement the server with a new feature at start up or at run time. Some of them are NETCONF standard modules and some are YumaPro extension modules.

YANG Objects

This section describes the basic design of the YANG object tree and the API functions to access this tree.

Object Trees

The object tree is a tree representation of all the YANG module rpc, data definition, and notification statements. It starts with a 'root' container. This is defined with a YANG container statement which has an ncx:root extension statement within it. The <config> parameter within the <edit-config> operation is an example of an object node which is treated as a root container. Each configuration database maintained by the server (E.g., <candidate> and <running>) has a root container value node as its top-level object.

A root container does not have any child nodes defined in it within the YANG file. However, the YumaPro tools will treat this special container as if any top-level YANG data node is allowed to be a child node of the 'root' container type.

Object Tree Ordering

Top-Level Data Nodes are derived from:

  • data-def-stmt

  • rpc-stmt

  • notification-stmt

The YANG object tree is maintained in “schema order” wherever that is defined.

  • Top-Level Data Nodes:

    • These nodes are sorted:

      • Primary Key: local-name

      • Secondary Key: module-name

    • The top-level YANG objects from all loaded modules are present in the object tree (except if removed by status=obsolete or deviation=not-supported)

  • Child Data Nodes, RPC and Notification Nodes

    • Child nodes defined in the current module are in schema order (order they appear in the YANG module

    • Augmenting child nodes are in no defined order. They will be after all the real child nodes and the order may change depending on how the augmenting modules are loaded.

      • DO NOT DEPEND ON THE ORDER OF AUGMENTING DATA NODES

Object Node Types

There are 14 different YANG object node types, and a discriminated union of sub-data structures contains fields common to each sub-type. Object templates are defined in ncx/obj.h.

YANG Object Types

enum obj_type_t

enumeration for different YANG data def statement types the enum order is significant!!! do not change!!!

Values:

enumerator OBJ_TYP_NONE

not set

enumerator OBJ_TYP_ANYXML

This object represents a YANG 1.1 anydata data node.

enumerator OBJ_TYP_CONTAINER

This object represents a YANG presence or non-presence container.

  • If the ncx:root stmt present then the object represents a NETCONF database root, and no direct child nodes are expected

enumerator OBJ_TYP_LEAF

This object represents a YANG leaf data node.

enumerator OBJ_TYP_LEAF_LIST

This object represents a YANG leaf-list data node.

enumerator OBJ_TYP_LIST

This object represents a YANG list data node.

enumerator OBJ_TYP_CHOICE

This object represents a YANG choice schema node.

  • The only children allowed are case objects.

  • This object does not have instances in the data tree.

enumerator OBJ_TYP_CASE

This object represents a YANG case schema node.

  • This object does not have instances in the data tree.case: last named database object

enumerator OBJ_TYP_USES

This object represents a YANG uses schema node.

  • The contents of the grouping it represents will be expanded into object tree.

  • It is saved in the object tree even during operation, in order for the expanded objects to share common data.

  • This object does not have instances in the data tree.

  • Object has no name: The obj_get_name function returns the string 'uses'

enumerator OBJ_TYP_REFINE

This object represents a YANG refine statement.

  • It is used to alter the grouping contents during the expansion of a uses statement.

  • Must be a direct child of 'uses' stmt

  • It does not have instances in the data tree.

enumerator OBJ_TYP_AUGMENT

This object represents a YANG augment statement.

  • It is used to add additional child objects to an existing data definition statement.

  • This object is only allowed to be a child of a uses

  • statement or a top-level stateement within the module

  • It does not have instances in the data tree, however any children of the augment node will generate object nodes that have instances in the data tree.

enumerator OBJ_TYP_RPC

This object represents a YANG rpc statement.

  • It is used to define new <rpc> operations.

  • This object will only appear as a top-level statement within the module.

  • It does not have instances in the data tree.

  • Only 'rpcio' nodes are allowed to be children of an RPC node.

enumerator OBJ_TYP_RPCIO

This object represents a YANG input or output statement.

  • It is used to define input or output for an <rpc> operation.

  • This object will only appear as a child of an RPC node.

  • It does not have instances in the data tree.rpc input or output

enumerator OBJ_TYP_NOTIF

This object represents a YANG notification statement.

  • It is used to define new <notification> event types.

  • This object will only appear as a top-level statement within the module.

  • It does not have instances in the data tree.

enumerator OBJ_TYP_ANYDATA

This object represents a YANG 1.1 anydata data node.

enumerator OBJ_TYP_ACTION

This object represents a YANG 1.1 action schema node.

  • It is used to define a data-node specific operation.

  • The parent node must be a container or list

  • NMDA specifies all actions are accessed in <operational>not really data, YANG 1.1 only

obj_template_t

The following typedef is used to represent an object tree node:

struct obj_template_t

One YANG data-def-stmt.

Public Members

dlq_hdr_t qhdr

queue header

obj_type_t objtype

object type (def)

uint32 yang_hash

experimental: not used

uint32 flags

see OBJ_FL_* definitions

uint32 xflags

see OBJ_FL_* definitions

uint32 xflags2

see OBJ_FL_* definitions

uint32 uflags

see OBJ_FL_* definitions

uint8 silflags

see OBJ_FL_* definitions

uint8 sil_priority

picks SIL callback order

obj_testflags_t testflags

see AGT_TEST_FL_* definitions

obj_testflags_t desc_testflags

see AGT_TEST_FL_* definitions

ncx_error_t tkerr

file and line info for compiler

grp_template_t *grp

non-NULL == in a grp.datadefQ

obj_index_t index

object index for val_child ordering

obj_oid_t *oid

SNMP OID for this object (set if needed)

boolean set_snmp_flags

need SNMP flags

boolean xpath_oper_ok

object OK for referencing oper-data in XPath

struct obj_template_t_ *parent

backptr to parent

struct obj_template_t_ *usesobj

backptr to uses-obj if grouping expand

struct obj_template_t_ *augobj

backptr to augment-obj if augment expand

struct xpath_pcb_t_ *when

optional when clause

dlq_hdr_t metadataQ

Q of obj_metadata_t.

dlq_hdr_t appinfoQ

Q of ncx_appinfo_t.

dlq_hdr_t iffeatureQ

Q of ncx_iffeature_t.

dlq_hdr_t inherited_iffeatureQ

Q of obj_iffeature_ptr_t.

dlq_hdr_t inherited_whenQ

Q of ncx_backptr_t with node == xpath_pcb_t.

dlq_hdr_t *dataruleQ

Q obj NACM data-rule backptrs.

void *cbset

cbset is different based on the object type:

void *get2cb

get2fn is getcb_fn2_t for local GET2

struct ncx_module_t_ *mod

object module and namespace ID assigned at runtime this can be changed over and over as a uses statement is expanded.

The final expansion into a real object will leave the correct value in place

xmlns_id_t nsid

namespace ID assigned to the object at run-time

struct xpath_pcb_t_ *xpath_backptr[OBJ_NUM_XPATH_BACKPTRS]

xpath backptr caching support

dlq_hdr_t xpath_backptrQ

Q of ncx_back_ptr_t with node == xpath_pcb_t.

void *commit_test_cb

backptr to the commit_test record for this object which will only exist if this object has 'must' or 'when' statements defined within it

dlq_hdr_t *errmsgQ

custom error message used if this is the server running

void *yangmap_cb

if set, backptr to the nodemap in a YANG model mapping OBJ_IS_YANGMAP_SOURCE() indicates this is the source node OBJ_IS_YANGMAP_TARGET() indicates this is the target node

ncx_transaction_id_t edit_txid

current edit transaction ID for commit test pruning

ncx_transaction_id_t must_txid

current edit transaction ID for MUST test default nodes pruning

ncx_transaction_id_t when_txid

current edit transaction ID for WHEN test default nodes pruning

ncx_transaction_id_t leafref_txid

current edit transaction ID for intermediate leafref processing

union obj_template_t::def_ def
void *def_hook_cb

def_hook_cb is ncx_def_hook_cbfn_t callback function for Dynamic Default Hook callback.

union def_

object specific variants

Public Members

obj_container_t *container
obj_leaf_t *leaf
obj_leaflist_t *leaflist
obj_list_t *list
obj_choice_t *choic
obj_case_t *cas
obj_uses_t *uses
obj_refine_t *refine
obj_augment_t *augment
obj_rpc_t *rpc
obj_rpcio_t *rpcio
obj_notif_t *notif

obj_template_t Access Functions

The file ncx/obj.h contains many API functions so that object properties do not have to be accessed directly. The following table highlights the most commonly used functions. Refer to the H file for a complete definition of each API function.

obj_template_t Access Functions

obj_find_template

Find a top-level object template within a module.

obj_find_child

Find the specified child node within a complex object template . Skips over any nodes without names (augment, uses, etc.). Uses the module name that defines the obj_template_t to find a child.

obj_find_child_fast

Find the specified child node within a complex object template . Skips over any nodes without names (augment, uses, etc.). Uses the module namespace ID that to find a child.

obj_first_child

Get the first child node within a complex object template. Skips over any nodes without names.

obj_next_child

Get the next child node after the current specified child. Skips over any nodes without names.

obj_first_terminal_child

Get the first TERMINAL child object within a complex object template. Skips over any nodes without names.

obj_next_terminal_child

Get the next TERMINAL child object within a complex object template. Skips over any nodes without names.

obj_first_terminal_child_nokey

Get the first TERMINAL child object within a complex object template. Skips over any nodes without names and KEY leafs.

obj_next_terminal_child_nokey

Get the next TERMINAL child object within a complex object template. Skips over any nodes without names and KEY leafs.

obj_first_child_augok

Get the first child object within a complex object template. Skips over “refine” and “uses” nodes.

obj_next_child_augok

Get the next child object if the specified object has any children; return augment, not just obj_has_name. Skips over “refine” and “uses” nodes.

obj_first_child_deep

Get the first child node within a complex object template . Skips over any nodes without names, and also any choice and case nodes.

obj_next_child_deep

Get the next child object after the current specified child. Skips over any nodes without names, and also any choice and case nodes.

obj_previous_child

Get the previous child object if the specified object has any children. Skips over any nodes without names.

obj_last_child

Get the LAST child object within a complex object template. Skips over any nodes without names.

obj_find_case

Find the specified case object child object within the specific complex object node.

obj_find_type

Check if a typ_template_t in the obj typedefQ hierarchy.

obj_find_grouping

Check if a grp_template_t in the obj typedefQ hierarchy.

obj_find_key

Find a specific key component by key leaf identifier name.

obj_first_key

Get the first obj_key_t structure for the specified list object type.

obj_next_key

Get the next obj_key_t structure for the specified list object type.

obj_gen_object_id

Allocate and generate the YANG object ID for an object node.

obj_gen_object_id_prefix

Allocate and generate the YANG object ID for an object node. Uses the prefix in every node.

obj_gen_object_id_oid

Allocate and generate the YANG object ID for an object node. Uses the JSON/YANG module-name for prefix convention . Only print module name when namespace changes.

obj_gen_object_id_xpath

Allocate and generate the YANG object ID for an object node. Remove all conceptual choice and case nodes so the resulting string will represent the structure of the value tree for XPath searching.

obj_get_name

Get the object name string.

obj_set_name

Set the name field for this object.

obj_has_name

Return TRUE if the object has a name field.

obj_has_text_content

Return TRUE if the object has text content.

obj_get_status

Get the YANG status for the object.

obj_get_description

Get the YANG description statement for an object. Note that the server will always return a NULL pointer.

obj_get_alt_description

Get the alternate YANG description field for this obj ect. Check if any 'info', then 'help' appinfo nodes present.

obj_get_reference

Get the YANG reference statement for an object. Note that the server will always return a NULL pointer.

obj_get_config_flag

Get the YANG config statement value for an object.

obj_get_max_access

Get the YANG NCX max-access enumeration for an object. Return the explicit value or the inherited value.

obj_get_typestr

Get the name string for the type of an object.

obj_get_default

Get the YANG default value for an object.

obj_get_next_default

Get the next YANG default value for the specified object. Only leaf-list object type is supported.

obj_get_default_case

Get the name of the default case for a choice object.

obj_npcon_has_defaults

Check if the specified NP container has defaults within it. Must be a config object.

obj_get_level

Get the nest level for the specified object . Top-level is '1' . Does not count groupings as a level.

obj_has_typedefs

Check if the object has any nested typedefs in it . This must only be called if the object is defined in a grouping.

obj_get_typdef

Get the internal type definition for the leaf or leaf-list object.

obj_get_basetype

Get the internal base type enumeration for an object.

obj_get_mod_prefix

Get the module prefix for an object.

obj_get_mod_xmlprefix

Get the module prefix (XML format) for this object.

obj_get_mod_name

Get the module name containing an object.

obj_get_mod_version

Get the module revision date for the module containing an object.

obj_in_submodule

Check if the object is defined in a submodule.

obj_get_nsid

Get the internal XML namespace ID for an object.

obj_get_min_elements

Get the YANG min-elements value for a list or leaf-list object.

obj_get_max_elements

Get the YANG max-elements value for a list or leaf-list object.

obj_get_units

Get the YANG units field for a leaf or leaf-list object.

obj_get_parent

Get the parent object node for an object.

obj_get_real_parent

Get the parent object node for an object. Skips over choice and case.

obj_get_presence_string

Get the YANG presence statement for a container object.

obj_get_child_count

Get the number of child nodes for a complex object.

obj_get_fraction_digits

Get the YANG fraction-digits statement for a decimal64 leaf or leaf-list object.

obj_is_anyxml

Return TRUE if the object is YANG anyxml type.

obj_is_anydata

Return TRUE if the object is YANG anydata type.

obj_is_leaf

Return TRUE if the object is YANG leaf type.

obj_is_crypt_hash

Check if the object is a leaf of type crypt-hash. Return TRUE if the object is YANG crypt-hash leaf type.

obj_is_list

Return TRUE if the object is YANG list type.

obj_is_key

Return TRUE if the object is YANG key type.

obj_is_leafy

Return TRUE if the object is YANG leaf or leaf-list type.

obj_get_leaf_list_defset

Get the defset flag for a leaf-list. Return TRUE if leaf-list with original defaults.

obj_find_defval

Find a default for leaf-list. Return TRUE if found.

obj_is_container

Return TRUE if the object is YANG container type.

obj_is_choice

Return TRUE if the object is YANG choice type.

obj_is_case

Return TRUE if the object is YANG case type.

obj_is_uses

Return TRUE if the object is YANG uses type.

obj_is_terminal

Return TRUE if the object is YANG leaf, leaf-list, anyxml, or anydata type.

obj_is_mandatory

Return TRUE if the object is YANG mandatory statement.

obj_is_mandatory_when

Return TRUE if the object is YANG mandatory, but first check if any when statements are FALSE first.

obj_has_when_stmts

Check if any when statements apply to this object . Does not check if they are true, just any when statements present. Return TRUE if object has any when statement associated with it.

obj_is_cloned

Return TRUE if the object is expanded from a grouping or augment statement.

obj_is_augclone

Return TRUE if the object is expanded from an augment statement.

obj_is_augment

Return TRUE if the object is YANG augment statement.

obj_is_external_augment

Check if an object is an external augment. Return TRUE if an augment to another module.

obj_is_external_data_augment

Check if an object is an external augment of a data node. Return TRUE if an augment to data node in another module.

obj_is_refine

Return TRUE if the object is YANG refine statement.

obj_is_data

Check if the object is defined within data or within a notification or RPC instead.

obj_is_data_db

Return TRUE if the object is defined within a YANG database definition.

obj_is_data_node

Return TRUE if the object is a real node type.

obj_is_rpc

Return TRUE if the object is a YANG RPC type.

obj_rpc_has_input

Check if the RPC object has any real input children. Return TRUE if there are any input children.

obj_rpc_has_output

Check if the RPC object has any real output children. Return TRUE if there are any output children.

obj_is_rpcio

Return TRUE if the object is a YANG RPCIO type.

obj_is_action

Return TRUE if the object is a YANG action type.

obj_is_notif

Return TRUE if the object is a YANG notification type.

obj_is_empty

Return TRUE if object is empty of subclauses.

obj_in_rpc

Return TRUE if the object is defined within an RPC statement.

obj_in_notif

Return TRUE if the object is defined within a notification statement.

obj_is_xsdlist

Return TRUE if the object is marked as an XSD list.

obj_is_hidden

Return TRUE if object contains the ncx:hidden extension.

obj_is_root

Return TRUE if object contains the ncx:root extension.

obj_is_password

Return TRUE if object contains the ncx:password extension.

obj_is_cli

Return TRUE if object contains the ncx:cli extension.

obj_is_abstract

Return TRUE if object contains the ncx:abstract extension.

obj_is_xpath_string

Return TRUE if the object is a leaf or leaf-list containing an XPath string.

obj_is_schema_instance_string

Return TRUE if the object is a leaf or leaf-list containing a schema instance identifier string.

obj_is_secure

Return TRUE if object contains the nacm:secure extension.

obj_is_very_secure

Return TRUE if object contains the nacm:very-secure extension.

obj_is_sil_delete_children_first

Check if object is marked as ncx:sil-delete-children-first. Return TRUE if object contains ncx:sil-delete-children-first extension.

obj_is_block_user_create

Check if object is marked as ncx:user-write with create access disabled. Return TRUE if object is marked to block user create access.

obj_is_block_user_update

Check if object is marked as ncx:user-write with update access disabled.

Return TRUE if object is marked to block user update access.

obj_is_block_user_delete

Check if object is marked as ncx:user-write with delete access disabled.

Return TRUE if object is marked to block user delete access.

obj_is_system_ordered

Return TRUE if the list or leaf-list object is system ordered; FALSE if it is user ordered.

obj_is_np_container

Return TRUE if the object is a YANG non presence container.

obj_is_p_container

Return TRUE if the object is a YANG presence container.

obj_is_enabled

Return TRUE if the object is enabled; FALSE if any if-feature, when-stmt, or deviation-stmt has removed the object from the system.

obj_has_iffeature

Return TRUE if object has any if-feature statements.

obj_is_single_instance

Return TRUE if object is a single instance object.

obj_sort_children

Rearrange any child nodes in YANG schema order

obj_is_top

Check if the object is top-level object within the YANG module that defines it. Return TRUE if obj is a top-level object.

obj_is_datapath

Check if object is marked as a ywx:datapath object. Return TRUE if object is marked as datapath.

obj_has_children

Check if there are any accessible nodes within the object. Return TRUE if there are any accessible children.

obj_has_ro_children

Check if there are any accessible read-only child nodes within the object. Return TRUE if there are any accessible read-only children.

obj_has_ro_descendants

Check if there are any accessible read-only descendant nodes within the object. Return TRUE if there are any accessible read-only descendant.

obj_has_rw_children

Check if there are any accessible read-write child nodes within the object. Do not count list keys in this check. Return TRUE if there are any accessible read-write children.

obj_enabled_child_count

Get the count of the number of enabled child nodes for the object template. Returns number of enabled child nodes.

obj_get_keystr

Get the key string for this list object. Returns pointer to key string or NULL if none or not a list.

obj_is_supported

Check an RPC node to check if it is supported or not . It could be disabled at run-time without removing it. Return TRUE if object is supported.

obj_is_dup_local

Check if this object is one that has a duplicate sibling with the same local-name and different module namespace. Return if flagged as duplicate local-name.

obj_is_exclusive_rpc

Return TRUE if exclusive-rpc set; cannot be called by multiple sessions at once.

obj_parent_same_module

Check if the object parent object is the same. Return TRUE if parent from the same module or not sure. Return FALSE if parent not the same module and parent is not root.

obj_set_sil_priority

Set the SIL priority field.

obj_get_sil_priority

Get the SIL priority field.

obj_has_config_defaults

Check if the object itself or child nodes can have a default. Return TRUE if can have a default.

Object Tree Handling Examples

There are various API functions that can be used to locate desired objects. They differ widely by available information that you have prior the search and by desired search precision.

Find Top-Level Object

Find With the Object Name

If the top-level object name is unique, it is safe to find it by just its name.

obj_template_t *ncx_find_any_object(const xmlChar *objname)

Find any top-level object in any YANG module.

Check if an obj_template_t in in any module that matches the object name string

Parameters

objname -- object name to match

Returns

pointer to struct if present, NULL otherwise

const xmlChar *objname = (const xmlChar *)"interfaces";
obj_template_t *topobj = ncx_find_any_object(objname);
if (!topobj) {
    res = ERR_NCX_DEF_NOT_FOUND;
}

Find With the Module Pointer

If you know the module where the target object is located, and have the "ncx_module_t" pointer for the module, the following retrieval functions can be used:

obj_template_t *obj_find_template_top(ncx_module_t *mod, const xmlChar *modname, const xmlChar *objname)

Check if an obj_template_t in the mod->datadefQ or any of the include files visible to this module.

Top-level access is not tracked, so the 'test' variable is hard-wired to FALSE

Parameters
  • mod -- ncx_module to check

  • modname -- module name for the object (needed for augments) (may be NULL to match any 'objname' instance)

  • objname -- object name to find

Returns

pointer to struct if present, NULL otherwise

In this API function, the "module_name" parameter can be set to NULL.

obj_template_t *topobj =
    obj_find_template_top(mod, module_name, object_name);
if (!topobj) {
    res = ERR_NCX_DEF_NOT_FOUND;
}
obj_template_t *ncx_find_object(ncx_module_t *mod, const xmlChar *objname)

Find a top level module object.

Parameters
  • mod -- ncx_module to check

  • objname -- object name to find

Returns

pointer to struct if present, NULL otherwise

obj_template_t *topobj = ncx_find_object(mod, object_name);
if (!topobj) {
    res = ERR_NCX_DEF_NOT_FOUND;
}

Find With the Module Namespace ID

If the module namespace is known, then use this API function.

obj_template_t *ncx_find_object_nsid(xmlns_id_t nsid, const xmlChar *objname)

Find a top level module object by module NSID.

Parameters
  • nsid -- namespace ID to check

  • objname -- object name

Returns

pointer to struct if present, NULL otherwise

/* get the top object node using NSID of the OBJ */
obj_template_t *topobj = ncx_find_object_nsid(nsid, object_name);
if (!topobj) {
    res = ERR_NCX_DEF_NOT_FOUND;
}

Access Module Pointer

A module is usually loaded as server boot-time, but it can be loaded at run-time with the <load> or <load-bundle> operations.

The following code illustrates how the module pointer can be obtained during the module load procedure:

status_t ncxmod_load_module(const xmlChar *modname, const xmlChar *revision, dlq_hdr_t *savedevQ, ncx_module_t **retmod)

Determine the location of the specified module and then load it into the system, if not already loaded Main API for this module.

This is the only load module variant that checks if there are any errors recorded in the module or any of its dependencies !!! ONLY RETURNS TRUE IF MODULE AND ALL IMPORTS ARE LOADED OK !!!

Parameters
  • modname -- module name with no path prefix or file extension

  • revision -- optional revision date of 'modname' to find

  • savedevQ -- Q of ncx_save_deviations_t to use, if any

  • retmod -- address of return module (may be NULL)

Return values

*retmod -- pointer to requested module version

Returns

status

static status_t
     init_function (const xmlChar *modname,
                    const xmlChar *revision)
{

    ncx_module_t *mod = NULL;
    res = ncxmod_load_module(modname,
                             revision,
                             agt_get_savedevQ(),
                             &mod);
    if (res != NO_ERR) {
        return res;
    }

    // the "mod" pointer should be set if res == NO_ERR
}

To locate a module that is already loaded, use the following API function:

ncx_module_t *ncx_find_module(const xmlChar *modname, const xmlChar *revision)

Find a ncx_module_t in the ncx_sesmodQ.

These are the modules that are already loaded Does not look for submodule names if no module name found

Parameters
  • modname -- module name for the new module

  • revision -- module revision date for the new module (may be NULL)

Returns

module pointer if found or NULL if not found

// find a specific revision of the module
ncx_module_t *mod = ncx_find_module(modname, revision);
if (mod == NULL) {
    // find any revision of the module
    ncx_module_t *mod = ncx_find_module(modname, NULL);
    if (mod == NULL) {
        res = ERR_NCX_MOD_NOT_FOUND;
    }
}

Match Any Object

The second option to locate a top level object is to search through all the modules and try to match the object name string. The following API functions illustrate how to locate the top level object based on module name:

obj_template_t *ncx_match_any_object(const xmlChar *objname, ncx_name_match_t name_match, boolean alt_names, status_t *retres)

Match any object in any YANG module with extra search options.

Check if an obj_template_t in any module that matches the object name string (objname).

Use name_match to specify the type of name match desired Use alt_names to check objname as the alt-name for the object. This is usually only used in the CLI.

Parameters
  • objname -- object name to match

  • name_match -- name match mode enumeration

  • alt_names -- TRUE if alternate names should be checked after regular names; FALSE if not

  • retres -- address of return status

Return values

*retres -- return status

Returns

pointer to struct if present, NULL otherwise

status_t res = NO_ERR;
obj_template_t *topobj =
    ncx_match_any_object(object_name, NCX_MATCH_FIRST, alt_names, &res);

In the above example, the NCX_MATCH_FIRST parameter dictates the API function to stop search on the first match. Alternatively, the parameter can be changed to the following options:

Match Mode

Description

NCX_MATCH_EXACT

Try a case-sensitive exact-length match

NCX_MATCH_EXACT_NOCASE

Try a case-insensitive exact-length match

NCX_MATCH_ONE

Try a case-sensitive partial-name match

NCX_MATCH_ONE_NOCASE

Try a case-insensitive partial-name match

NCX_MATCH_FIRST

Try a case-sensitive first match

NCX_MATCH_FIRST_NOCASE

Try a case-insensitive first match

The 'alt_names' parameter dictates if alternative names should be checked in addition to the YANG node names. If set to TRUE the check will be applied; if set to FALSE the check will be ignored.

The '*res' return parameter may be set to ERR_NCX_MULTIPLE_MATCHES in case there are multiple matching objects available. In this case the function will not return any of the object templates. To search with the highest precision for a specific object. use NCX_MATCH_EXACT value and set alt_names to FALSE.

There is an extended version of the previous API function:

obj_template_t *ncx_match_any_object_ex(const xmlChar *modname, const xmlChar *objname, boolean dataonly, ncx_name_match_t name_match, boolean alt_names, status_t *retres)

Match any object in any YANG module with double extra search options.

Check if an obj_template_t in in any module that matches the object name string; extended parameters

Use name_match to specify the type of name match desired Use alt_names to check objname as the alt-name for the object. This is usually only used in the CLI. Use dataonly to search only for datastore objects

Parameters
  • modname -- module name of object (may be NULL to search all)

  • objname -- object name to match

  • dataonly -- TRUE for data nodes only; FALSE for any object type

  • name_match -- name match mode enumeration

  • alt_names -- TRUE if alternate names should be checked after regular names; FALSE if not

  • retres -- address of return status

Return values

*retres -- return status

Returns

pointer to struct if present, NULL otherwise

status_t res = NO_ERR;
obj_template_t *topobj =
     ncx_match_any_object_ex(modname,
                             object_name,
                             dataonly,
                             NCX_MATCH_EXACT,
                             alt_names,
                             &res);

In the above example, the module name is optional; however it is recommended. If it is present the API function will search only within specified module and will not try to search through all the modules.

The 'dataonly' parameter specifies whether the search function should try to match only data nodes. Set to "false" to match any node.

Check For Child Nodes

In order to verify if the current object has any available children, use the following API function:

boolean obj_has_children(obj_template_t *obj)

Check if there are any accessible nodes within the object.

Parameters

obj -- obj_template to check

Returns

TRUE if there are any accessible children

FALSE if no datadb child nodes found

boolean has_children = obj_has_children(topobj);

If there are any accessible data nodes within the object the function returns "true".

Find a Specific Child Node

The following example code illustrates how to locate a specific child object of the current top level object. The following API function checks for accessible names only. That means child nodes of choice, case will be present instead of the choice name or case name:

obj_template_t *obj_find_child(obj_template_t *obj, const xmlChar *modname, const xmlChar *objname)

Find a child object with the specified Qname.

This is the main API for finding object child nodes

Parameters
  • obj -- obj_template_t to check

  • modname -- module name for the object (needed for augments) (may be NULL to match any 'objname' instance)

  • objname -- object name to find

Returns

pointer to obj_template_t or NULL if not found

/* get the child obj template */
obj_template_t *child_obj = obj_find_child(topobj, modname, objname);

Find Choice or Case Child Node

To locate choice or case objects as well as other accessible names, use the following API function.

This function checks not only for accessible names. That means the choice name or case name will be preferred instead of their child nodes:

obj_template_t *obj_find_child_choice_case(obj_template_t *obj, const xmlChar *modname, const xmlChar *objname)

Find a child object with the specified Qname.

Parameters
  • obj -- the obj_template_t to check

  • modname -- the module name that defines the obj_template_t if it is NULL and first match will be done, and the module ignored (Name instead of QName)

  • objname -- the object name to find

Returns

pointer to obj_template_t or NULL if not found

/* get the child obj template */
obj_template_t *child_obj =
    obj_find_child_choice_case(topobj, modname, objname);

There is also an extended version of the previous API function:

obj_template_t *obj_find_child_ex(obj_template_t *obj, const xmlChar *modname, const xmlChar *objname, ncx_name_match_t match_names, boolean alt_names, boolean dataonly, status_t *retres)

Find a child object with the specified Qname extended match modes.

Parameters
  • obj -- obj_template_t to check

  • modname -- module name for the object (needed for augments) (may be NULL to match any 'objname' instance)

  • objname -- object name to find

  • match_names -- enum for selected match names mode

  • alt_names --

    TRUE if alt-name should be checked in addition to the YANG node name

    FALSE to check YANG names only

  • dataonly --

    TRUE to check just data nodes

    FALSE to check all nodes

  • retres -- address of return status

Return values

*retres -- set to return status

Returns

pointer to struct if present, NULL otherwise

/* get the child obj template */
status_t res = NO_ERR;
obj_template_t *child_obj =
    obj_find_child_ex(topobj,
                      modname,
                      objname,
                      NCX_MATCH_FIRST,
                      alt_names,
                      dataonly,
                      &res);

Object Child Node Traversal

To traverse the child nodes of a parent node (container, list, choice, case), the following set of API functions are used.

To get the first child object of the current object, the following API function can be used. Note, that this function skips over augments and uses:

obj_template_t *obj_first_child(obj_template_t *obj)

Get the first child object if the specified object has any children.

!!!! SKIPS OVER AUGMENT AND USES AND DISABLED OBJECTS !!!!

Parameters

obj -- obj_template_t to check for children

Returns

pointer to first child obj_template_t or NULL if not found

obj_template_t *first_obj = obj_first_child(topobj);

Similarly, the following API function can be used to locate the first terminal object. Note, that this function also skips over augments and uses:

obj_template_t *obj_first_terminal_child(obj_template_t *obj)

Get the first child object if the specified object has any children; MUST BE A TERMINAL NODE!!!

!!!! SKIPS OVER AUGMENT AND USES AND DISABLED OBJECTS !!!!

Parameters

obj -- obj_template_t to check for children

Returns

pointer to first child obj_template_t or NULL if not found

obj_template_t *first_obj = obj_first_terminal_child(topobj);

In order to get the next, the last, parent or previous child object or the terminal child object the following API functions can be used:

obj_template_t *obj_next_child(obj_template_t *obj)

Get the next child object if the specified object has any children.

!!!! SKIPS OVER AUGMENT AND USES !!!!

Parameters

obj -- obj_template_t to check

Returns

pointer to next child obj_template_t or NULL if not found

obj_template_t *next_obj = obj_next_child(first_obj);
obj_template_t *obj_next_terminal_child(obj_template_t *obj)

Get the next child object if the specified object has any children; MUST BE TERMINAL NODE!!

!!!! SKIPS OVER AUGMENT AND USES !!!!

Parameters

obj -- obj_template_t to check

Returns

pointer to next terminal child obj_template_t or NULL if not found

obj_template_t *next_obj = obj_next_terminal_child(first_obj);
obj_template_t *obj_previous_child(obj_template_t *obj)

Get the previous child object if the specified object has any children.

!!!! SKIPS OVER AUGMENT AND USES !!!!

Parameters

obj -- obj_template_t to check

Returns

pointer to previous child obj_template_t or NULL if not found

obj_template_t *prev_obj = obj_previous_child(next_obj);
obj_template_t *obj_last_child(obj_template_t *obj)

Get the last child object if the specified object has any children.

!!!! SKIPS OVER AUGMENT AND USES !!!!

Parameters

obj -- obj_template_t to check

Returns

pointer to last child obj_template_t or NULL if not found

obj_template_t *last_obj = obj_last_child(topobj);
obj_template_t *obj_get_parent(obj_template_t *obj)

Get the parent of the current object.

Parameters

obj -- object to check

Returns

pointer to the parent of this object or NULL if none

obj_template_t *topobj = obj_get_parent(next_obj);

The following example shows how the child object nodes can be traversed in forward order:

/* check all the child nodes forward */
obj_template_t *chobj = obj_first_child(obj);
for (; chobj; chobj = obj_next_child(chobj)) {

    const xmlChar *modname = obj_get_mod_name(chobj);
    const xmlChar *objname = obj_get_name(chobj);

    /* process objects here */

}

The following example shows how the child object nodes can be traversed in reverse order:

/* check all the child nodes reverse */
obj_template_t *chobj = obj_last_child(obj);
for (; chobj; chobj = obj_prev_child(chobj)) {

    const xmlChar *modname = obj_get_mod_name(chobj);
    const xmlChar *objname = obj_get_name(chobj);

    /* process objects here */

}

Process Ancestor Nodes

In order to loop through ancestor entries and process them as required, the following code may be used:

boolean obj_is_root(const obj_template_t *obj)

Check if object is marked as a root object.

Parameters

obj -- obj_template to check

Returns

TRUE if object is marked as ncx:root; FALSE if not

/* go through the ancestor entries and process them as needed */
obj_template_t *testobj = obj_get_parent(chobj);
while (testobj) {
    /* process parent objects here */
    testobj = obj_get_parent(testobj);
    if (testobj && obj_is_root(testobj)) {
        testobj = NULL;
    }
}

The 'obj_is_root' API function signals when to stop. It is also possible to have a NULL pointer returned. That indicates you reached the top root container. This container must not be modified, accessed or changed in any manner.

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_set_index_chain function MUST be called before val_add_child for a list

Sometimes server or SIL code creates data. In order to create a list, the following steps must be followed

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

  2. Create the key leafs and add them to the parent 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)

Add a child value node to a parent value node Force add even if Tree Insersion fails.

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_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 is skip_sil_partial needed.

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

union val_value_t::v_ v
union v_

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!!

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 the 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 smple 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.

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);

Leaf Examples

The following examples show how to construct a simple leaf data node.

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 -- address of return status

Return values

*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_vale 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

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 -- address of return status

Return values

*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 data node with integer types, the following API function can be used:

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

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 -- address of return status

Return values

*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_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

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 -- address of return status

Return values

*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

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 -- address of return status

Return values

*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

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 -- address of return status

Return values

*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 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)

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 -- address of return status

Return values

*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 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)

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 -- address of return status

Return values

*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_empty_leaf(parentobj,
                        modname,
                        leafname,
                        boolval,
                        &res);

Leaf-list Examples

The following examples show how to construct a simple leaf-list data node.

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.

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_value) {
    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.

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.

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

The container nodes can be constructed different ways depending whether it is a top level container or not. If the container is not a top level container, the following code can be used:

/* 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);

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;
    }
}

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_obj, 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_obj,
                                 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;
    }
}

Configuration Load and Reload

Configuration Initialization

During phase 2 initiation the server will load the NV-stored configuration, depending on the CLI and conf file parameters: Four different settings are checked in order:

  1. --no-config: An empty factory-default configuration will be loaded, except the NV-stored version will not be set back to factory default values.

  2. --config=foo.conf: The specified configuration file will be loaded. The $YUMAPRO_DATAPATH environment variable or --datapath parameter can be used to control the search path for this XML file.

  3. --factory-startup: If --startup-factory-file is specified then that file must be present, or an empty factory-default configuration will be loaded, and the NV-stored version will also be set back to factory default values.

  4. default: The default configuration file will be loaded. The $YUMAPRO_DATAPATH environment variable or --datapath parameter can be used to control the search path for this XML file (startup-cfg.xml). The default location is HOME/yumapro/startup-cfg.xml, unless the --fileloc-fhs parameter is used.

The --config and --no-config parameters cannot be used together because they are defined as a YANG choice.

Validation Phase

Once the initial configuration is parsed and converted to a tree of val_value_t structures, it is validated according to the YANG field validation rules in the loaded modules. The SIL edit callback function must not allocate any resources or alter system behavior during the validate phase.

The --startup-error CLI or conf file parameter controls how the server proceeds at this point:

  • --startup-error=stop: Any unknown definitions (namespace, element, attribute) will cause the server to terminate. Any invalid values for the expected data type for each node will cause the server to terminate. This is the default action.

  • --startup-error=continue: Any unknown definitions (namespace, element, attribute) will cause the server to prune those nodes, log warnings, and continue . Any invalid values for the expected data type for each node will cause the server to prune those nodes, log warnings and continue.

After the configuration is field-validated, the user SIL edit callbacks are called for the validation phase.

After all SIL edit callbacks have been invoked and no errors have been reported, the agt_val_root_check function is run to perform all the YANG datastore validation tests, according to the modules loaded in the server. The steps are enumerated, but actually implemented to be performed at the same time:

  • Remove all false when-stmts (delete_dead_nodes)

  • Validate that the correct number of instances are present

    • optional container or leaf: 0 or 1 instances

    • mandatory container or leaf: 1 instance

    • mandatory choice: 1 case present

    • list, leaf-list: min-elements, max-elements

  • Check YANG specific constraints:

    • list: all keys present

    • list unique-stmt: specified descendant nodes checked across all list entries to make sure no duplicate values in any entries

    • all nodes with must-stmt validation expressions are checked to make sure the Xpath expression result is a boolean with the value 'true'.

Apply Phase

After all validation tests have been run the server decides if it can continue by checking the --running-error CLI/conf file parameter:

  • --running-error=stop: If any errors are reported in the validation phase the server will exit with an error because the running configuration is not valid. This is the default behavior.

  • --running-error=continue: If any errors are reported in the validation phase the server will attempt to prune the nodes with errors. The server will continue booting even if the configuration is not valid according to the YANG datastore validation rules. The server will remember that the configuration is bad and only perform full validation checks until a valid configuration is saved.

If the server continues beyond this point, then the SIL edit callbacks are all called again for the apply phase. The SIL code can reserve resources at this point but not activate the configuration.

If any SIL callback generates an error during this phase the configuration load will be terminated and the server will shutdown.

Commit/Rollback Phase

If no SIL callback functions generate an error in the apply phase then the server will attempt to commit the configuration. All of the SIL edit callback functions will be called again to commit the configuration. If any SIL callback function generates an error then the server will switch into rollback mode.

The callback type will either be AGT_CB_COMMIT or AGT_CB_ROLLBACK.

The SIL code must activate or free any reserved resources at this point. It will only be called once for either commit or rollback, during the same edit.

If the callback type is AGT_CB_COMMIT then it must also activate the configuration.

If the server attempts to rollback the SIL configuration commits, then any nodes that have already accepted the commit will be called again to validate, apply, and commit a “delete” operation (OP_EDITOP_DELETE) on the data node that was created via the OP_EDIT_LOAD operation.

Configuration Replay

It is possible to replay the entire configuration if the underlying system resets or restarts, but the server process is still running.

A configuration replay is often triggered by a user timer callback function.

The system will trigger a config replay for a subsystem upon request, and when the subsystem registers or re-registers with the server.

Triggering a Configuration Replay

The configuration replay procedure must be triggered by some external mechanism, such as a timer callback function. This function can be registered in the yp-system library or in a SIL library. The purpose of this callback is to check the health of the underlying system and if it has restarted and needs to be re-initialized with configuration, then the 'agt_request_replay' function must be called.

void agt_request_replay(void)

Request replay of the running config to SIL modules because SIL hardware has reset somehow.

This is used by SIL code to force a complete replay attempt SIL-SA code will send a <trigger-replay> message to cause a subsystem replay.

Timer Callback Example to Check System

Example Timer Callback:

 /* timer callback function
  *
  * Process the timer expired event
  *
  * INPUTS:
  *    timer_id == timer identifier
  *    cookie == context pointer, such as a session control block,
  *            passed to agt_timer_set function (may be NULL)
  *
  * RETURNS:
  *     0 == normal exit
  *    -1 == error exit, delete timer upon return
  */
 static int
     force_replay (uint32  timer_id, void *cookie)
 {
     (void)timer_id;
     (void)cookie;

     boolean need_replay = FALSE;

     // check the system somehow

     if (need_replay) {
         agt_request_replay();
     }

     return 0;
 }  /* force_replay */

Example Timer Callback Registration:

status_t u_foo_init (
    const xmlChar *modname,
    const xmlChar *revision)
{
    status_t res = NO_ERR;
    ncx_module_t *foo_mod = NULL;

    foo_mod = ncx_find_module(
        y_foo_M_address,
        y_foo_R_address);
    if (foo_mod == NULL) {
        return ERR_NCX_OPERATION_FAILED;
    }

    /* put your module initialization code here */
    uint32 timerid = 0;

    /* example will check every 10 seconds */
    res = agt_timer_create(10, TRUE, force_replay, NULL, &timerid);

    return res;

} /* u_foo_init */

SIL Edit Callbacks

The same SIL callback procedure is used for the initial configuration load and a replay config load.

The YANG field and datastore validation is not done. Only the SIL callback functions are called to allow the SIL code to reconfigure the underlying system according to the replay values.

Some parameters are different, and the SIL edit callback functions may need to know the difference, since data structures may already be setup and the SIL code would leak memory if pointers to malloced data were re-initialized without cleaning up first.

If the callback is for a replay then the following macro from ncx/rpc_msg.h will return TRUE:

RPC_MSG_IS_REPLAY(msg):
  • Evaluates to true if the msg is for the <replay-config> operation

  • Evaluates to false if the msg is not for the <replay-config> operation

Refer to the Edit Callback Overview section for details on SIL and SIL-SA Edit Callback usage.

Configuration Replay Callbacks

The 'agt_replay_fn_t' callback defined in agt/agt.h is invoked when a configuration replay procedure is started, and then invoked again when it is finished.

The details for using this system callback are in the Configuration Replay section.

Terminal Diagnostic Messages

The server can support generation of notification messages that will automatically be displayed on yangcli-pro and yp-shell terminal sessions. A SIL or SIL-SA program will generate the <term-msg> notifications and the server will deliver them to all clients enabled to receive notifications. The client program will display the text message contents on the terminal.

  • The yumaworks-term-msg.yang module defines the <term-msg> notification.

  • The --with-term-msg CLI parameter is used by netconfd-pro and yangcli-pro/yp-shell to enable the <term-msg> notifications

  • The 'agt_make_term_msg' API function is used to make a notification structure using the provided text string.

Diagnostic Message Generation by SIL and SIL-SA Callbacks

Once this feature is enabled, the SIL or SIL-SA code must use a special API function to create a terminal message to send.

Once created, the notification is queued in the normal manner.

Do not use the "stream" specific APIs for terminal messages!
agt_not_msg_t *agt_make_term_msg(const xmlChar *msg, status_t *res)

Create a <term-msg> notification.

Parameters
  • msg -- string to send as the data field

  • res -- address of return status

Return values

*res -- return status

Returns

malloced agt_not_msg_t struct if OK; NULL if some error

SIL Example Usage

const xmlChar *msgbuff =
   (const xmlChar *)"This is a 1 line message";
status_t res = NO_ERR;
agt_not_msg_t *notif = agt_make_term_msg(msgbuff, &res);
if (notif == NULL) {
    log_error("\nError: cannot make term-msg (%s)",
               get_error_string(res));
    return;
}

agt_not_queue_notification(notif);

SIL-SA Example Usage:

const xmlChar *msgbuff =
   (const xmlChar *)"This is a 2 line message\nthat will be displayed";
status_t res = NO_ERR;
agt_not_msg_t *notif = agt_make_term_msg(msgbuff, &res);
if (notif == NULL) {
    log_error("\nError: cannot make term-msg (%s)",
              get_error_string(res));
    return;
}

sil_sa_queue_notification(notif);

YControl Subsystem

The YControl subsystem is used to allow asynchronous operation between netconfd-pro and higher layer services, such as SIL-SA. The YControl subsystem is a shared library that is linked with other libraries and runs in the vendor process.

The YControl subsystem is designed to support multiple independent upper-layer services . The main server does not provide an external API to the server handler for each subsystem at this time.

An IO poll API is called periodically by the vendor process to check for incoming messages.

The subsystem provides a communication service between the server and another process. It does not provide any high-level service within the server. It handles the following services:

  • connection and reconnection to the netconfd-pro server

  • configurable connect and retry parameters

  • the server can boot before or after the process using YControl.

  • registration API for higher layer services

  • message processing API for higher layer services

Subsystem Software Components

../_images/ycontrol_block_diagram.png

Server to Subsystem Service Layer

../_images/async_control_plane.png

YControl API Functions

Each high level service must register the proper callback functions with the YControl subsystem.

The application will poll the IO function periodically to handle communication with the main server. The callback functions registers with the subsystem will be invoked as needed, based on the received message.

The main YControl functions are described in this section.

The following sample applications show how to use YControl API functions.

  • combo-app

  • db-api-app

  • sil-sa-app

Initialization and Cleanup

The following functions are used for initialization and cleanup of the YControl subsystem:

  • ycontrol_init: Phase 1 Initialization

  • ycontrol_init2: Phase 2 Initialization

  • ycontrol_register_service: Register a set of callback functions fort a high-level service

  • ycontrol_cleanup: Shutdown YControl subsystem and all upper layer services

  • ycontrol_request_shutdown: Trigger shutdown of YControl subsystem and all upper layer services

  • ycontrol_shutdown_requested: Check if shutdown of YControl subsystem has been triggered

status_t ycontrol_init(int argc, char *argv[], const xmlChar *subsys_id)

Initialize the YControl library.

Setup global vars before accepting any requests. Process all the CLI parameters. There is no .conf file for a subsystem.

Parameters
  • argc -- argument count

  • argv -- argument array

  • subsys_id -- sub-system identifier

Returns

status: 0 OK

status_t ycontrol_init2(void)

Phase 2 initialization.

Setup connection to server.

Returns

status

status_t ycontrol_register_service(const xmlChar *service_name, ycontrol_service_start_t service_start, ycontrol_service_stop_t service_stop, ycontrol_service_msg_rcvr_t service_rcvr, ycontrol_service_shutdown_t service_shut)

Register a specific service with the YumaPro control message manager.

Officially only YumaPro services are supported but the YControl layer does not check. All callback functions must be provided . Only the receiver callback is technically required to do anything. It MUST send a subsys-response if a server-request is received.

Parameters
  • service_name -- unique name of the service

  • service_start -- service start callback

  • service_stop -- service stop callback

  • service_rcvr -- Message receiver callback

  • service_shut -- service shutdown callback

Returns

status

void ycontrol_cleanup(void)

Cleanup ycontrol layer.

Called by the application to cleanup resources. Must call if ycontrol_init function variant called.

void ycontrol_request_shutdown(void)

Request a control message handler shutdown.

This is the correct way to shutdown YControl from the application.

boolean ycontrol_shutdown_requested(void)

Check if a control message handler shutdown is in progress.

Returns

TRUE if shutdown mode has been started

Runtime Functions

The following functions are used after initialization is complete to access the YControl subsystem:

  • ycontrol_check_io: Check for any incoming messages from the main server

  • ycontrol_is_ready: Make sure the YControl subsystem is ready for a new request

  • ycontrol_send: Send any YControl message to the main server

  • ycontrol_send_error: Send an error response to the main server

status_t ycontrol_check_io(void)

Check for input/output.

Called from application to process any incoming YControl messages.

Returns

status

boolean ycontrol_is_ready(void)

Check if the ycontrol ready is up and ready to be used.

Returns

TRUE if ready; FALSE if not

status_t ycontrol_send(const xmlChar *service_id, uint32 *msgid, ycontrol_msgtype_t msgtype, val_value_t *service_payload, status_t msg_status)

Send a YControl message.

Parameters
  • service_id -- service sending this message

  • msgid -- address of message ID; for response this is non-zero

  • msgtype -- type of YControl message to send

  • service_payload -- val_value_t

    tree to add to message payload

    NULL if not used

  • msg_status -- NO_ERR or error status for message; ignored if service_payload is non-NULL

Return values

*msgid -- set to the message ID assigned to the msg, if msgid non-NULL

Returns

status

status_t ycontrol_send_error(const xmlChar *service_id, uint32 *msgid, status_t msg_status, uint32 error_index, const xmlChar *error_message, const xmlChar *txid_str)

Send a YControl <error> message.

Parameters
  • service_id -- service sending this message

  • msgid -- address of message ID; for response this is non-zero

  • msg_status -- NO_ERR or error status for message; ignored if service_payload is non-NULL

  • error_index -- error index if sending an <error> response

  • error_message -- error message if sending an <error> response

  • txid_str -- transaction-id string to add (may be NULL to skip)

Returns

status

YControl Message Structure

../_images/ycontrol_messages.png

There are 6 types of messages supported, indicated by the message-type leaf

  • server-request: request from server to subsystem, expecting a subsys-response

  • server-response: response from server to subsystem to a subsys-request message

  • server-event: event from server to subsystem; no response

  • subsys-request: request from subsystem to server, expecting a server-response

  • subsys-response: response from subsystem to server to a server-request message

  • subsys-event: event from subsystem to server; no response

Each message type shares the same payload structure. There are 3 formats, specified by the 'message-payload' choice-stmt in the YANG module.'

  • container payload: message body

  • leaf ok: NO_ERR response

  • container error: error response

The yumaworks-ycontrol.yang module contains the YControl message structure.

SIL-SA Subsystem

The SIL-SA subsystem provides sub-agent support for SIL callbacks. It uses messages between the server and subsystem to distribute SIL operations across multiple remote subsystems.

To use the SIL-SA subsystem, initialize and run the sil-sa library API functions. See the sil-sa-app source file “main.c” for an example of the API calls needed to enable the SIL-SA subsystem.

If SIL-SA libraries are available to the subsystem, and the YANG modules are available to the main server and the submodule, then the SIL-SA callback registration and message handling will be automatically handled by the subsystem sil_sa and ycontrol libraries.

There is no mechanism at this time to configure separate YANG modules on each subsystem. Each subsystem is required to ignore SIL-SA service requests for objects it does not support. It is possible for multiple subsystems and the main module to register for the same object. Each callback must decide if it should handle the specific instance being requested (E.g. interface “eth0” or “eth3”). Subsystems need to ignore requests for unsupported instances by returning an “ok” message to the main server for transaction requests.

In the registration phase, the main server will send the complete module and bundle parameter list to each subsystem. Each subsystem will load its SIL-SA library code as required for that subsystem.

It is up to the developer to make sure each subsystem registers with the main server with a unique subsys-id string. Only one subsystem can use the default (subsys1), The --subsys-id CLI parameter can be used to set the subsytem identifier.

Example SIL-SA Application

The following file can be found in the /usr/share/yumapro/src/sil-sa-app/src directory.

The sil-sa-app program is a complete application. Only the 'main.c' file is shown here, not the entire program.

/********************************************************************
* FUNCTION main
*
* This is an example SIL-SA service main function.
*
* RETURNS:
*   0 if NO_ERR
*   status code if error connecting or logging into ncxserver
*********************************************************************/
int main (int argc, char **argv)
{
#ifdef MEMORY_DEBUG
    mtrace();
#endif

    char *subsys = NULL;
    char *address = NULL;
    uint16 port = 0;
    uint16 retry_limit = 0;
    boolean if_notif=false;  // hidden parameter
    boolean ycontrol_done = FALSE;

    /* need to check for the subsys-id parm before
     * the system is initialized
     */
    status_t res = get_subsys_parm(argv, &subsys);
    if (res != NO_ERR) {
        print_usage();
    }

    /* 1) setup yumapro messaging service profile */
    if (res == NO_ERR) {
        if (subsys == NULL) {
            res = ycontrol_init(argc, argv,
                                (const xmlChar *)"subsys1");
        } else {
            res = ycontrol_init(argc, argv,
                                (const xmlChar *)subsys);
        }
        ycontrol_done = TRUE;
    }

    /* 2) register services with the control layer */
    if (res == NO_ERR) {
        res = sil_sa_register_service();
    }

#ifdef SIL_SA_APP_STATLIB_TEST
    /* 2B) setup any static SIL-SA libraries */
    if (res == NO_ERR) {
        res = static_silsa_init();
    }
#endif  // SIL_SA_APP_STATLIB_TEST


    /* get the CLI parameters after the system is initialized
     * so library parameter handled correctly
     */
    if (res == NO_ERR) {
        res =  get_cli_parms(argv, &address, &port, &if_notif, &retry_limit);
        if (res != NO_ERR) {
            print_usage();
        }
    }

    /* set the retry limit if provided */
    if ((res == NO_ERR) && (retry_limit > 0)) {
        ycontrol_set_retry_limit(retry_limit);
    }

    /* It is also possible to set the retry_interval but there is
     * no CLI parameter provided for this purposes
     * if (res == NO_ERR) {
     *     ycontrol_set_retry_interval(retry_int_milliseconds);
     * }
     */


    /* 3) do 2nd stage init of the control manager (connect to server) */
    if (res == NO_ERR) {
        if (address) {
            if (port == 0) {
                port = 2023;
            }
            res = ycontrol_init2_ha("server1", address, port);
        } else {
            res = ycontrol_init2();
        }
    }

    useconds_t usleep_val = 10000;  // 10K micro-sec == 1/100 sec
    boolean done = FALSE;

    /* 4) call ycontrol_check_io periodically from the main program
     * control loop
     */
#ifdef SIL_SA_APP_DEBUG
    int id = 0;
#endif  // SIL_SA_APP_DEBUG

#ifdef SIL_SA_APP_NOTIF_TEST
    uint32 loop_cnt = 0;
#endif // SIL_SA_APP_NOTIF_TEST

#ifdef SIL_SA_APP_TERM_MSG_TEST
    uint32 term_msg_loop_cnt = 0;
#endif // SIL_SA_APP_TERM_MSG_TEST

    while (!done && res == NO_ERR) {
#ifdef SIL_SA_APP_DEBUG
        if (LOGDEBUG3) {
            log_debug3("\nsil-sa-app: checking ycontrol IO %d", id++);
        }
#endif  // SIL_SA_APP_DEBUG

        res = ycontrol_check_io();

        if (ycontrol_shutdown_now()) {
            // YControl has received a <shutdown-event>
            // from the server subsystem is no longer active
            // could ignore or shut down YControl IO loop
            done = TRUE;
        }

        // Using sleep to represent other program work; remove for real
        if (!done && res == NO_ERR) {
            (void)usleep(usleep_val);
        }

#ifdef SIL_SA_APP_NOTIF_TEST
        loop_cnt++;
        if (loop_cnt % 50 == 0 && if_notif) {
            sil_sa_notif_test(10, 20, (const xmlChar *)"this is a test");
        }
#endif // SIL_SA_APP_NOTIF_TEST

#ifdef SIL_SA_APP_TERM_MSG_TEST
        term_msg_loop_cnt++;
        if (term_msg_loop_cnt % 100 == 0) {
            sil_sa_term_msg_test(term_msg_loop_cnt);
        }
#endif  // SIL_SA_APP_TERM_MSG_TEST

    }

    /* 5) cleanup the control layer before exit */
    if (ycontrol_done) {
        ycontrol_cleanup();
    }

#ifdef MEMORY_DEBUG
    muntrace();
#endif

    return (int)res;

}  /* main */

SIL-SA API Functions

Most SIL callbacks have corresponding SIL-SA callbacks, which are the same or almost the same as the SIL API functions.

Some-SA APIs are different from SIL code and some callbacks cannot provide the same set of pointers to use during the invocation> For example, Transaction Complete and Start Hook Callbacks cannot provide Transaction Control Block in SIL-SA version of the callback since the Transaction Control Block is not available in the SIL-SA subsystem. It is only part of the netconfd-pro server and only accessible from the SIL code.

However, to supply as much information to the SIL-SA callbacks and provide as much possible the same functionality as in the SIL callbacks there are multiple API that can be used to achieve SIL like functionality in the SIL-SA code:

Session information is not available in the session control block for SIL-SA callbacks. Special APIs have to be used instead.

Function

Description

sil_sa_get_username()

Get the user_id value from the message header

sil_sa_get_client_addr()

Get the client address (client_addr value from the message header)

sil_sa_get_rpc_msg_id()

Get the rpc message id (txid_str) from the message header

sil_sa_set_error_msg()

Set Custom Error Message string in case of error

These APIs are only available in the SIL-SA version of the SIL code and are intended to provide access to specific fields in the client message header.

Client Username

The following APIs provide a way to access the User Identifier and Client Address that are stored in the message header in regular SIL code.

const xmlChar *sil_sa_get_username(void)

Get the user_id value from the message header.

Time-sensitive API. Each message received by a user will cause the saved state to be updated.

Returns

user_id value or NULL

/* Get the user_id value from the message header */
const xmlChar *user = sil_sa_get_username();

Client Address

const xmlChar *sil_sa_get_client_addr(void)

Get the client address (client_addr value from the message header)

This is the external client address, not the SIL-SA peer address. Time-sensitive API. Each message received by a user will cause the saved state to be updated.

Returns

client_addr value or NULL

/* Get the client address (client_addr value from the message header) */
const xmlChar *client_addr = sil_sa_get_client_addr();

Message ID

The following API provides a way to access the Message ID (txid_str). This API is only available to EDIT callbacks.

It will be NULL for all the other callbacks.

const xmlChar *sil_sa_get_rpc_msg_id(rpc_msg_t *msg)

Get the rpc transaction id (txid_str) from the rpc_sil_sa_cb.

This function is only available to EDIT callbacks! It is NULL for GET2 callbacks.

Parameters

msg -- rpc msg to retrive rpc msg id from

Returns

txid_str value or NULL

/* Get the rpc message id (txid_str) from the rpc_sil_sa_cb */
const xmlChar *msg_id =  sil_sa_get_rpc_msg_id(msg);

Set Error Message

The <error-message> field in the <rpc-error> response can be set from a SIL-SA callback.

The following YANG module will be used for this example:

module error-example {
  namespace "http://netconfcentral.org/ns/error-example";
  prefix "err";

  revision 2021-11-19 {
    description
      "Example module";
  }

  container test-silsa-errors {                 // EDIT2 CB
    presence "Presence";
    leaf test-error {
      type string;
    }
  }
}

Assume the SIL-SA code reports an error in case the 'test-error' leaf creation and when the value of a new leaf is 'force-error'.

In this case there is an API 'sil_sa_set_error_msg' to set up a custom error to be returned to the server. Refer to the SIL-SA Creation of Custom Error Messages And AppInfo section for details on this API function.


status_t
    u_err_test_silsa_errors_edit (
    ses_cb_t *scb,
    rpc_msg_t *msg,
    agt_cbtyp_t cbtyp,
    op_editop_t editop,
    val_value_t *newval,
    val_value_t *curval)
{
    val_value_t *child_val = NULL;
    if (newval) {
        child_val =
            val_find_child(newval,
                           (const xmlChar *)"error-example",
                           (const xmlChar *)"test-error");
    }

    /* Get the rpc message id (txid_str) from the rpc_sil_sa_cb */
    const xmlChar *msg_id = sil_sa_get_rpc_msg_id(msg);

    /* Get the user_id value from the message header */
    const xmlChar *user = sil_sa_get_username();

    /* Get the client address (client_addr value from the message header) */
    const xmlChar *client_addr = sil_sa_get_client_addr();

    if (LOGDEBUG3) {
        log_debug3("\nEnter u_err_test_silsa_errors_edit");
        log_debug3_append("\nmsg_id:%s", msg_id);
        log_debug3_append("\nuser:%s", user);
        log_debug3_append("\nclient_addr:%s", client_addr);
    }

    switch (cbtyp) {
    case AGT_CB_VALIDATE:
        /* description-stmt validation here */
        break;
    case AGT_CB_APPLY:
        /* database manipulation done here */
        break;
    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:

            if (child_val &&
                !xml_strcmp(VAL_STR(child_val), (const xmlChar *)"force-error")) {

                /* TRIGGER ERROR */
                res = ERR_NCX_OPERATION_NOT_SUPPORTED;

                sil_sa_set_error_msg(msg,
                                     (const xmlChar *)"SOME CUSTOM ERROR MSG");
            }

            break;
        case OP_EDITOP_DELETE:
            break;
        default:
            /* USE SET_ERROR FOR PROGRAMMING BUGS ONLY */
            res = SET_ERROR(ERR_INTERNAL_VAL);
        }
        break;
    case AGT_CB_ROLLBACK:
        /* undo device instrumentation here */
        break;
    default:
        /* USE SET_ERROR FOR PROGRAMMING BUGS ONLY */
        res = SET_ERROR(ERR_INTERNAL_VAL);
    }
    return res;

} /* u_err_test_silsa_errors_edit */

As a result of this operation, the server will produce an error with a custom error message:

{
 "ietf-restconf:errors": {
  "error": [
   {
    "error-type":"application",
    "error-tag":"operation-not-supported",
    "error-app-tag":"no-support",
    "error-path":"/err:test-silsa-errors",
    "error-message":"SOME CUSTOM ERROR MSG",
    "error-info": {
     "error-number":273
    }
   }
  ]
 }
}

SIL-SA Message Format

The SIL-SA service uses several messages to interact with the netconfd-pro server.

These messages are defined in the yumaworks-sil-sa.yang module. The SIL-SA payload is defined as a YANG container that augments the YControl “message-payload” container.

SIL-SA Registration Message Flow

The SIL-SA service needs to initialize with the netconfd-pro server.

  1. SIL-SA to Server: config-request The SIL-SA service registers itself with the server and requests its configuration in 1 message.

  2. Server to SIL-SA: config-response The server responds to the config request with a list of modules and SIL-SA bundles that need to be loaded

  3. SIL-SA to Server: register-request The SIL-SA service registers the SIL-SA callbacks supported by the subsystem.

  4. Server to SIL-SA: ok The server responds to the config request with an <ok> or an <error> message

Server Edit Transaction Message Flow

The netconfd-pro server will initiate edit datastore transactions when the subsystem starts or restarts (load-config), or when clients edit the datastore via a network management protocol.

  1. Server to SIL-SA: start-transaction The server starts the transaction with the “validate” SIL-SA callback phase.

  2. SIL-SA to Server: ok The SIL-SA service responds to the start-transaction request with an <ok> or an <error> message

  3. Server to SIL-SA: continue-transaction The server continues the transaction with the “apply” SIL-SA callback phase.

  4. SIL-SA to Server: ok The SIL-SA service responds to the continue-transaction request with an <ok> or an <error> message

  5. Server to SIL-SA: continue-transaction The server continues the transaction with the “commit” or “rollback” SIL-SA callback phase.

  6. SIL-SA to Server: ok The SIL-SA service responds to the continue-transaction request with an <ok> or an <error> message. The SIL-SA service removes any saved state for this transaction.

Exception handling:

validate:

The server may send a “start-transaction” message with the “validate” flag set. The SIL-SA service will know that the transaction is only 1 message and not to expect a “continue-transaction” message from the server.

reverse-edit:

The server may send a “start-transaction” message with the “reverse-edit” flag set. The SIL-SA service will handle all 3 callback phases for the edit at once. Only 1 response message is sent to the server. This mode is used when a different subsystem responded with an error after the subsystem performing the reverse edit has already successfully completed the transaction. A new transaction is sent to the subsystem in this case to undo the edit.

cancel-transaction:

The server may send a “cancel-transaction” message after a transaction has started. This can occur if another SIL-SA subsystem or main server SIL callback returned an error. Any error causes the transaction to be canceled. The SIL-SA service will not expect a “continue-transaction” message from the server.

SIL-SA Register Request Message

The <register-request> is a subsytem request message. The purpose of the message is to register the SIL-SA callback functions for this sub-system.

  • Expected Response Message: ok or error

The following diagram illustrates the <register-request> event message:

../_images/silsa_register_request.png

SIL-SA Start Transaction Message

The <start-transaction> message is a server request message. The purpose of the message is to start an edit transaction which may require the SIL-SA callback functions on the subsystem to be invoked.

This message requests that a new edit transaction be started on the subsystem. Only 1 transaction can be in progress at a time.

If this transaction is for a validate operation then there will not be any followup messages. Otherwise, the subsystem must retain the information in this message until a cancel-transaction message has been received with the same transaction-id value, or a continue-transaction message has been received with the same transaction-id value for the 'rollback' or 'commit' phase.

  • Expected Response Message: transaction-response or error

The following diagram illustrates the <start-transaction> message:

../_images/silsa_start_transaction.png

SIL-SA Continue Transaction Message

The <continue-transaction> is a server request message. The purpose of the message is to invoke a callback phase for an edit transaction in progress.

  • Expected Response Message: transaction-response or error

The following diagram illustrates the <continue-transaction> message:

SIL-SA Transaction Response Message

The <transaction-response> is a subsystem response message. The purpose of the message is:

  • Set Hook: return added edits data or status

  • Post Set Hook: return added edits data or status

  • Transaction Hook: Expected Response Message: ok or error

  • If no Hook invoked: Expected Response Message: ok or error

The following diagram illustrates the <transaction-response> event message:

../_images/silsa_transaction_response.png

SIL-SA Cancel Transaction Event

The <cancel-transaction> is a server event message. The purpose of the message is to cancel an edit transaction in progress.

  • Expected Response Message: none

The following diagram illustrates the <cancel-transaction> message:

../_images/silsa_cancel_transaction.png

SIL-SA Load Event

The <load-event> is a server event message. The purpose of the message is to notify the SIL-SA subsytem that a module or bundle has been loaded or unloaded at run-time. Subsystem will load SIL-SA code and trigger a register event for any SIL calls registered.

  • Expected Response Message: none

The following diagram illustrates the <load-event> message:

../_images/silsa_load_event.png

SIL-SA Bundle Load Event

The <bundle-load-event> is a subsytem event message. The purpose of the message is to notify the server that a SIL-SA bundle has been loaded with a load-event sent from the server. This has caused some modules to be loaded on the subsystem, that need to be reported back to the main server so the datastore validation, agt_state, and other system book-keeping can be done.

  • Expected Response Message: none

The following diagram illustrates the <bundle-load-event> message:

../_images/silsa_bundle_load_event.png

SIL-SA Get Request Message

The <get-request> is a server request message. The purpose of the message is to send a composite retrieval request to support NETCONF and RESTCONF get operations.

  • Expected Response Message: get-response or error

The following diagram illustrates the <get-request> message:

../_images/silsa_get_request.png

SIL-SA Get Response Message

The <get-response> is a subsystem response message. The purpose of the message is to send a subsystem generated composite retrieval response to support NETCONF and RESTCONF get operations.

  • Expected Response Message: none

The following diagram illustrates the <get-response> message:

../_images/silsa_get_response.png

SIL-SA Notification Message

The <notification> is a subsystem event message. The purpose of the message is to send a subsystem generated YANG notification event for NETCONF and RESTCONF streams.

  • Expected Response Message: none

The following diagram illustrates the <notification> message:

../_images/silsa_notification_message.png

SIL-SA RPC Request Message

The <rpc-request> is a server request message. The purpose of the message is to start a RPC transaction which may require the require the SIL-SA callback functions on the subsystem to be invoked.

  • This message requests that a new remote procedure call be validated and invoked on the subsystem.

  • If there are input parameters the subsystem must validate them.

  • If not valid or if the operation cannot be performed, the subsystem must return an error.

  • Expected Response Message: rpc-response

The following diagram illustrates the <rpc-request> event message:

../_images/silsa_rpc_request.png

SIL-SA RPC Response Message

The <rpc-response> is a subsystem response message. The purpose of the message is to return RPC data or status.

  • Expected Response Message: none

The following diagram illustrates the <rpc-response> message:

../_images/silsa_rpc_response.png

SIL-SA Action Request Message

The <action-request> is a server request message. The purpose of the message is to start an ACTION transaction which may require the SIL-SA callback functions on the subsystem to be invoked.

  • This message requests that a new action call be validated and invoked on the subsystem.

  • If there are input parameters the subsystem must validate them.

  • If not valid or if the operation cannot be performed, the subsystem must return an error.

  • Expected Response Message: action-response

The following diagram illustrates the <action-request> message:

../_images/silsa_action_request.png

SIL-SA Action Response Message

The <action-response> is a subsystem response message. The purpose of the message is to return Action data or status.

  • Expected Response Message: none

The following diagram illustrates the <action-response> message:

../_images/silsa_action_response.png

SIL-SA Transaction Start Message

The <trans-start-hook> is a server request message. The purpose of the message is to send a request which may require the SIL-SA callback functions on the subsystem to be invoked.

  • This message requests that Transaction Start Hook callbacks should be invoked on the subsystem.

  • Expected Response Message: a regular error message or OK.

The following diagram illustrates the <trans-start-hook> event message:

../_images/silsa_trans_start.png

SIL-SA Transaction Complete Event

The <trans-complete-hook> is a server event message. The purpose of the message is to send an event which may require the SIL-SA callback functions on the subsystem to be invoked.

  • Expected Response Message: none

The following diagram illustrates the <trans-complete-hook> message:

../_images/silsa_trans_complete.png

SIL-SA Hook Get Request Message

The <hook-get-request> is a subsystem request message. The purpose of the message is to start a transaction which may require the server to run 'agt_val_get_data' API and return the result.

  • The server will get the data node value based on XPath of object instance.

  • This function will return a value only if there is an existing node in the datastore or there is a default for the node.

  • Expected Response Message: hook-get-response

The following diagram illustrates the <hook-get-request> message:

../_images/silsa_hook_get.png

SIL-SA Hook Get Response Message

The <hook-get-response> is a server reply message. The purpose of the message is to send the element containing the requested val_value_t based on XPath of the object instance.

  • Expected Response Message: None

The following diagram illustrates the <hook-get-response> message:

../_images/silsa_hook_get_response.png

SIL-SA Stream Callback Event

The <stream-callback-event> is a server event message. The purpose of the message is to invoke an event-stream callback on a SIL-SA platform.

  • One event will be generated for each remote subsystem entry found that needs to be invoked. E.g., if 3 modules register but all are mapped to the same stream, then 3 events would be sent to the subsystem.

  • Expected Response Message: None

The following diagram illustrates the <stream-callback-event> message:

../_images/silsa_stream_callback.png

SIL-SA Commit Completeness Hook Message

The <commit-completeness-hook> is a server request message. The purpose of the message is to start a transaction which may require the SIL-SA callback functions on the subsystem to be invoked.

  • This message requests that a Commit Completeness callback should be invoked on the subsystem.

  • Expected Response Message: a regular error message or OK.

The following diagram illustrates the <commit-completeness-hook> message:

../_images/silsa_commit_complete.png

DB-API Subsystem

The DB-API subsystem provides a special datastore editing interface for subsystems to access the YANG-based data controlled by the main server. It can be used by network management components within the device that operate in addition to the netconfd-pro server.

The DB-API service provides the ability for a subsystem to send a configuration edit to the main server. The YControl protocol is used to manage the connection and communication with the main server.

DB-API runs on an external process on the same system as netconfd-pro to send database requests to the server.

The yumaworks-db-api.yang YANG module contains message definitions for the DB-API subsystem.

The db-api-app example application is provided to show usage of some of the database and system access APIs available in the DB-API library. It is usually installed in /usr/share/yumapro/src/db-api-app/main.c.

This application contains example operations only.
It is not intended to be a functional application like sil-sa-app.

DB-API Interface Functions

The DB-API service uses the YControl subsystem similar to the SIL-SA service. The same YControl callback interface is used for handling database access messages.

Refer to db-api/db_api.h for complete details.

Setup

  • db_register_service: Initialize the DB-API service with the YControl subsystem

  • db_api_service_ready: Check if the DB-API service is ready to send an edit

Single Edit Patch

  • db_api_send_edit: Send an edit request to the main server. The SIL and SIL-SA callbacks will not be invoked for this edit.

    • Only the main server datastore will be updated to contain the requested edit.

    • The main server will return an “ok” message if the edit was accepted or an “error” message if the edit was rejected for some reason.

    • The resulting running datastore must pass any YANG validation checks that apply to the changed data somehow.

  • db_api_send_edit_ex: Same as db_api_send_edit, but with more parameters

  • db_api_send_edit_full: Same as db_api_send_edit_ex, but with more parameters

  • db_api_send_edit_full2: Same as db_api_send_full, but with a skip-sil parameter added

Multi-Edit Patch

  • db_api_start_patch: start a multi-edit patch request

  • db_api_add_edit: add an edit to the YANG Patch (called 1 – N times)

  • db_api_send_patch: send the YANG Patch request to the server'

  • db_api_free_patch: free the memory used by a YANG Patch request

Get Configuration Data

  • db_api_send_getconfig: send a config request to the server. The running configuration will be retrieved and saved in the specified file.

Get Filtered Configuration or Operational Data

  • db_api_send_getfilter: send a retrieval request to the server. The running configuration and optionally the operational state will be retrieved and saved in the specified file. An XPath filter can be used to return a subset of the data.

Check Status

  • db_api_check_edit: check if an edit request or <getconfig> request has finished

  • db_api_check_edit_ex: Similar to db_api_check_edit but returns error string from server (if any)

Maintenance Mode

  • db_api_send_enter_maintmode: send a <enter-maintmode> request to the server

  • db_api_send_exit_maintmode: send a <exit-maintmode> request to the server

DB-LOCK-TEST Mode

  • db_api_request_local_db_lock: send a <db-lock> request to the server

  • db_api_release_local_db_lock: send a <db-unlock> request to the server

Set Log Level

  • db_api_send_set_loglevel: send a <set-log-level> request to the server

SubRPC

  • db_api_send_subrpc_request: send a <subrpc-request> message to the server

DB-API Application Structure

A DB-API application is designed to be integrated into another application or used as the main program. It consists of 3 parts:

  • Initialization

  • Main Loop

  • Cleanup

The initialization step is required to load the proper YANG modules and other data structures. The subsystem needs to connect to the main netconfd-pro server and register.

The 'YControl' transport library handles all low level interactions with the main server. After the YControl transport layer has been initialized, the DB-API service must register with the main server.

The first phase initialization is post-CLI and pre-database load. The second phase initialization is post-database load.

After initialization the main loop is used to check for network IO from the main server. First the main loop must wait until the DB-API service is ready.

Refer to the Initialization and Cleanup section for details on the following functions:

  • ycontrol_init

  • ycontrol_init2

  • ycontrol_check_io

  • ycontrol_cleanup

status_t db_api_register_service(void)

Register the DB-API service with the YControl layer.

Must be called before any other DB-API function can be used.

Returns

status

boolean db_api_service_ready(void)

Check if the DB-API service is done initializing, and is now ready to send requests to the server.

Returns

TRUE if ready state; FALSE otherwise

boolean ycontrol_shutdown_now(void)

Check if the YControl subsystem has shut down because a <shutdown-event> was received from the server.

Returns

TRUE is YControl has shut down

FALSE if YControl has not shut down

Example main db-api-app Function

Note

The 'db-api-app' example program does not do meaningful edits or other database operations. It is only provided as a template to create real database operations as needed by a subsystem application.

int main (int argc, char **argv)
{
    /* 1) setup yumapro messaging service profile */
    status_t res =
        ycontrol_init(argc, argv, (const xmlChar *)"subsys1");

    /* 2) register services with the control layer */
    if (res == NO_ERR) {
        res = db_api_register_service();
    }

    /* 3) do 2nd stage init of the control manager (connect to server) */
    if (res == NO_ERR) {
        res = ycontrol_init2();
    }

    useconds_t usleep_val = 100000;  // 100K micro-sec == 1/10 sec
    boolean done = FALSE;

    /* 4) call ycontrol_check_io periodically from the main program
     * control loop
     */
#ifdef DB_API_APP_DEBUG
    int id = 0;
#endif  // DB_API_APP_DEBUG

    boolean test_done = FALSE;

    while (!done && res == NO_ERR) {
#ifdef DB_API_APP_DEBUG
        if (LOGDEBUG3) {
            log_debug3("\ndb-api-app: checking ycontrol IO %d", id++);
        }
#endif  // DB_API_APP_DEBUG

        res = ycontrol_check_io();
        if (res != NO_ERR) {
            continue;
        }

        if (ycontrol_shutdown_now()) {
            // YControl has received a <shutdown-event>
            // from the server subsystem is no longer active
            // could ignore or shut down YControl IO loop
            done = TRUE;
        }

        // Using sleep to represent other program work; remove for real
        if (!done && res == NO_ERR) {
            (void)usleep(usleep_val);
        }

        if (db_api_service_ready() && !test_done) {
            send_test_edit();
            test_done = TRUE;
        } else if (db_api_service_ready() && test_done) {
            const xmlChar *error_msg = NULL;
            /* check the test edit */
            res = db_api_check_edit_ex(&error_msg);
            if (res == NO_ERR) {
                done = TRUE;
            }
        }  // else wait for service ready
    }

    /* 5) cleanup the control layer before exit */
    ycontrol_cleanup();

    return (int)res;

}  /* main */

Database Edit APIs

The DB-API service can be used to send datastore edits from the system to the netconfd-pro server. This is usually done because configuration can come from CLI, SNMP, or other internal system components.

There are several editing APIs. APIs cannot be changed after they are published, so many incremental versions of the "send_edit" APIs exist

The file db-api.h must be included in the application to access these API functions.

Function

Description

db_api_send_edit

Edit 1 target node with a basic operation and XML value

db_api_send_edit_ex

Add patch-id and system edit flag parameters

db_api_send_edit_full

Add insert parameters for user-ordered lists

db_api_check_edit

Check if a send-edit transaction has completed

db_api_check_edit_ex

Check if a send-edit has completed and get error message

Simple Database Edits

The "send_edit" functions are used to make a single patch to the configuration.

status_t db_api_send_edit_full(const xmlChar *edit_target, const xmlChar *edit_operation, const xmlChar *edit_xml_value, const xmlChar *patch_id_str, boolean system_edit, const xmlChar *insert_point, const xmlChar *insert_where)

Create a YANG Patch edit request and send it to the DB-API service on the main server.

Adds insert operation support.

The content should represent the intended target resource as specified in YANG-API (NOT RESTCONF) Only the data resource identifier is provided, not the API wrapper identifiers (so this can change when RESTCONF is supported)

* Example leaf:
*
*   edit_target /interfaces/interface/eth0/mtu
*   edit_value "<mtu>9000</mtu>"
*   edit_operation "merge"
*   patch_id_str "my-patch-x01"
*   system_edit true
*
@verbatim
* Example list:
*
*   edit_operation <operation string>
*        -  "create"
*        -  "delete"
*        -  "insert"
*        -  "merge"
*        -  "move"
*        -  "replace"
*        -  "remove"
*
*@verbatim
*
*   edit_target /interfaces/interface/eth0/ipv4
*   edit_value "<ipv4>
*                    <enabled>true</enabled><
*                    <forwarding>true</forwarding>
*                    <address>204.102.10.4</address>
*                    <prefix-length>24</prefix-length>
*                  </ipv4>"

Uses db_api_send_edit_full2 with the following defaults:

  • skip_sil is TRUE

See also

db_api_send_edit

Parameters
  • edit_target -- target resource (YANG-API path expression)

  • edit_operation -- edit operation (create merge replace delete remove)

  • edit_xml_value --

    XML payload in string form, whitespace allowed

    MAY BE NULL if no value required (delete remove))

  • patch_id_str --

    string to use as the patch ID

    NULL to use the default patch-id field

  • system_edit --

    TRUE if this edit is from the system and should bypass access control enforcement

    FALSE if this edit is from a user and should not bypass access control enforcement

  • insert_point --

    is a string like the target except a different instance of the same list of leaf-list; only for before, after.

    NULL to ignore this parameter

  • insert_where -- <insert enum="" string>="">

    • "before"

    • "after"

    • "first"

    • "last"

      NULL to ignore this parameter

Returns

status

The DB-API application should wait until the server completes the edit operation before continuing on with more DB-API operations. The main application can keep working. It does not have to block while waiting for a DB-API to complete.

status_t db_api_check_edit_ex(const xmlChar **errstr)

Check on the status of an edit in progress.

Get the error string if any

Parameters

errstr -- address of return error string (may be NULL)

Return values

*errstr -- pointer to error string if return value is not NO_ERR and there is a response from the server that had an error string

Returns

status

  • ERR_NCX_NOT_FOUND if final status and no message response is pending

  • ERR_NCX_SKIPPED if final status is not known yet

  • NO_ERR if there is a last-completed operation that completed with an OK response

  • <errcode> if there is a last-completed operation that completed with an ERROR response

Example db-api-app send_edit from db-api-app/main.c Most of the example edits require the YANG module test.yang to be loaded in the server.

/********************************************************************
* FUNCTION send_test_edit
*
* This is an example send edit function.
* The module test.yang needs to be loaded for this to work
*********************************************************************/
static void
    send_test_edit (void)
{
    /* EXAMPLE EDIT:
     * TBD: replace with parameters from command line
     */

    /* set 3 top-level leafs at once with test module */
    const xmlChar *path_str = (const xmlChar *)"/";
    const xmlChar *operation_str = (const xmlChar *)"merge";
    const xmlChar *value_str = (const xmlChar *)
        "<config>"
        "<int8.1 xmlns='http://netconfcentral.org/ns/test'>22</int8.1>"
        "<int16.1 xmlns='http://netconfcentral.org/ns/test'>11</int16.1>"
        "<int32.1 xmlns='http://netconfcentral.org/ns/test'>1000</int32.1>"
        "</config>";

    const xmlChar *patch_id_str = NULL;
    boolean system_edit = FALSE;
    const xmlChar *insert_point = NULL;
    const xmlChar *insert_where = NULL;

    status_t res =
        db_api_send_edit_full(path_str,
                              operation_str,
                              value_str,
                              patch_id_str,
                              system_edit,
                              insert_point,
                              insert_where);
    if (res != NO_ERR) {
        log_error("\nSend test edit failed %s %s = %s (%s)\n",
                  operation_str, path_str, value_str,
                  get_error_string(res));
    } else if (LOGDEBUG) {
        log_debug("\nSend test edit OK  %s %s = %s\n",
                  operation_str, path_str, value_str);
    }

}  /* send_test_edit */

Database Edits Using YANG Patch

In order to edit multiple data nodes at once, the 'YANG Patch' edit method defined in RFC 8072 is used.

There are 4 steps needed to use this database editing method:

  • start patch

  • add edit to patch (repeat as needed)

  • send patch edit request to the server

  • cleanup the patch

status_t db_api_start_patch(const xmlChar *patch_id_str, boolean system_edit, yang_patch_cb_t **retcb)

Start a YANG Patch request that can have multiple edits.

* Steps to use:
*  1) db_api_start_patch
*  2 .. N-2) db_api_add_edit
*  N-1) db_api_send_patch
*  N) db_api_free_patch

See also

db_api_add_edit

Parameters
  • patch_id_str --

    string to use as the patch ID

    NULL to use the default patch-id field

  • system_edit -- TRUE if this edit is from the system and should

  • retcb -- address of return control block

Return values

*retcb --

the patch control block in progress

Must free with ab_api_free_patch!!

Returns

status

status_t db_api_start_patch2(const xmlChar *patch_id_str, boolean system_edit, boolean skip_sil, const xmlChar *comment, yang_patch_cb_t **retcb)

Start a YANG Patch request that can have multiple edits with complete parameters (YPW-1826)

* Steps to use:
*  1) db_api_start_patch2
*  2 .. N-2) db_api_add_edit
*  N-1) db_api_send_patch
*  N) db_api_free_patch

See also

db_api_add_edit

Parameters
  • patch_id_str --

    string to use as the patch ID

    NULL to use the default patch-id field

  • system_edit -- TRUE if this edit is from the system and should

  • skip_sil --

    TRUE to skip SIL callbacks (default)

    FALSE to invoke SIL callbacks for this patch

  • comment --

    string to override default 'DB-API patch'

    NULL to use the default comment

  • retcb -- address of return control block

Return values

*retcb --

the patch control block in progress

Must free with ab_api_free_patch!!

Returns

status

status_t db_api_add_edit(yang_patch_cb_t *pcb, const xmlChar *edit_id_str, const xmlChar *edit_target, const xmlChar *edit_operation, const xmlChar *edit_xml_value, const xmlChar *insert_point, const xmlChar *insert_where)

Create an edit request and add to YANG Patch in progress.

If return NO_ERR then a new edit is added to pcb

Parameters
  • pcb -- patch control block in progress

  • edit_id_str -- index value for the edit

  • edit_target -- edit target path string

  • edit_operation -- edit operation (create merge replace delete remove)

  • edit_xml_value --

    XML payload in string form, whitespace allowed

    MAY BE NULL if no value required (delete remove))

  • insert_point -- is a string like the target except a different instance of the same list of leaf-list; only for before, after

  • insert_where -- <insert enum="" string>="">

    • "before"

    • "after"

    • "first"

    • "last"

Returns

status

status_t db_api_send_patch(yang_patch_cb_t *pcb)

Send a previously created patch request.

This does not free the pcb! Must call db_api_free_patch after this call

Parameters

pcb -- patch control block in progress

Returns

status

void db_api_free_patch(yang_patch_cb_t *pcb)

Free a patch control block.

Parameters

pcb -- patch control block in progress

This example edit function is from db-api-app/main.c


/********************************************************************
* FUNCTION send_complex_test_edit
*
* This is an example send edit function.
* It uses multiple APIs to build a patch with 3 edits
* The module test.yang needs to be loaded for this to work
*********************************************************************/
static void
    send_complex_test_edit (void)
{
    const xmlChar *patch_id_str = (const xmlChar *)"complex-P1";
    boolean system_edit = TRUE;
    yang_patch_cb_t *pcb = NULL;

/* change to 1 to use db_api_start_patch version */
#if 0
    /* Step 1: create a patch control block
     * Use db_api_start_patch or db_api_start_patch2
     * to set 2 additional fields
     */
    status_t res =
        db_api_start_patch(patch_id_str,
                           system_edit,
                           &pcb);
#else
    /* Step 1 using db_api_start_patch2 to show additional fields
     * The default for skip_sil is TRUE so use this API if the
     * server SIL or SIL-SA callbacks should be invoked for this edit
     * The comment field default is 'DB-API patch'. Set to NULL
     * to use the default or set plain string as comment
     */
    status_t res =
        db_api_start_patch2(patch_id_str,
                            system_edit,
                            FALSE,    // skip_sil
                            (const xmlChar *)"my first patch", // comment
                            &pcb);


#endif  // 0 : pick start patch API

    if (pcb == NULL) {
        log_error("\nCreate patch failed (%s)\n", get_error_string(res));
        return;
    }

    const xmlChar *edit_id_str = (const xmlChar *)"edit1";
    const xmlChar *edit_target_str = (const xmlChar *)"/int8.1";
    const xmlChar *edit_operation_str = (const xmlChar *)"replace";
    const xmlChar *edit_xml_value = (const xmlChar *)
        "<int8.1 xmlns='http://netconfcentral.org/ns/test'>44</int8.1>";
    const xmlChar *insert_point = NULL;
    const xmlChar *insert_where = NULL;

    /* Step 2: add edit1 */
    res = db_api_add_edit(pcb,
                          edit_id_str,
                          edit_target_str,
                          edit_operation_str,
                          edit_xml_value,
                          insert_point,
                          insert_where);
    if (res != NO_ERR) {
        log_error("\nAdd edit1 failed for complex test edit (%s)\n",
                  get_error_string(res));
        db_api_free_patch(pcb);
        return;
    }

    /* Step 3: add edit2 */
    edit_id_str = (const xmlChar *)"edit2";
    edit_target_str = (const xmlChar *)"/int16.1";
    edit_operation_str = (const xmlChar *)"remove";
    edit_xml_value = NULL;

    res = db_api_add_edit(pcb,
                          edit_id_str,
                          edit_target_str,
                          edit_operation_str,
                          edit_xml_value,
                          insert_point,
                          insert_where);
    if (res != NO_ERR) {
        log_error("\nAdd edit2 failed for complex test edit (%s)\n",
                  get_error_string(res));
        db_api_free_patch(pcb);
        return;
    }

    /* Step 4: add edit3 */
    edit_id_str = (const xmlChar *)"edit3";
    edit_target_str = (const xmlChar *)"/uint32.1";
    edit_operation_str = (const xmlChar *)"merge";
    edit_xml_value = (const xmlChar *)
        "<uint32.1 xmlns='http://netconfcentral.org/ns/test'>400</uint32.1>";

    res = db_api_add_edit(pcb,
                          edit_id_str,
                          edit_target_str,
                          edit_operation_str,
                          edit_xml_value,
                          insert_point,
                          insert_where);
    if (res != NO_ERR) {
        log_error("\nAdd edit3 failed for complex test edit (%s)\n",
                  get_error_string(res));
        db_api_free_patch(pcb);
        return;
    }

    /* Step 4: send patch request */
    res = db_api_send_patch(pcb);
    if (res != NO_ERR) {
        log_error("\nSend complex test edit failed (%s)\n",
                  get_error_string(res));
    } else if (LOGDEBUG) {
        log_debug("\nSend complex test edit OK\n");
    }

    /* step 5: free the patch control block */
    db_api_free_patch(pcb);

}  /* send_complex_test_edit */

Database Retrieval APIs

There are 2 API functions to retrieve YANG data from the server:

db_api_send_getconfig: Used to retrieve the entire <running> datastore

status_t db_api_send_getconfig(const xmlChar *filespec, boolean withdef)

Create a <getconfig> request and send it to the main server.

Parameters
  • filespec -- file specification to contain the XML instance document retrieved from the server

  • withdef -- TRUE to get with defaults; FALSE to leave out defaults

Returns

status

db_api_send_getfilter: Used to retrieve configuration data from the <running> datastore or operational data from the system. An XPath filter can be used to select data instead of retrieving all data nodes.

status_t db_api_send_getfilter(const xmlChar *filespec, boolean withdef, boolean get_config, const xmlChar *xpath_filter)

Retrieve data from the server with complete parameters.

Create a <getconfig> request and send it to the main server. An XPath filter can be sent as a parameter

Parameters
  • filespec -- file specification to contain the XML instance document retrieved from the server

  • withdef -- TRUE to get with defaults; FALSE to leave out defaults

  • get_config -- TRUE for config only; FALSE for get (config + state)

  • xpath_filter -- XPath expression (may be nULL)

Returns

status

/********************************************************************
* FUNCTION send_test_getconfig
*
* Send a <getconfig> request
*********************************************************************/
static void send_test_getconfig (const char *filespec,
                                 boolean withdef,
                                 boolean with_state,
                                 const char *xpath_filter)
{
    status_t res = NO_ERR;

    if (xpath_filter || with_state) {
        res = db_api_send_getfilter((const xmlChar *)filespec,
                                    withdef,
                                    !with_state,
                                    (const xmlChar *)xpath_filter);
    } else {
        res = db_api_send_getconfig((const xmlChar *)filespec, withdef);
    }
    if (res != NO_ERR) {
        log_error("\nSend test getconfig failed (%s)\n",
                  get_error_string(res));
    } else if (LOGDEBUG) {
        log_debug("\nSend test getconfig OK\n");
    }

} /* send_test_getconfig */

Filtered Retrieval Example

The db_api_send_getfilter function is similar to db_api_send_getconfig, but with extended parameters.

This example retrieves the RESTCONF monitoring state information:

> db-api-app --getconfig --withdef --with-state --xpath-filter=/restconf-state --filespec=test.xml

The server might reply:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://yumaworks.com/ns/yumaworks-db-api">
  <restconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring">
    <capabilities>
      <capability>urn:ietf:params:restconf:capability:depth:1.0</capability>
      <capability>urn:ietf:params:restconf:capability:with-defaults:1.0</capability>
      <capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability>
      <capability>urn:ietf:params:restconf:capability:fields:1.0</capability>
      <capability>urn:ietf:params:restconf:capability:replay:1.0</capability>
      <capability>urn:ietf:params:restconf:capability:filter:1.0</capability>
      <capability>urn:ietf:params:restconf:capability:yang-patch:1.0</capability>
    </capabilities>
    <streams>
      <stream>
        <access>
          <encoding>xml</encoding>
          <location>http://localhost/restconf/stream</location>
        </access>
        <description>default RESTCONF event stream</description>
        <name>RESTCONF</name>
        <replay-log-creation-time>2018-06-05T16:17:40Z</replay-log-creation-time>
        <replay-support>true</replay-support>
      </stream>
    </streams>
  </restconf-state>
</config>

Subsystem RPC Requests

This API allows the subsystem to send any RPC request to the server. This should be considered as a last resort low-level API if none of the other DB-APIs can be used.

status_t db_api_send_subrpc_request(const xmlChar *user_id, const xmlChar *rpc_modname, const xmlChar *rpc_name, const xmlChar *in_filespec, const xmlChar *out_filespec)

Create a <subrpc-request> request and send it to the main server.

Parameters
  • user_id -- user name to run RPC on server; NULL system user

  • rpc_modname -- module name containing the RPC or NULL to scan all modules for the first match of rpc_name

  • rpc_name -- name of the RPC (must be present)

  • in_filespec -- file specification to contain the XML instance document for the rpc-method element (if needed)

  • out_filespec -- file specification to contain the XML instance document retrieved from the server (must be present)

Return values

out_filespec -- file named by out_filespec created if operation succeeded.

Returns

status

Subsystem RPC Request Example

The <subrpc-request> message is used by a subsystem to send an RPC Operation Request to the server.

The <subrpc-response> message is used by the server to send an RPC reply message to the subsystem that sent the subrpc-request message.

The rpc1.yang module is used for this example:

module rpc1 {
  namespace "http://www.yumaworks.com/ns/rpc1";
  prefix r1;
  revision "2020-02-25";

  rpc rpc1 {
    input {
      leaf A {
        type string;
      }
      leaf B {
        type int32;
      }
    }
    output {
      leaf C {
        type string;
      }
      leaf D {
        type int32;
      }
    }
   }

} // module rpc1

The input file rpc1.xml:

<rpc1 xmlns="http://www.yumaworks.com/ns/rpc1">
  <A>test</A>
  <B>11</B>
</rpc1>

The code to send the RPC request to the server:

#include "dp_api.h"

const xmlChar *user_id = (const xmlChar *)"admin1";
const xmlChar *rpc_modname = (const xmlChar *)"rpc1";
const xmlChar *rpc_name = (const xmlChar *)"rpc1";
const xmlChar *in_filename = (const xmlChar *)"rpc1.xml";
const xmlChar *out_filename = (const xmlChar *)"rpc1-out.xml";

status_t res =
    db_api_send_subrpc_request(user_id,
                               rpc_modname,
                               rpc_name,
                               in_filename,
                               out_filename);

The output file rpc1-out.xml:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="http://www.yumaworks.com/ns/rpc1">
  <C>test1</C>
  <D>14</D>
</rpc-reply>

Subsystem Action Request Example

A YANG Action Request is just a special form of the RPC operation. The same format is used here as in the NETCONF protocol for the YANG action RPC.

The ex-action.yang module is used for the example:

module ex-action {
  yang-version 1.1;
  namespace "http://netconfcentral.org/ns/ex-action";
  prefix exa;
  import ietf-yang-types { prefix yang; }
  revision 2020-03-06;

  list server {
    key name;
    leaf name {
      type string;
      description "Server name";
    }
    action reset {
      input {
        leaf reset-msg {
          type string;
          description "Log message to print before server reset";
        }
      }
      output {
        leaf reset-finished-at {
          type yang:date-and-time;
          description "Time the reset was done on the server";
        }
      }
    }
  }
}

The act1.xml input file:

<action xmlns="urn:ietf:params:xml:ns:yang:1">
  <server xmlns="http://netconfcentral.org/ns/ex-action">
   <name>test1</name>
   <reset>
    <reset-msg>this is a reset message</reset-msg>
   </reset>
  </server>
</action>

The code to send the action request to the server:

#include "dp_api.h"

const xmlChar *user_id = (const xmlChar *)"admin1";
const xmlChar *rpc_modname = (const xmlChar *)"ex-action";
const xmlChar *rpc_name = (const xmlChar *)"reset";
const xmlChar *in_filename = (const xmlChar *)"act1.xml";
const xmlChar *out_filename = (const xmlChar *)"act1-out.xml";

status_t res =
    db_api_send_subrpc_request(user_id,
                               rpc_modname,
                               rpc_name,
                               in_filename,
                               out_filename);

The output file act1-out.xml:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="http://netconfcentral.org/ns/ex-action">
  <reset-finished-at>2020-03-11T19:50:30Z</reset-finished-at>
</rpc-reply>

gNMI Subsystem

The following diagram illustrates key components of the gNMI and netconfd-pro integration.

../_images/ypgnmi_integration.png

As illustrated above gNMI clients can be written in any languages that are supported for gNMI clients. The client part is out of the scope of this document and the current gNMI protocol integration does not include client part.

  • The clients communicate to the target (netconfd-pro) with help of the ypgnmi-app application and send gRPC request to the application.

  • The ypgnmi-app application is written in GO language and talk to the netconfd-pro server via socket with XML encoding and acts as a YControl subsystem.

  • The main role of this application is to translate clients requests to XML and contact netconfd-pro server and send the replies back to the gNMI clients.

  • The ypgnmi-app application is a YControl subsystem that communicates to the netconfd-pro server and also it is a gNMI server that communicates to the gNMI clients.

  • It is responsible for encoding conversion between gNMI gRPC to XML that are sent to the netconfd-pro server and vise versa.

The messages processing between gNMI client to the netconfd-pro server can be split into the following components:

  • gNMI clients to the ypgnmi-app application processing - includes message parsing and conversion of the messages to the XML message

  • The ypgnmi-app application to the netconfd-pro server messages processing. YControl messages exchange encoded in XML

  • The netconfd-pro server internal processing – includes subsystem registration, subsystem messages handling and parsing, conversion to RESTCONF RCB for the request processing.

    • YANG-PATCH in case of gNMI SetRequest

    • RESTCONF GET in case of gNMI GetRequest and Subscribe with mode ONCE

gNMI Client to netconfd-pro Processing

The following diagram illustrates the messages processing path from gNMI clients to the netconfd-pro server and to the database for configuration and retrieval.

../_images/gnmi_netconfd.png

ypgnmi-app Processing

The ypgnmi-app application implements multiple goroutines to manage the replies and the clients. All of the managers are goroutines. They run in parallel and asynchronously.

  • Client Manager goroutine: Responsible for all the gNMI clients connections. After the client contacts the gNMI server, this goroutine register the client, store its information and run all the authentication processing. It verifies the certificates and triggers the connection to be shutdown.

  • Reply Manager goroutine: This manager is responsible for any already parsed messages from the netconfd-pro server or gNMI client, it stores any not processed messages that are ready to be processed. It is responsible to find the corresponding clients that wait for the response and triggers the response procedure.

  • Message Parser goroutine: It is responsible for all the messages parsing. It parses messages from clients encoded in the protobuf and convert them into XML encoded messages to be send to the netconfd-pro server and vice versa.

  • Message Manager goroutine: This manager is responsible for storing any ready to be processed messages that are going to the netconfd-pro server and that are coming back from the server.

The core of the ypgnmi-app is the gNMI server that is build on top of gRPC server. It contains gNMI server code that responsible for the following:

  • gNMI client to the netconfd-pro server communication

  • Handle GET/SET/Subscribe/Capability callbacks. Send/get messages from the netconfd-pro server and response to the clients.

  • Register gNMI server

  • Register gRPC server for the protobuf message handling

  • Run main Serve loop that handles all the client/server communication

The ypgnmi-app application implements the HTTP/2 server over TLS in Go standard package golang.org/x/net/http2. This package provides authentication, connection, listen, etc handlers that fully fulfill all the requirements for the ypgnmi-app application.

The ypgnmi-app application has the following startup steps for the gNMI server component:

  • Initialize all the prerequisites and parse all the CLI parameters

  • Based on the netconfd-pro server modules capability create gNMI target

  • Open TCP socket to listen for clients requests

  • Serve any incoming gNMI messages from gNMI clients and re-transmit them to the netconfd-pro server if needed with help of all the goroutine managers.

The ypgnmi-app acts as a YControl subsystem. It continuously listens to the netconfd-pro server and keeps the AF_LOCAL or TCP socket open to continue communication whenever it is needed. The communication is terminated only if the ypgnmi-app application is terminated or the netconfd-pro server terminates.

All the message definitions described in the yumaworks-yp-gnmi.yang YANG module are similar to the original gNMI .proto message definitions which makes the conversion easier and faster.

The ypgnmi-app application has the following startup steps to initialize connection with the netconfd-pro server:

  • Initialize all the prerequisites and parse all the CLI parameters

  • Open socket and send <ncx-connect> request to the server with

    • transport = netconf-aflocal

    • protocol = yp-gnmi

  • Register yp-gnmi service

    • Send <register-request> to the server

    • Register the 'yp-gnmi' subsystem and initialize all corresponding code in the netconfd-pro server to be ready to handle 'yp-gnmi' service requests

  • Create data model structure that will be used for gNMI client/server communication

    • Send <config-request> to the server

    • Feed the gNMI ModelData with all supported modules

  • Keep listening socket until terminated

ypgnmi-app Message Format

The ypgnmi-app application uses several messages to interact with the netconfd-pro server.

These messages are defined in the yumaworks-yp-gnmi.yang YANG module. The ypgnmi-app payload is defined as a YANG container that augments the YControl “message-payload” container.

The following diagram illustrates the YControl messages overview:

../_images/ypgnmi_messages.png

ypgnmi-app Registration Message Flow

During the startup phase the server will initialize the yp-gnmi subsystem callback functions and handlers (similar way as for db-api module does).

The connection with the server is getting started with <ncx-connect> message that adds the YControl subsystem with the “yp-gnmi” subsystem ID to the server.

YControl protocol connection parameters:

  • transport: netconf-aflocal

  • protocol: yp-gnmi

  • <port-num> not used

Additional parameters:

  • subsys-id: yp-gnmi

The Registration message flow looks as follows:

  • ypgnmi-app to Server: config-request The service requests the netconfd-pro server its configuration in 1 message.

  • Server to ypgnmi-app: config-response The server responds to the config request with a list of modules that need to be loaded to gNMI server DataModel

  • ypgnmi-app to Server: register-request The ypgnmi-app service registers callbacks supported by the subsystem.

  • Server to ypgnmi-app: ok The server responds to the register request with an <ok> or an <error> message

netconfd-pro Processing for gNMI

The following diagram illustrates how the ypgnmi-app application requests are getting processed in the netconfd-pro server.

../_images/gnmi_internal_message.png

The message processing steps:

  • Parse an incoming <ycontrol> message from a subsystem

  • Handle an incoming <ycontrol> message from a subsystem and dispatch this message to the service handler

  • Run callback template for YControl yp-gnmi services: message handler

  • Initializer the RESTCONF specific dispatcher for the yp-gnmi request

    • Dispatch RESTCONF GET request in case of gNMI GetRequest

    • Dispatch YANG-PATCH processing in case of gNMI SetRequest

  • Construct the Response based on the RESTCONF dispatcher return values

  • Send the reply to the yp-gnmi service

Refer to the gNMI Service Definition section for more details.

gRPC Subsystem

This subsystem is only available in the 21.10T release train.

This section describes the gRPC integration within the netconfd-pro server and ypgrpc-go-app application.

../_images/grpc_subsystem.png

The above diagram illustrates deployment of the gRPC server, all its Services and messages handling and how the netconfd-pro server is integrated into this deployment.

The gRPC server and netconfd-pro server integration is deployed with help of ypgrpc-go-app application that transfers information between integrated gRPC server and gRPC clients and netconfd-pro server. Also, this is an application that provides faster and easier platform to implement gRPC Services. It is similar to db-api-app where users create instrumentation for their Services and RPCs.

Refer to the YumaPro gRPC User Guide document for details on yp-GRPC and the ypgrpc-go-app application.