../_images/logo.png

YumaPro yp-system API Guide

yp-system Introduction

This document contains a quick reference for the API callback interface for the yp-system component in the netconfd-pro server.

The netconfd-pro server supports an external vendor library that is loaded at boot-time by the server, similar to a SIL for a YANG module. This library code is run in the context of the main server process. It is used to hook vendor-specific functions into the server,.

The following tasks are supported by the yp-system library interface:

  • yp-system External Interface : Creating and using the general system SIL library

  • YP-HA Interface : Provide an external controller for changing the YP-HA server role

  • Access Control Model Interface : Provide an external access control system instead of NACM

  • SYSLOG Interface : Provide an external syslog interface instead of the Linux API

  • System Callback Functions : Provide server callbacks not specific to a single YANG module

The default system library is located in called libyp_system.so, and it is located in the library path, such as /usr/lib/yumapro/libyp_system.so.

There is an example yp-system library in the 'libsystem' directory of the YumaPro source tree. This file in the 'src' directory called example-system.c contains some stub code showing how this library is used.

Copy and Setup the libsystem subtree

The default library created by building libsystem is libyp_system-example.so. This is not the correct filename, in order to prevent the real yp-server library from being overwritten by the empty example library, if “make install” is run.

The 'libsystem' subtree should be copied to your own development area and modified.

#
# to make a real library, copy this directory contents
# to a new location and change yp-system-example to yp_system
# in the SUBDIR_NM macro below
#
#  SUBDIR_NM=yp_system
#
SUBDIR_NM=yp_system-example

Change the string yp-system-example to yp-system in the src/Makefile in the copy of libsystem.

yp-system

The following API functions should be defined in all yp-library implementations:

  • yp_system_init_profile: Change default settings in the server profile defined in agt/agt.h

  • yp_system_init1: Phase 1 initialization function

  • yp_system_init2: Phase 2 initialization function

  • yp_system_cleanup: Server cleanup function

The following example shows example-system.h contents from the libsystem/src directory:

#ifndef _H_example_system
#define _H_example_system
/* 
 * Copyright (c) 2012 - 2014, YumaWorks, Inc., All Rights Reserved.
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */

#ifndef _H_agt
#include "agt.h"
#endif

#ifndef _H_status
#include "status.h"
#endif


/* system init server profile callback
 *
 * Initialize the server profile if needed
 *
 * INPUTS:
 *  profile == server profile to change if needed
 */
extern void yp_system_init_profile (agt_profile_t *profile);


/* system init1 callback
 * init1 system call
 * this callback is invoked twice; before and after CLI processing
 * INPUTS:
 * pre_cli == TRUE if this call is before the CLI parameters
 *            have been read
 *            FALSE if this call is after the CLI parameters
 *            have been read
 * RETURNS:
 *  status; error will abort startup
 */
extern status_t yp_system_init1 (boolean pre_cli);


/* system init2 callback
 * init2 system call
 * this callback is invoked twice; before and after 
 * load_running_config processing
 *
 * INPUTS:
 * pre_load == TRUE if this call is before the running config
 *            has been loaded
 *            FALSE if this call is after the running config
 *            has been loaded
 * RETURNS:
 *  status; error will abort startup
 */
extern status_t yp_system_init2 (boolean pre_load);


/* system cleanup callback
 * this callback is invoked once during agt_cleanup
 */
extern void yp_system_cleanup (void);


#endif

yp_system_init_profile

This function is used to modify the defaults in the agt_profile_t Struct used by the server. This allows features and various behavior to be configured. Many of these settings have associated CLI/conf parameters that can change the default at run-time. Refer to the agt_profile_t Struct section for details on the contents of the Agent Profile structure.

typedef void (*agt_system_init_profile_fn_t)(agt_profile_t *profile)

system init server profile callback

Initialize the server profile if needed

Param profile

server profile to change if needed

The contents of this function should set profile fields as needed.

Note that if the field has a CLI override parameter with a default value, then the YANG default for that parameter needs to be changed if the profile default is changed.

Example Profile Initialization Function

This example profile initialization function shows how to disable all the Yuma YANG modules:

void yp_system_init_profile (agt_profile_t *profile);
{

    profile->agt_use_yuma_arp = false;
    profile->agt_use_yuma_if = false;
    profile->agt_use_yuma_mysession = false;
    profile->agt_use_yuma_proc = false;
    profile->agt_use_yuma_proc = false;

} /* yp_system_init_profile */

yp_system_init1

This function is used to initialize the server during phase 1 initialization.

This function is called twice during system initialization:

  1. pre_cli = TRUE: Called before the CLI and conf file parameters have been processed

  2. pre_cli = FALSE: Called after the CLI and conf file parameters have been processed

typedef status_t (*agt_system_init1_fn_t)(boolean pre_cli)

system init1 callback

init1 system call this callback is invoked twice; before and after CLI processing

Param pre_cli

TRUE if this call is before the CLI parameters have been read

FALSE if this call is after the CLI parameters have been read

Return

status; error will abort startup

This function should register system callback functions and setup the system.

The first time the function is called the pre_cli flag is true. This is before the CLI and config file parameters have been applied. The second time the function is called the pre_cli flag is false. This is after the CLI and config file parameters have been applied.

Example Phase 1 Initialization Function

This example phase 1 initialization function shows setup of external vendor logging and external access control model callbacks :

/* system init1 callback
 * init1 system call
 * this callback is invoked twice; before and after CLI processing
 * INPUTS:
 * pre_cli == TRUE if this call is before the CLI parameters
 *            have been read
 *            FALSE if this call is after the CLI parameters
 *            have been read
 * RETURNS:
 *  status; error will abort startup
 */
status_t yp_system_init1 (boolean pre_cli)
{
    if (pre_cli) {
        // example -- register vendor callback to consume logging output.
        log_vendor_extern_register_send_fn(log_vendor_send_fn);
    } else {
        // example -- external NACM callbacks
        // load module for external module
        // with ncxmod_load_module

        // register the external ACM callbacks
        // this will have no affect unless the
        // yp_system_init_profile fn sets the
        // agt_acm_model to AGT_ACM_MODEL_EXTERNAL
        agt_acm_extern_register_callbacks(acm_extern_rpc,
                                          acm_extern_notif,
                                          acm_extern_write,
                                          acm_extern_read);
    }
    return NO_ERR;
}  /* yp_system_init1 */

yp_system_init2

This function is used to initialize the server during phase 2 initialization. This function is invoked after all phase 1 initialization has been successfully completed.

The first time the function is called the pre_load flag is true. This is before the running configuration has been initialized. The second time the function is called the pre_load flag is false. This is after the running configuration has been initialized with configuration from non-volatile storage and YANG defaults.

This function should initialize global callbacks based configuration settings.

typedef status_t (*agt_system_init2_fn_t)(boolean pre_load)

system init2 callback

init2 system call this callback is invoked twice; before and after load_running_config processing

Param pre_load

TRUE if this call is before the running config has been loaded

FALSE if this call is after the running config has been loaded

Return

status; error will abort startup

Example Phase 2 Initialization Function

This example phase 2 initialization function shows the general structure of the code for this function.

/* system init2 callback
 * init2 system call
 * this callback is invoked twice; before and after
 * load_running_config processing
 *
 * INPUTS:
 * pre_load == TRUE if this call is before the running config
 *            has been loaded
 *            FALSE if this call is after the running config
 *            has been loaded
 * RETURNS:
 *  status; error will abort startup
 */

status_t yp_system_init2 (boolean pre_load)
{
    log_debug("\nyp_system init2\n");

    if (pre_load) {
        ;
    } else {
        ;
    }
    return NO_ERR;
}

yp_system_cleanup

This function is used to cleanup the system when the server is shutting down or restarting.

typedef void (*agt_system_cleanup_fn_t)(void)

system cleanup callback

this callback is invoked once during agt_cleanup

Example Phase 2 Initialization Function

This example cleanup function shows the general structure of the code for this function.

/* system cleanup callback
 * this callback is invoked once during agt_cleanup
 */
void yp_system_cleanup (void)
{
    log_debug("\nyp_system cleanup\n");

    /* put your cleanup function calls here */

} /* system cleanup callback */

YP-HA Interface

The High Availability features in the netconfd-pro server can be accessed from the yp-system library.

There are two functions that call be called from the “init1” or “init2” phases in the yp-system library initialization. They can also be called from an agt_timer callback function during system run time.

Normally when the server running with YP-HA enabled (--ha-enabled=true), the server will wait until it is told which HA role should be used (active or standby). The :--ha-initial-active CLI parameter can be used to hard-wire the YA role in the configuration. This should only be used for debugging. In production networks, the HA management system should signal all the HA role changes to the server.

agt_ha_role_t

The 'agt_ha_role_t' is an enumeration specifying the different YP-HA roles.

enum agt_ha_role_t

server HA mode mode

Values:

enumerator AGT_HA_ROLE_NONE

not set

enumerator AGT_HA_ROLE_NOT_SET

HA role not set.

enumerator AGT_HA_ROLE_STANDBY

HA standby role.

enumerator AGT_HA_ROLE_ACTIVE

HA active role.

YP-HA Interface Functions

The YP-HA role can be set from internal API functions of from the DB-API subsystem interface, using the “yp-ha-role” message.

agt_yp_ha_get_role

The 'agt_yp_ha_get_role' function is used to retrieve the current YP-HA role used by the server.

agt_ha_role_t agt_yp_ha_get_role(void)

Get the YP-HA server role for this server.

Returns

server role enum

agt_yp_ha_be_active

The 'agt_yp_ha_be_active' function is used to tell the server to be the active server in the YP-HA protocol.

status_t agt_yp_ha_be_active(void)

Put this server in YP-HA Active mode.

Returns

status

agt_yp_ha_be_standby

The 'agt_yp_ha_be_standby' function is used to tell the server to be a standby server in the YP-HA protocol.

status_t agt_yp_ha_be_standby(const xmlChar *new_server_id)

Put this server in YP-HA Standby mode.

Parameters

new_server_id -- server-id of the new active server

Returns

status NO_ERR if this server able to enter YP-HA Standby mode

agt_yp_ha_be_none

The 'agt_yp_ha_be_none' function is used to tell the server to leave its current YP-HA role, go offline, and wait for a new YP-HA role to be set.

status_t agt_yp_ha_be_none(void)

Put this server in WAIT_ROLE state.

Returns

status

DB-API <yp-ha-role> Event Message

The DB-API service has a new utility function to set the YP-HA role on the local server.

The <yp-ha-mode> message is sent from a subsystem to the main server. It has 2 variants. Either the “go-active” or the “go-standby” variant is sent.

container yp-ha-mode {
  description
   "Message type: subsys-event;
    Purpose: send mode change event to the server
    Expected Response Message: none";

  choice action {
    mandatory true;
    leaf go-active {
      type empty;
      description "Become the YP-HA active server";
    }
    container go-standby {
      description "Become a YP-HA standby server";
      leaf new-active {
        type string;
        mandatory true;
        description
          "Server name of the active server to use";
      }
    }
  }
}  // container yp-ha-mode

Example message to set the YP-HA Active mode:

<?xml version="1.0" encoding="UTF-8"?>
<ycontrol xmlns="http://yumaworks.com/ns/yumaworks-ycontrol">
  <message-id>2</message-id>
  <message-type>subsys-event</message-type>
  <server-id>ha-1</server-id>
  <subsys-id>subsys1</subsys-id>
  <service-id>db-api</service-id>
  <payload>
    <db-api xmlns="http://yumaworks.com/ns/yumaworks-db-api">
       <yp-ha-mode>
          <go-active/>
      </yp-ha-mode>
    </db-api>
  </payload>
</ycontrol>

Access Control Model Interface

The netconfd-pro server supports 2 different access control models:

If the 'external' mode is used, then ACM callback functions must be registered with yp-system API functions that the server needs to validate the following types of client access:

  • protocol operation requests

  • database read and write requests

  • notification delivery

Register ACM Callback Functions

The function 'agt_acm_extern_register_callbacks' in agt/agt_acm_extern.h is used by the external system code to register its own callback functions to handle access control requests. These functions are used by the system instead of its own access control cache and data model.

If the external access control method is selected in the system initialization then these callback functions MUST be provided or all access requests will be granted by default!

void agt_acm_extern_register_callbacks(agt_acm_extern_rpc_fn_t rpcfn, agt_acm_extern_notif_fn_t notfn, agt_acm_extern_write_fn_t writefn, agt_acm_extern_read_fn_t readfn)

Register the external callbacks for ACM implementation.

A NULL callback means that type of access will always be granted!!!

Parameters
  • rpcfn -- check-rpc function callback

  • notfn -- check-notification function callback

  • writefn -- check-val-write function callback

  • readfn -- check-val-write function callback

The following functions must be registered:

  • RPC Request Callback Function

  • Notification Send Callback Function

  • Database Write Request Callback Function

  • Database Read Request Callback Function

The following example "init1" function shows how this function might be used:

 status_t yp_system_init1 (boolean pre_cli)
 {
     log_debug("\nyp_system init1\n");

     if (pre_cli) {
         ;
     } else {
         // example -- external NACM callbacks
         // load module for external module
         // with ncxmod_load_module

         // register the external ACM callbacks
         // this will have no affect unless the
         // yp_system_init_profile fn sets the
         // agt_acm_model to AGT_ACM_MODEL_EXTERNAL
         agt_acm_extern_register_callbacks(acm_extern_rpc,
                                           acm_extern_notif,
                                           acm_extern_write,
                                           acm_extern_read);
     }
     return NO_ERR;
 }

RPC Request

Checks if the user is allowed to invoke the specified YANG-defined protocol operation. The 'agt_acm_extern_rpc_fn_t' template is used for this callback.

typedef boolean (*agt_acm_extern_rpc_fn_t)(xml_msg_hdr_t *msg, const xmlChar *user, const obj_template_t *rpcobj)

Check if the specified user is allowed to invoke an RPC.

Param msg

XML header in incoming message in progress

Param user

user name string

Param rpcobj

obj_template_t for the RPC method to check

Return

TRUE if user allowed invoke this RPC; FALSE otherwise

This example RPC Request callback can be found in example-system.c:

static boolean
    acm_extern_rpc (xml_msg_hdr_t *msg,
                    const xmlChar *user,
                    const obj_template_t *rpcobj)
{
    (void)msg;

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

    log_debug("\nChecking RPC access for user %s to operation '%s:%s'",
              user, modname, objname);

    // check access here

    log_debug("\nacm_extern_rpc: return OK\n");
    return TRUE;

} /* acm_extern_rpc */

Notification Send

Checks if the user is allowed to receive the specified YANG-defined notification event. The 'agt_acm_extern_notif_fn_t' template is used for this callback.

typedef boolean (*agt_acm_extern_notif_fn_t)(const xmlChar *user, const obj_template_t *notifobj)

Check if the specified user is allowed to receive a notification event.

Param user

user name string

Param notifobj

obj_template_t for the notification event to check

Return

TRUE if user allowed receive this notification event

FALSE otherwise

This example Notification Send callback can be found in example-system.c:

static boolean
    acm_extern_notif (const xmlChar *user,
                      const obj_template_t *notifobj)
{
    const xmlChar *modname = obj_get_mod_name(rpcobj);
    const xmlChar *objname = obj_get_name(rpcobj);

    log_debug("\nChecking Notification access for "
              "user %s to event '%s:%s'",
              user, modname, objname);

    // check access here

    log_debug("\nacm_extern_notif: return OK\n");
    return TRUE;

} /* acm_extern_notif */

Database Write

Checks if the user is allowed to edit the specified YANG-defined data node. The 'agt_acm_extern_write_fn_t' template is used for this callback.

typedef boolean (*agt_acm_extern_write_fn_t)(xml_msg_hdr_t *msg, const xmlChar *user, const val_value_t *newval, const val_value_t *curval, op_editop_t editop)

Check if the specified user is allowed to access a value node.

The val->obj template will be checked against the val->editop requested access and the user's configured max-access

Param msg

XML header from incoming message in progress

Param newval

val_value_t

in progress to check

(may be NULL, if curval set)

Param curval

val_value_t

in progress to check

(may be NULL, if newval set)

Param val

val_value_t in progress to check

Param editop

requested CRUD operation

Return

TRUE if user allowed this level of access to the value node

This example Database Write callback can be found in example-system.c:

static boolean
    acm_extern_write (xml_msg_hdr_t *msg,
                      const xmlChar *user,
                      const val_value_t *newval,
                      const val_value_t *curval,
                      op_editop_t editop)
{
    (void)msg;
    (void)user;
    (void)newval;
    (void)curval;
    (void)editop;
    log_debug("\nacm_extern_write: return OK\n");
    return TRUE;

} /* acm_extern_write */

Database Read

Checks if the user is allowed to read the specified YANG-defined data node. The 'agt_acm_extern_read_fn_t' template is used for this callback.

typedef boolean (*agt_acm_extern_read_fn_t)(xml_msg_hdr_t *msg, const xmlChar *user, const val_value_t *val)

Check if the specified user is allowed to read a value node.

Param msg

XML header from incoming message in progress

Param user

user name string

Param val

val_value_t in progress to check

Return

TRUE if user allowed read access to the value node

This example Database Write callback can be found in example-system.c:

static boolean
    acm_extern_read (xml_msg_hdr_t *msg,
                     const xmlChar *user,
                     const val_value_t *val)
{
    (void)msg;
    (void)user;
    (void)val;
    log_debug("\nacm_extern_read: return OK\n");
    return TRUE;

} /* acm_extern_read */

Syslog Interface

The external syslog interface allows a vendor-specific syslog send function to be used instead of the Linux system syslog daemon. The yp_library user library code can be used to initialize an external syslog send function.

The external syslog send function must follow the C function templates defined in ncx/log_vendor_extern.h:

  • logfn_vendor_init1_t: Phase 1 initialization function

  • logfn_vendor_send_t: syslog send function

  • logfn_vendor_cleanup_t: Termination cleanup function

The 'example-system C and H files in the yp-system directory contains example usage of these callback functions.

When the vendor callback is invoked, the vendor code should translate the YumaPro application parameters into an "application" and/or "facility" equivalent appropriate to the vendor logging schema. Likewise, it should translate the YumaPro level parameters into a "message type/level" appropriate to its own requirements.

The following H files need to be included to use the functions in this section:

#include "log.h"
#include "log_vendor.h"
#include "log_vendor_extern.h"

logfn_vendor_send_t

This is the Logging Send function, called by the server when a SYSLOG message needs to be sent.

typedef void (*logfn_vendor_send_t)(log_debug_app_t app, log_debug_t level, const char *fstr, va_list args)

Vendor Logging Send Callback.

Param app

YumaPro application (such as netconfd-pro or yangcli-pro) This info is already known to syslog via the connect.

Param level

YumaPro log message level. This will be translated appropriately into a syslog equivalent

Param fstr

start ptr to format string

Param args

va_list

The following log send function can be found in example-system.c:

static void
    log_vendor_send_fn (log_debug_app_t app,
                        log_debug_t level,
                        const char *fstr,
                        va_list args)
{
    (void)app;
    (void)level;
    (void)fstr;
    (void)args;
    return;

}

System Callbacks

The callback functions described in this section are invoked as part of normal server operation.

They can be registered from the yp-system library module or a SIL module. Some internal functions are documented as well. These are needed by the system and multiple callbacks are not supported.

Refer to the file /usr/share/yumapro/src/libsystem/src/example-system.c for examples of the callbacks in this section.

Note

System Callback code can be in a SIL library or the yp-system library.

API

Description

Candidate Reload Callback

cfg_reload_candidate_cb_t: Invoked each time the candidate datastore is reloaded from the running datastore, such as the <discard-changes> operation.

Module Load Callback

ncx_load_cbfn_t: Invoked when a module is loaded into the server.

Module Unload Callback

ncx_unload_cbfn_t: Invoked when a module is unloaded from the server.

Config Replay Callback

agt_replay_fn_t: Invoked when a configuration replay procedure is started or finished.

NV-Load Callback

agt_nvload_fn_t: Invoked when the server needs to retrieve the configuration contents from non-volatile storage.

NV-Save Callback

agt_nvsave_fn_t: Invoked when the configuration needs to be saved to Non-Volatile storage.

Periodic Timer Service

agt_timer_fn_t: Invoked periodically (1-shot or repeat) to do a short amount of work, and not block the running process.

Session Hook Callback

agt_cb_session_hook_t: Invoked when a session starts and ends.

Shutdown Callback

agt_cb_shutdown_t: Invoked when the server is about to restart or shutdown.

Command Complete Callback

agt_cb_command_complete_t: Invoked when the server has completed an RPC operation and the response has been generated to the client (may not have been sent yet).

NACM External Groups Callback

agt_acm_group_cbfn_t: Invoked when the server creates a new client session for NETCONF or RESTCONF sessions. It is used to retrieve a list of group names that should be used, as defined in RFC 8341

User Defined Data Types Callbacks

Invoked when a data node needs to be processed to process the value as it is being created.

Candidate Reload Callback

The Candidate Reload callback is an internal system callback that is used to clear some cached validation data when the running configuration is loaded into to candidate datastore.

Note

This is an internal API and cannot be replaced. Do not use.

There can be one callback registered to be invoked each time the candidate datastore is reloaded from the running datastore, such as the <discard-changes> operation, or anytime the candidate is filled in from <startup>, <running>, or inline configuration.

If the server is run with the :writable-running capability the callback will not be available since the <candidate> datastore is not present when the capability is used. The --target CLI parameter is used to control this behavior.

Module Load Callback

The Module Load callback is a user/system callback that is invoked when a YANG module is loaded into the server. This callback is only invoked for main modules, not submodules.

The following function template definition is used for Module Load callback functions:

typedef void (*ncx_load_cbfn_t)(ncx_module_t *mod)

user function callback template when a module is loaded into the system

ncx_load_cbfn_t

Run an instrumentation-defined function for a 'module-loaded' event

Param mod

module that was added to the registry

  • The 'mod' pointer represents a module control block structure that is defined in the netconf/src/ncx/ncxtypes.h.

  • This control block is a representation of one module or submodule during and after parsing.

  • It provides access to the module specific information, such as module name, revision, prefix, and other module specific information.

  • Note: do not alter any fields in this structure

Module Load Callback Initialization and Cleanup

The 'ncx_set_load_callback' function is used to declare the Module Load callback. The registration can be done during the Initialization Phase 2 after the running configuration has been loaded from the startup file. It can actually be done at any time, as needed.

status_t ncx_set_load_callback(ncx_load_cbfn_t cbfn)

Set the callback function for a load-module event.

Parameters

cbfn -- callback function to use

Returns

status

Example registration function usage:

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register load module callback */
     res = ncx_set_load_callback(add_module_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

The 'ncx_clear_load_callback' function is used to unload the Module Load callback.

void ncx_clear_load_callback(ncx_load_cbfn_t cbfn)

Clear the callback function for a load-module event.

Parameters

cbfn -- callback function to use

Example un-register function usage:

 void interfaces_cleanup (void)
 {
     // ...
     ncx_clear_load_callback(add_module_callback);

     ...
 }

Module Load Callback Function Example

The 'load_module_callback' function is used to to record a backptr for each module or imported module that is encountered during the <load> operation.

static void load_module_callback (ncx_module_t *mod)
{
    ncx_backptr_t *backptr = ncx_new_backptr(mod);
    if (backptr == NULL) {
        log_error("\nError: malloc backptr failed");
        return;
    }
    dlq_enque(backptr, &added_moduleQ);

}  /* load_module_callback */

The 'do_load_module' function is located in agt/agt_ncx_load.c. It is used to load a module at runtime. It does the following operations:

  • registers the callback

  • saves backptrs during the module load

  • reads the saved backptrs after the module load

  • unregisters the callback

  • cleans up the saved backptrs

static status_t
    do_module_load (ses_cb_t *scb,
                    rpc_msg_t *msg,
                    const xmlChar *modname,
                    const xmlChar *revision,
                    ncx_module_t **retmod)
{
    boolean callback_loaded = FALSE;
    ncx_module_t *mod = NULL;
    boolean already_loaded =
        ncx_find_module(modname, revision) ? TRUE : FALSE;

    *retmod = NULL;

    /* save all the loaded modules including imported modules */
    status_t res = ncx_set_load_callback(load_module_callback);
    if (res == NO_ERR) {
        callback_loaded = TRUE;
    } else {
        return res;
    }

#ifdef STATIC_SERVER
    res = ncxmod_load_module(modname,
                             revision,
                             agt_get_savedevQ(),
                             &mod);
#else
    res = agt_load_sil_code(modname,
                            revision,
                            TRUE,
                            AGT_SILTYP_MODULE,
                            FALSE);
    if (res == ERR_NCX_SKIPPED) {
        log_warn("\nWarning: SIL code for module '%s' not found",
                 modname);
        res = ncxmod_load_module(modname,
                                 revision,
                                 agt_get_savedevQ(),
                                 &mod);
        if (res == NO_ERR && mod) {
            ncx_set_mod_unloadable(mod);
        }
    }
#endif  // STATIC_SERVER

    /* reget the module; it should be found if status == NO_ERR */
    if (res == NO_ERR) {
        mod = ncx_find_module(modname, revision);
        if (mod) {
            /* use the list in added_moduleQ not just this 1 module
             * this tracks the imports loaded dynamically as well
             * as the main module loaded by the <load> operation
             */
            res = process_loaded_modules(scb, msg, NULL);
        } else {
            res = ERR_NCX_OPERATION_FAILED;
        }
    }

    if (callback_loaded) {
        ncx_clear_load_callback(load_module_callback);
    }

    /* done with the added_moduleQ; just used to process modules
     * not to save the list of loaded modules (load_moduleQ)
     */
    ncx_clean_backptrQ(&added_moduleQ);

    if (res == NO_ERR) {
        SET_MOD_SIL_LOADED(mod);
        *retmod = mod;
        if (!already_loaded) {
            /* add to the load_moduleQ */
            add_loaded_module(modname);
        }
    }

    return res;

} /* do_module_load */

The following functions may be used to obtain required information from the loaded module:

  • ncx_get_modname() - Get the main module name

  • ncx_get_mod_nsid() - Get the main module namespace ID

  • ncx_get_modversion() - Get the module version

  • ncx_get_modnamespace() - Get the module namespace URI

const xmlChar *ncx_get_modname(const ncx_module_t *mod)

Get the main module name.

Parameters

mod -- module or submodule to get main module name

Returns

main module name or NULL if error

xmlns_id_t ncx_get_mod_nsid(const ncx_module_t *mod)

Get the main module namespace ID.

Parameters

mod -- module or submodule to get main module namespace ID

Returns

namespace id number

const xmlChar *ncx_get_modversion(const ncx_module_t *mod)

Get the [sub]module version.

Parameters

mod -- module or submodule to get module version

Returns

module version or NULL if error

const xmlChar *ncx_get_modnamespace(const ncx_module_t *mod)

Get the module namespace URI.

Parameters

mod -- module or submodule to get module namespace

Returns

module namespace or NULL if error

Module Unload Callback

The Module Unload callback is a user/system callback that is invoked when a YANG module is unloaded from the server. This callback is only invoked for main modules, not submodules.

The following function template definition is used for Module Unload callback functions:

typedef void (*ncx_unload_cbfn_t)(ncx_module_t *mod)

user function callback template when a module is unloaded from the system

ncx_unload_cbfn_t

Run an instrumentation-defined function for a 'module-loaded' event

Param mod

module that is being removed

  • The 'mod' pointer represents a module control block structure that is defined in the netconf/src/ncx/ncxtypes.h.

  • This control block is a representation of one module or submodule during and after parsing.

  • It provides access to the module specific information, such as module name, revision, prefix, and other module specific information.

  • Note: do not alter any fields in this structure

Module Unload Callback Initialization and Cleanup

The 'ncx_set_unload_callback' function is used to declare the Module Unload callback. The registration can be done during the Initialization Phase 2 after the running configuration has been loaded from the startup file. It can actually be done at any time, as needed.

status_t ncx_set_unload_callback(ncx_unload_cbfn_t cbfn)

Set the callback function for an unload-module event.

Parameters

cbfn -- callback function to use

Returns

status

Example Module Unload register usage:

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register unload module callback */
     res = ncx_set_unload_callback(remove_module_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

The 'ncx_clear_unload_callback' function is used to unload the Module Unload callback.

void ncx_clear_unload_callback(ncx_unload_cbfn_t cbfn)

Clear the callback function for an unload-module event.

Parameters

cbfn -- callback function to use

Example Module Unload un-register usage:

 void interfaces_cleanup (void)
 {
     // ...
     ncx_clear_unload_callback(remove_module_callback);

     // ...
 }

Module Unload Callback Function Example

In this example, the callback code will find a val_value_t tree, based on the information from 'mod' pointer, and remove it from the static data list.


/********************************************************************
* FUNCTION remove_module
*
* Remove a module entry from the static modules list
*
* INPUTS:
*   mod == module to remove
*
********************************************************************/
static void
    remove_module (ncx_module_t *mod)
{

    xmlns_id_t nsid = val_get_nsid(mymodules_val);

    val_value_t *module = val_get_first_child(mymodules_val);
    for (; module; module = val_get_next_child(module)) {

        val_value_t *checkval =
            val_find_child_fast(module, nsid, (const xmlChar *)"name");

        /* check the module name, if it match, remove the module from the list */
        if (checkval) {
            if (xml_strcmp(VAL_STR(checkval), ncx_get_modname(mod))) {
                continue;
            }
        } else {
            continue;  // error
        }

        /* check the module revision, if it match, remove the module from the list */
        if (ncx_get_modversion(mod)) {
            checkval =
                val_find_child_fast(module, nsid, (const xmlChar *)"revision");
            if (checkval) {
                if (xml_strcmp(VAL_STR(checkval), ncx_get_modversion(mod))) {
                    continue;
                }
            } else {
                continue;  // error!
            }
        }

        val_remove_child(module);
        val_free_value(module);
        return;
    }

}  /* remove_module */

Config Replay Callback

The Config Replay callback is a user callback that is invoked when the replay configuration procedure is started and finished.

Only one callback function can be registered.

Refer to the Configuration Replay section for complete details on triggering a Configuration Replay procedure.

The following function template definition is used for Config Replay callback functions:

typedef void (*agt_replay_fn_t)(boolean is_start)

replay callback (agt_replay_fn_t)

this callback is invoked when a configuration replay is started or finished, which has been triggered by a call to agt_request_replay()

Param is_start

TRUE if start; FALSE if finish

Config Replay Callback Initialization and Cleanup

The 'agt_register_replay_callback' function is used to register this callback.

void agt_register_replay_callback(agt_replay_fn_t cbfn)

Register a callback function for a replay config event.

Parameters

cbfn -- replay function to use (==NULL to clear)

Example Config Replay register usage:

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register config replay callback */
     res = agt_register_replay_callback(replay_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

There is no unregister function for this callback. The server will cleanup the system automatically during shutdown.

Config Replay Callback Example

static void replay_callback (boolean is_start)
{
    if (is_start) {
        log_debug("\nConfig Replay is starting");
    } else {
        log_debug("\nConfig Replay is ending");
    }
}

NV-Load Callback

The NV-Load callback function is a user callback that is invoked when the running configuration needs to be read from non-volatile storage.

  • This callback is used to provide the server with an XML file instead of the default from the --config CLI parameter.

  • The server will invoke this callback to get the file name and file encoding of the startup configuration file.

  • Only XML encoding is supported at this time.

  • Only one callback can be registered.

  • The check_load_external_config function in agt/agt.c will check this callback during the <load-config> operation.

  • This callback will not be used if the --no-nvstore CLI parameter is used.

Note

If the callback returns NO_ERR and *filespec is empty then the server will use the factory configuration.

The following function template definition is used for the NV-Load callback function:

typedef status_t (*agt_nvload_fn_t)(ncx_display_mode_t *encoding, xmlChar **filespec)

nvload callback

this callback is invoked when some config needs to be read from non-volatile storage

Param encoding

address of encoding for the config

Param filespec

address of filespec containing the config that was loaded

Retval *encoding

set to the enum for the encoding used in the config

Retval *filespec

malloced filespec containing the config that was loaded

== NULL if loading the factory default config

Return

status; error indicates NV-load failed somehow If return NO_ERR and *filespec == NULL then use the factory config

NV-Load Callback Initialization and Cleanup

The 'agt_register_local_nv_handler' function is used to declare the NV-Load callback. The registration can be done during the Initialization Phase 2, after the running configuration has been loaded from the startup file.

status_t agt_register_local_nv_handler(agt_nvload_fn_t load_fn, agt_nvsave_fn_t store_fn)

Register a set of nvstore and nvload callback functions to handle the non-volatile storage of the configuration.

Parameters
  • load_fn -- NV-load callback function

  • store_fn -- NV-save callback function

Returns

status

 static status_t interfaces_init2 (void)
 {
     // ...

     /* register NV-storage handler to load/save config
      * uncomment following to enable
      */
     res = agt_register_local_nv_handler(nvload_callback,
                                         nvsave_callback);
     if (res != NO_ERR) {
         return res;
     }

     // ...
 }

There is no unregister function for this callback. The cleanup will be done automatically by the server.

NV-Load Callback Function Example

/********************************************************************
* FUNCTION nvload_callback
*
* this callback is invoked when some config needs to be read
* from non-volatile storage
*
* INPUTS:
*    encoding == address of encoding for the config
*    filespec == address of filespec containing the config that was loaded
*
* OUTPUTS:
*    *encoding == set to the enum for the encoding used in the config
*    *filespec == malloced filespec containing the config that was loaded
*
* RETURNS:
*    status; error indicates NV-load failed somehow
*    If return NO_ERR and *filespec == NULL then use the factory config
*
*********************************************************************/
static status_t
    nvload_callback (ncx_display_mode_t *encoding,
                    xmlChar **filespec)
{
    log_debug("\nEnter nvload_callback ");

    *filespec = NULL;
    *encoding = NCX_DISPLAY_MODE_XML;

    status_t res = NO_ERR;

    if (ncxmod_test_filespec(EXAMPLE_CONFIG_SPEC)) {

        /* file exists so copy the filespec */
        *filespec = xml_strdup(EXAMPLE_CONFIG_SPEC);
        if (*filespec == NULL) {
            res = ERR_INTERNAL_MEM;
        }
    }

    return res;

}  /* nvload_callback */

The following function is used in the example to search for the specified filespec:

boolean ncxmod_test_filespec(const xmlChar *filespec)

Check the exact filespec to see if it a file.

Parameters

filespec -- file spec to check

Returns

TRUE if valid readable file; FALSE otherwise

NV-Save Callback

The NV-Save callback function is a user callback that is invoked when the running configuration needs to be saved to non-volatile storage.

  • This callback is used to provide the system with an XML file to save, instead of the default from the --config CLI parameter.

  • The server will invoke this callback to pass the file name and file encoding of the configuration file to save.

  • Only XML encoding is supported at this time.

  • Only one callback can be registered.

  • The agt_check_save_external_config function in agt/agt.c will check this callback during the "save-config" procedure.

The following function template definition is used for NV-Save callback functions:

typedef status_t (*agt_nvsave_fn_t)(ncx_display_mode_t encoding, const xmlChar *filespec)

brief nvsave callback

this callback is invoked when some config needs to be saved to non-volatile storage

Param encoding

encoding format for the config (xml only allowed value)

Param filespec

filespec containing the config to save

Return

status; error indicates NV-save failed somehow

NV-Save Callback Initialization and Cleanup

This callback is regered at the same time as the NV Load callback using the 'agt_register_local_nv_handler' function.

There is no cleanup function for this callback. The cleanup will be done automatically by the server.

Refer to the NV-Load Callback Initialization and Cleanup section for details.

NV-Save Callback Function Example

The following example can be found in example-system.c.

#define EXAMPLE_CONFIG_SPEC (const xmlChar *)"/tmp/example-config.xml"
/********************************************************************
* FUNCTION example_nvsave
*
* Nvsave callback is invoked when some config needs to be saved
* to non-volatile storage
*
* INPUTS:
*    encoding == encoding format for the config (xml only allowed value)
*    filespec == filespec containing the config to save
*
* RETURNS:
*    status; error indicates NV-save failed somehow
*
*********************************************************************/
static status_t
  example_nvsave (ncx_display_mode_t encoding,
                  const xmlChar *filespec)
{
    status_t res = NO_ERR;

    if (filespec == NULL || *filespec == 0) {
        res = ERR_NCX_INVALID_VALUE;
    } else if (encoding != NCX_DISPLAY_MODE_XML) {
        res = ERR_NCX_INVALID_VALUE;
    } else {
        res = ncxmod_copy_text_file(filespec, EXAMPLE_CONFIG_SPEC);
    }

    return res;

}  /* example_nvsave */

Periodic Timer Service

Some SIL code may need to be called at periodic intervals to check system status, update counters, and/or perhaps send notifications.

The file agt/agt_timer.h contains the timer access function declarations.

This section provides a brief overview of the SIL timer service.

Timer Callback Function

The timer callback function is expected to do a short amount of work, and not block the running process. The function returns zero for a normal exit, and -1 if there was an error and the timer should be destroyed.

The 'agt_timer_fn_t' template in agt/agt_timer.h is used to define the SIL timer callback function prototype. This typedef defines the callback function template expected by the server for use with the timer service:

typedef int (*agt_timer_fn_t)(uint32 timer_id, void *cookie)

timer callback function

Provided the the SIL code registering the timer. It is expected to process the timer expired event.

Param timer_id

timer identifier

Param cookie

context pointer, such as a session control block, passed to agt_timer_set function (may be NULL)

Return

0 normal exit

-1 error exit, delete timer upon return

Timer Access Functions

A SIL timer can be set up as a periodic timer or a one-time event.

The timer interval (in seconds) and the SIL timer callback function are provided when the timer is created. A timer can also be restarted if it is running, and the time interval can be changed as well.

The following table highlights the SIL timer access functions in agt/agt_timer.h:

SIL Timer Access Functions

agt_timer_create

Create a SIL timer.

agt_timer_restart

Restart a SIL timer

agt_timer_delete

Delete a SIL timer

status_t agt_timer_create(uint32 seconds, boolean is_periodic, agt_timer_fn_t timer_fn, void *cookie, uint32 *ret_timer_id)

Malloc and start a new timer control block.

Main User API - FOr SIL only at this time

Parameters
  • seconds -- number of seconds to wait between polls

  • is_periodic --

    TRUE if periodic timer

    FALSE if a 1-event timer

  • timer_fn -- address of callback function to invoke when the timer poll event occurs

  • cookie -- address of user cookie to pass to the timer_fn ret_timer_id address of return timer ID

  • ret_timer_id -- address of return timer ID

Return values

*ret_timer_id -- timer ID for the allocated timer, if the return value is NO_ERR

Returns

NO_ERR if all okay, the minimum spare requests will be malloced

status_t agt_timer_restart(uint32 timer_id, uint32 seconds)

Restart a timer with a new timeout value.

If this is a periodic timer, then the interval will be changed to the new value. Otherwise a 1-shot timer will just be reset to the new value

Parameters
  • timer_id -- timer ID to reset

  • seconds -- new timeout value

Returns

status, NO_ERR if all okay,

void agt_timer_delete(uint32 timer_id)

Remove and delete a timer control block.

Periodic timers need to be deleted to be stopped 1-shot timers will be deleted automatically after they expire and the callback is invoked

Parameters

timer_id -- timer ID to destroy

Example Timer Callback Function

The following example from toaster.c simply completes the toast when the timer expires and calls the auto-generated 'toastDone' send notification function:

/********************************************************************
* FUNCTION toaster_timer_fn
*
* Added timeout function for toaster function
*
* INPUTS:
*     see agt/agt_timer.h
*
* RETURNS:
*     0 for OK; -1 to kill periodic timer
********************************************************************/
static int
    toaster_timer_fn (uint32 timer_id,
                      void *cookie)
{
    (void)timer_id;
    (void)cookie;

    /* toast is finished */
    toaster_toasting = FALSE;
    toaster_timer_id = 0;
    if (LOGDEBUG2) {
        log_debug2("\ntoast is finished");
    }
    y_toaster_toastDone_send((const xmlChar *)"done");
    return 0;

} /* toaster_timer_fn */

Session Hook Callback

The Session Hook callback function is a user callback that is invoked when a session starts and ends.

This callback is invoked before any data structures and components have been cleaned up and their data destroyed. It can be used to perform session end or start tasks that are not related to a specific YANG module.

  • Max Callbacks: No limit (except available heap memory)

  • File: agt/agt\_cb.h

The following function template definition is used for Session Hook callback functions:

typedef void (*agt_cb_session_hook_t)(ncx_ses_event_t ses_event, const ses_cb_t *scb)

Typedef of the agt_cb_session_hook_t callback.

The Session Hook callback is invoked when a session starts and ends. Use ses.h macros like SES_MY_USERNAME(scb) to get data like client username. Max Callbacks: No limit (except available heap memory)

Param ses_event

session event type

Param scb

session control block for the session event

Session Hook Callback Initialization and Cleanup

The 'agt_cb_session_hook_register' function is used to declare the Session Hook callback. The registration can be done during the Initialization Phase, before or after the running configuration has been loaded from the startup file.

status_t agt_cb_session_hook_register(agt_cb_session_hook_t cbfn)

Register a Session Hook callback.

Max Callbacks: No limit (except available heap memory)

Parameters

cbfn -- address of callback function to use

Returns

the status of the operation.

Initialization function with the Session Hook callback registration may look as follows:

 status_t
     y_yumaworks_server_init (const xmlChar *modname,
                              const xmlChar *revision)
 {
     status_t res = NO_ERR;

     // ... load module, etc.

     /* Register a Session Hook callback */
     res = agt_cb_session_hook_register(session_hook_callback);

     return res;
 }

The 'agt_cb_session_hook_unregister' function is used to cleanup this callback.

void agt_cb_session_hook_unregister(agt_cb_session_hook_t cbfn)

Unregister a Session Hook callback.

This function unregisters a Session Hook callback.

Parameters

cbfn -- address of callback function to use

The following example code illustrates how the Session Hook callback can be cleaned up.

 void y_yumaworks_server_cleanup (void)
 {
     // ...

     /* Unregister a Session Hook callback. */
     agt_cb_session_hook_unregister(session_hook_callback);

     // ...
 }

Session Hook Callback Function Example

In the following example we are going to register Session Hook callbacks. In the callback function we are going to check if a session that is getting started is a RESTCONF session and the input encoding is XML and then will generate specific logging information for the current session. Refer to the attached YANG module and its SIL code for a complete example.

/********************************************************************
* FUNCTION session_hook_callback
*
* The Session Hook callback is invoked when a session starts
* and ends.
*
* INPUTS:
*    ses_event == session event type
*    scb == session control block for the session event
*
* RETURNS:
*   status
********************************************************************/
static void
    session_hook_callback (ncx_ses_event_t ses_event,
                           const ses_cb_t *scb)
{
    if (LOGDEBUG) {
        log_debug("\n\nEnter Session Hook callback for %s@%s",
                 SES_MY_USERNAME(scb) ? SES_MY_USERNAME(scb) : NCX_EL_NOBODY,
                 SES_PEERADDR(scb) ? SES_PEERADDR(scb) : NCX_EL_LOCALHOST);
    }

    /* Perform specific tasks when the session is ended or started
     * and only if the current session protocol is RESTCONF
     * and the session has an input that is encoded in XML
     */
    ncx_display_mode_t input_mode = SES_IN_ENCODING(scb);
    if (input_mode == NCX_DISPLAY_MODE_XML) {

        if (SES_PROTOCOL(scb) == NCX_PROTO_RESTCONF) {
            if (ses_event == NCX_SES_EVENT_START) {

                if (LOGDEBUG) {
                    log_debug("\n ++++ Session Start ++++");
                }
            } else if (ses_event == NCX_SES_EVENT_END) {

                if (LOGDEBUG) {
                    log_debug("\n ---- Session END ----");
                }
            }
        }
    }

    return;

}  /* session_hook_callback */

Shutdown Callback

The Shutdown callback function is a user callback that is invoked when the server is about to restart or shutdown.

This callback is invoked before any data structures and components have been cleaned up and their data destroyed. It can be used to perform on-exit tasks that are not related to a specific YANG module.

  • Max Callbacks: No limit (except available heap memory)

The following function template definition is used for Shutdown callback functions:

typedef void (*agt_cb_shutdown_t)(void)

Typedef of the agt_cb_shutdown_t callback.

The Shutdown callback is the user/system callback that is invoked at the start of a normal agt_cleanup A normal shutdown or restart is in progress. This callback is not invoked if the server crashes.

Max Callbacks: No limit (except available heap memory)

Shutdown Callback Initialization and Cleanup

The 'agt_cb_shutdown_register' function is used to declare the Shutdown callback. The registration can be done during the Initialization Phase 2, before or after the running configuration has been loaded from the startup file.

status_t agt_cb_shutdown_register(agt_cb_shutdown_t cbfn)

Register a Shutdown callback.

Max Callbacks: No limit (except available heap memory)

Parameters

cbfn -- address of callback function to use

Returns

the status of the operation.

Example Initialization function with the Shutdown callback registration:

 static status_t server_init (void)
 {
     // ..

     res = agt_cb_shutdown_register(server_shutdown_cb);

     // ..
 }

There is no cleanup function for this callback. The cleanup will be done automatically by the server.

Shutdown Callback Function Example

The following example code shows a Shutdown callback:

static void server_shutdown_cb (void)
{

     // do some platform-specific cleanup

}

Command Complete Callback

The Command Complete callback function is a user callback that is invoked when the server has completed an RPC operation and the response has been generated to the client (may not have been sent yet). This callback can be used instead of a command-specific Post-RPC-Reply callback function.

  • Max Callbacks: No limit (except available heap memory)

The following function template definition is used for Command Complete callback functions:

typedef void (*agt_cb_command_complete_t)(ses_cb_t *scb, rpc_msg_t *msg, const xmlChar *command_modname, const xmlChar *command_name)

Typedef of the agt_command_complete_cb_t callback.

The Command Complete callback is the user/system callback that is invoked after each client command is executed for the NETCONF or RESTCONF protocols.

The Command Complete is typically used for retrieval operations (get, get-config, get-bulk) to release resources used during GET2 callbacks invoked during the operation.

Max Callbacks: No limit (except available heap memory)

Param scb

session control block making the request

Param msg

incoming rpc_msg_t in progress

Param command_modname

YANG module name of command that is completed

Param command_name

YANG RPC object name of command that is completed

Command Complete Callback Initialization and Cleanup

The 'agt_cb_command_complete_register' function is used to declare the Command Complete callback. The registration can be done during the Initialization Phase 2, before or after the running configuration has been loaded from the startup file.

status_t agt_cb_command_complete_register(agt_cb_command_complete_t cbfn)

Register a Command Complete callback.

Max Callbacks: No limit (except available heap memory)

Parameters

cbfn -- address of callback function to use

Returns

the status of the operation.

Example Initialization function for the Command Complete callback:

 static status_t my_init (void)
 {
     status_t res = NO_ERR;

     // … load module, etc.

     res = agt_cb_command_complete_register(example_command_complete_cbfn);

     // ...
 }

The 'agt_cb_command_complete_unregister' function can be used to remove this callback. This is optional. The server will cleanup automatically on shutdown.

void agt_cb_command_complete_unregister(agt_cb_command_complete_t cbfn)

Unregister a Command Complete callback.

This function unregisters a Command Complete callback.

Parameters

cbfn -- address of callback function to use

Example Cleanup function for the Command Complete callback:

 static void my_cleanup (void)
 {
     // ...

     agt_cb_command_complete_unregister(example_command_complete_cbfn);

     // ...
 }

Command Complete Callback Function Example

The following example code shows a Command Complete callback:

/*******************************************************************
* FUNCTION  example_command_complete_cbfn
*
* The Command Complete callback is the user/system callback
* that is invoked after each client command is executed
* for the NETCONF or RESTCONF protocols.
*
* The Command Complete is typically used for retrieval
* operations (get, get-config, get-bulk) to release resources
* used during GET2 callbacks invoked during the operation.
*
* Max Callbacks: No limit (except available heap memory)
*
* INPUTS:
*   scb == session control block making the request
*   msg == incoming rpc_msg_t in progress
*   command_modname == YANG module name of command that is completed
*   command_name == YANG RPC object name of command that is completed
*
* OUTPUTS:
*    none
*
* RETURNS:
*    none
*******************************************************************/
static void
    example_command_complete_cbfn (ses_cb_t *scb,
                                   rpc_msg_t *msg,
                                   const xmlChar *command_modname,
                                   const xmlChar *command_name)
{
    (void)command_modname;

    if (!xml_strcmp(command_name, (const xmlChar *)"get") ||
        !xml_strcmp(command_name, (const xmlChar *)"get-config") ||
        !xml_strcmp(command_name, (const xmlChar *)"get-bulk")) {

        /* cleanup our internal get cache data
         * example_clean_get2_cache(scb, msg);
         */
    }

}

NACM External Groups Callback

The NACM External Groups callback function is a user callback that is invoked when the server creates a new client session for NETCONF or RESTCONF sessions. It is used to retrieve a list of group names that should be used for the specified username, just for that session. These group names are added to any NACM configured groups in the ietf-netconf-acm YANG module.

  • Max Callbacks: 1

  • File: agt/agt_acm_ietf.h

The following function template definition is used for NACM External Groups callback functions:

typedef status_t (*agt_acm_group_cbfn_t)(const xmlChar *username, xmlChar **retgroups)

typedef for NACM External Groups callback function

Get the list of group names for this username These groups are added to the usergroup cache for the session

Param username

return the list of group names that this username is a member

Param retgroups

address of return malloced string

Retval *retgroups

is set to a malloced string that will be parsed.

It contains a whitespace delimited list of group named ' group1 group2 group3'

The caller will free this string with m__free

Return

status: if an error occurs the session will only use NACM groups

NACM External Groups Callback Initialization and Cleanup

The 'agt_acm_ietf_register_group_cbfn' function is used to declare the NACM External Group callback. The registration can be done during the Initialization Phase 2, before or after the running configuration has been loaded from the startup file.

void agt_acm_ietf_register_group_cbfn(agt_acm_group_cbfn_t cbfn)

Register a get-external-groups callback function.

This will be invoked at the start of each session as the acm_cache is created for a session

Parameters

cbfn -- callnack function to register

Example Initialization function for the NACM External Group callback:

 static status_t my_init (void)
 {
     status_t res = NO_ERR;

     // … load module, etc.

     /* example -- Register a NACM External Groups Callback */
     agt_acm_ietf_register_group_cbfn(nacm_external_group_cbfn);

     return res;
 }

There is no unregister function for this callback. The server will cleanup automatically on shutdown.

NACM External Groups Callback Function Example

The following example code shows a NACM External Group callback:

/********************************************************************
 * FUNCTION nacm_external_group_cbfn
 *
 * Get the list of group names for this username
 * These groups are added to the usergroup cache for the session
 * INPUTS:
 *   username: return the list of group names that this username
 *             is a member
 *   retgroups == address of return malloced string
 * OUTPUTS:
 *   *retgroups is set to a malloced string that will be parsed.
 *         It contains a whitespace delimited list of group named
 *            ' group1 group2 group3'
 *         The caller will free this string with m__free
 * RETURNS:
 *   status: if an error occurs the session will only use NACM groups
*********************************************************************/
static status_t
    nacm_external_group_cbfn (const xmlChar *username,
                              xmlChar **retgroups)
{
    if (retgroups == NULL) {
        return ERR_NCX_INVALID_VALUE;
    }

    (void)username;

    /* MUST use a function that allocates memory with m__getMem
     * Will be freed by the caller with m__free macro
     */
    *retgroups = xml_strdup("group1 group2 group5");
    if (*retgroups == NULL) {
        return ERR_INTERNAL_MEM;
    }

    return NO_ERR;
}

User Defined Data Types Callbacks

User defined data types are supported with callback functions that apply to all YANG leaf or leaf-list nodes using a specific data type.

There are 3 types of callbacks supported to allow customization of YANG data types:

  • validate: custom validation function, used when a data node is set

  • canonical: convert a value to canonical format, used when a data node is set

  • compare: compare 2 values of the same data type, used when a data node is set

There are several standard YANG data types that are supported with built-in canonical functions, which can be found in ncx/ipaddr_typ.c (if sources are available). The server will automatically check for validate and canonical callbacks when a data node is parsed from XML or JSON input. The automatic canonical check can be disabled using the --with-canonical CLI parameter.

User Defined Data Types Callback Initialization and Cleanup

A user defined type is registered with the 'typ_userdef_register' function.

  • This must be done after the YANG module containing the typedef statement has been loaded.

  • Only top-level typedefs are supported.

  • Typedefs within groupings or nested within containers or lists are not supported.

status_t typ_userdef_register(const xmlChar *modname, const xmlChar *typname, typ_validate_fn_t validate_fn, typ_canonical_fn_t canonical_fn, typ_compare_fn_t compare_fn, void *cookie)

Register the callback functions for a user defined type.

At least one callback needs to be set in order for this userdef type to have any affect

Parameters
  • modname -- module name defining the type

  • typname -- name of the type

  • validate_fn -- validate callback (may be NULL)

  • canonical_fn -- canonical callback (may be NULL)

  • compare_fn -- compare callback (may be NULL)

  • cookie -- cookie to pass to callback function

Returns

status

Example Registration function:

/* ietf-inet-types:ipv6-address */
status_t res =
    typ_userdef_register(NCXMOD_IETF_INET_TYPES,
                         (const xmlChar *)"ipv6-address",
                         NULL,   // validate_fn
                         ipv6_address_canonical_fn,
                         NULL,   // compare_fn
                         NULL);  // cookie
if (res != NO_ERR) {
    return res;
}

typ_validate_fn_t

The validate callback is used for special custom validation of values conforming to a YANG data type.

typedef status_t (*typ_validate_fn_t)(typ_def_t *typdef, val_value_t *val, void *cookie)

userdef validate callback function user validation callback for a userdef type

Param typdef

type definition for the user defined type

Param inval

input value to convert

Param cookie

cookie value passed to register function

Return

the validation status

This function returns NO_ERR even if it does not do any validation.

It returns an error code if validation fails.

Example Validate Callback function:

/********************************************************************
* FUNCTION admin_validate_fn
*
* validate callback for an administrative string
*
* INPUTS:
*   typdef == type definition for the user defined type
*   val == input value to validate
*   cookie == cookie value passed to register function
* RETURNS:
*   status
*/
static status_t
    admin_validate_fn (typ_def_t *typdef,
                       val_value_t *val,
                       void *cookie)
{
    (void)typdef;
    (void)cookie;

    if (VAL_TYPE(val) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    const xmlChar *valstr = val->v.str;
    if (valstr == NULL) {
        return NO_ERR;
    }

    /* check if the string passes admin format checks */
    if (check_admin_string_ok(valstr)) {
       return NO_ERR;
    }
    return ERR_NCX_INVALID_VALUE;


} /* admin_validate_fn */

typ_canonical_fn_t

The canonical callback is used to convert a data node value to the canonical format for the data type. This is important for list key leafs, or else different representations of the same value (E.g. ipv6-address) will be treated as separate list entries.

This function returns NO_ERR even if it does not do any conversion to canonical format.

It returns an error code if some error occurs and conversion to canonical format fails.

typedef status_t (*typ_canonical_fn_t)(typ_def_t *typdef, val_value_t *val, void *cookie)

userdef canonical callback function convert the inval to the canonical format for the type

Param typdef

type definition for the user defined type

Param val

input value to convert

Param cookie

cookie value passed to register function

Retval val

can be converted to canonical format

Return

status

Example Canonical Callback function:

/********************************************************************
* FUNCTION lowercase_canonical_fn
*
* <generic convert to lowercase>
* canonical callback for a domain name string
* convert the inval to the canonical format for the type
*
* INPUTS:
*   typdef == type definition for the user defined type
*   val == input value to convert
*   cookie == cookie value passed to register function
* OUTPUTS:
*   val == can be changed to canonical format
* RETURNS:
*   status
*/
static status_t
    lowercase_canonical_fn (typ_def_t *typdef,
                            val_value_t *val,
                            void *cookie)
{
    (void)typdef;
    (void)cookie;

    if (VAL_TYPE(val) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    xmlChar *valstr = VAL_STRING(val);
    if (valstr == NULL) {
        return NO_ERR;
    }

    /* convert the string to lowercase in place */
    xmlChar *p = valstr;
    while (*p) {
        *p = (xmlChar)tolower((int)*p);
    }

    return NO_ERR;

} /* lowercase_canonical_fn */

typ_compare_fn_t

The compare callback is used to compare two values of the same data type. It is used in the val_compare functions. The standard compare function will skipped if this callback is present for the data type. This function is only needed if the standard compare function for the data type is not correct for some reason (E.g: a vendor-specific structured string).

This function returns NO_ERR if the comparison is done and no need to do the standard compare.

This function returns ERR_NCX_SKIPPED if the comparison is not done so the standard compare function needs to be invoked.

This function returns some error code if an error occurs while attempting the comparison (E.g. ERR_INTERNAL_MEM if a malloc fails)

typedef status_t (*typ_compare_fn_t)(const typ_def_t *typdef, const val_value_t *val1, const val_value_t *val2, void *cookie, int *retval)

userdef compare callback function compare 2 val_value_t nodes of the same user defined type

Param typdef

type definition for the user defined type

Param val1

input value 1 to comapre

Param val2

input value 2 to comapre

Param cookie

cookie value passed to register function

Param retval

address of return compare value

Retval *retval

return compare value

Return

status (ERR_NCX_SKIPPED if no compare done)

Example Compare Callback function:

/********************************************************************
 * compare 2 val_value_t nodes of the same user defined type
 * INPUTS:
 *   typdef == type definition for the user defined type
 *   val1 == input value 1 to compare
 *   val2 == input value 2 to compare
 *   cookie == cookie value passed to register function
 *   retval == address of return compare value
 * OUTPUTS:
 *   *retval == return compare value
 * RETURNS:
 *   status (ERR_NCX_SKIPPED if no compare done)
 */
static status_t
    admin_compare_fn (typ_def_t *typdef,
                      const val_value_t *val1,
                      const val_value_t *val2,
                      void *cookie,
                      int *retval)
{
    (void)typdef;
    (void)cookie;

    if (VAL_TYPE(val1) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    if (VAL_TYPE(val12) != NCX_BT_STRING) {
        return ERR_NCX_OPERATION_FAILED;
    }

    const xmlChar *val1str = VAL_STRING(val1);
    if (val1str == NULL) {
        return ERR_NCX_SKIPPED;
    }

    const xmlChar *val2str = VAL_STRING(val2);
    if (val2str == NULL) {
        return ERR_NCX_SKIPPED;
    }

    /* compare as case-insensitive strings */
    int ret = strcasecmp(const char *)val1str, (const char *)val2str);
    *retval = ret;
    return NO_ERR;

} /* admin_compare_fn */

agt_profile_t Struct

The following structure is used to represent the 'agt_profile':

struct agt_profile_t

Multiple instances are not supported.

The yp-server library has an API to override the default profile settings.

hardwire some of the server profile parameters because they are needed before the NCX engine is running They cannot be changed after boot-time. This data structure is sent from the main server to each subsystem, when the YControl initialization is done

!!! Keep the profile data in this struct in synch with !!! the agt_profile container in yumaworks-agt-profile.yang

the order of the fields below must exactly match the names and bit number assignments in the follwing files:

  • agt/agt_sil_profile.c

  • sil-sa/sil_sa_profile.c Both files must be changed together to keep the bit numbers aligned

Public Members

ncx_agttarg_t agt_targ

BEGIN DATA SENT TO SUBSYSTEMS; target type (candidate or running)

ncx_agtstart_t agt_start

startup type enum (distinct or mirror)

log_debug_t agt_log_level

requested log-level

log_debug_t agt_syslog_log_level

requested SYSLOG log-level

log_debug_t agt_pthread_log_level

requested PTHREADS log level

boolean agt_session_sync_mutex

session sync mutex flag

boolean agt_log_acm_reads

log NACM read requests

boolean agt_log_acm_writes

log NACM write requests

boolean agt_validate_all

validate all flag

boolean agt_has_startup

has startup flag for with-startup

boolean agt_usestartup

track no-startup flag

boolean agt_factorystartup

track factory-startup flag

boolean agt_startup_error

startup-error parameter: stop, continue enums only

boolean agt_running_error

running-error parameter: stop, continue enums only

boolean agt_logappend

log-append parameter

boolean agt_xmlorder

use strict XML order flag

boolean agt_list_deleteall_ok

allow delete-all and remove-all operations on a list

boolean agt_leaflist_deleteall_ok

allow delete-all and remove-all operations on a leaf-list

boolean agt_stream_output

stream message output: d:true; no CLI support yet

boolean agt_delete_empty_npcontainers

delete empty config=true NP containers: d: false

boolean agt_notif_sequence_id

d: false

boolean agt_yuma_system_notifs

d: false

boolean agt_ietf_system_notifs

d: true

boolean agt_yumaworks_system

d: true

boolean agt_yumaworks_templates

d: true

boolean agt_support_save

d: true (needs WITH_SUPPORT_SAVE=1)

boolean agt_term_msg

d: true for <term-msg> notification

boolean agt_alt_names

Yuma REST-API urlselect extra parameters.

boolean agt_wildcards

Allow wildcards in filters.

ncx_name_match_t agt_match_names

match names behavior; needs to be EXACT for YANG

agt_transaction_model_t agt_transaction_model

TBD private transaction model.

const xmlChar *agt_accesscontrol

access control model

const xmlChar *agt_conffile

config file: default: /etc/yumapro/netconfd-pro.conf

const xmlChar *agt_confdir

config dir: default: /etc/yumapro/netconfd-pro.d

const xmlChar *agt_logfile

main logfile

const xmlChar *agt_startup

startup filespec

const xmlChar *agt_startup_factory_file

factory startup filespec

const xmlChar *agt_defaultStyle

default-style for with-defaults

const xmlChar *agt_extern_libspec

superuser name (allowed to be NULL) moved to agt.c agt_superuserQ Q of ncx_backptr_t to malloced string from CLI parameters

extern library spec

const xmlChar *agt_backup_dir

backup dir location

const xmlChar *agt_server_id

assigned server ID

uint32 agt_eventlog_size

eventlog-size set to 0 to disable replay

uint32 agt_maxburst

maxburst parameter

uint32 agt_hello_timeout

hello-timeout parameter

uint32 agt_idle_timeout

idle-timeout parameter

uint32 agt_linesize

line size for logging

int32 agt_indent

indent parameter

int32 agt_message_indent

message-indent parameter

boolean agt_usevalidate

with-validate parameter

boolean agt_useurl

with-url parameter

boolean agt_use_ccommit

enable confirmed-comiit

boolean agt_use_yangapi

enable YANG-API protocol (obsolete: ignored)

boolean agt_use_restconf

enable RESTCONF protocol

boolean agt_use_cli

enable yp-shell

boolean agt_use_netconf

enable NETCONF protocol

boolean agt_use_local_transport

use local transport flag

boolean agt_use_notifications

enable notifications

boolean agt_system_sorted

enable system-sorted (obsolete: sorted by val_child algorithms)

boolean agt_lax_namespaces

lax namespace usage: will try to match element name

agt_acm_model_t agt_acm_model

NACM or external ACM model.

ncx_withdefaults_t agt_defaultStyleEnum

default with-defaults enum

agt_acmode_t agt_accesscontrol_enum

access control enum

uint16 agt_max_sessions

max sessions (total)

uint16 agt_max_cli_sessions

max CLI sessions

uint16 agt_subsys_timeout

subsystem timeout in seconds

uint16 agt_ports[AGT_MAX_PORTS]

these port numbers are for the NETCONF-over-SSH protocol

uint16 agt_coap_port

the CoAP port number used only if WITH_COAP and agt_use_coap set

uint16 agt_coap_dtls_port

Coap over DTLS port (not implemented)

const xmlChar *agt_coap_address

Coap over DTLS address (not implemented)

const xmlChar *agt_yangapi_server_url

YANG-API server URL (obsolete)

const xmlChar *agt_restconf_server_url

RESTCONF server URL.

boolean agt_use_yuma_proc

load yuma-proc module

boolean agt_use_yuma_arp

load yuma-arp module

boolean agt_use_yuma_if

load yuma-if module

boolean agt_use_yuma_mysession

load yuma-mysession module

boolean agt_use_yumaworks_event_filter

load yumaworks-event-filter module

boolean agt_use_yuma_system

load yuma-system module

boolean agt_use_rollback_on_error

Use rollback-on-error.

All datastore transactions are all-or-none so this parameter is essentially ignored

boolean agt_use_ycontrol

enable YControl protocol

boolean agt_sil_skip_load

skip the SIL callbacks for load-config Useful if the authoritative database is really the external system.

All edits coming from DB-API or loaded from the startup-cfg.xml are already applied to the system via the other database

boolean agt_log_event_drops

log event drops

boolean agt_sil_missing_error

treat missing SIL libraries as an error, not warning

boolean agt_sil_skip_keys

do not invoke the SIL callback for a key leaf

boolean agt_use_rollback_failed_backup

save running config for a commit, in case the rollback fails

boolean agt_allow_tcp_socket

allow a TCP socket instead of an AF_LOCAL socket for connections

boolean agt_use_tcp_socket

use a TCP socket instead of an AF_LOCAL socket for connections

const xmlChar *agt_subsys_info_file

location of subsys info file

boolean agt_save_owners

flag to save owner strings in the database

boolean agt_json_leaf_list_1line

flag to print JSON leaf-list value on 1 line

boolean agt_save_config_system

flag to skip generation of the startup XML file when a save_config is done by the server.

Used with the external config mode where the external system database is already up to date so the XML file is not used Default is false. Set in yp_system_init_profile.

uint8 agt_withdef_enabled

bitmask of the with-defaults enumerations that should be enabled in the server

uint32 agt_watcher_interval

Specifies the number of seconds the YPWatcher process will sleep before checking if the netconfd-pro process has died.

Ignored if no-watcher CLI parameter was used

boolean agt_audit_log_candidate

If true, then transactions to the candidate datastore will be recorded in the audit log.

If false, then transactions to the candidate datastore will not be recorded in the audit log.";

boolean agt_sil_validate_candidate

If true, the server will invoke the VALIDATE phase for SIL and SIL-SA callbacks when each edit is made to the candidate datastore.

boolean agt_restconf_strict_headers

If 'true' the server will only accept requests with normative Accept header entries specified in the draft.

boolean agt_autodelete_pdu_error

If 'true' the server will treat edit-config auto-delete because of false when-stmts as an error instead of silent delete.

uint32 agt_sil_getbulk_max

Specifies the maximum number of getbulk entries to request from a GET2 callback.

This value will be used in the get2cb 'max_entries' field. The value 0 is used to indicate there is no max and the GET2 callback can return as many getbulk entries as desired. This is the default for leaf-list GET2 callbacks

const xmlChar *agt_crypt_hash_prefix

specifies the string that will be pre-pended to the password before calling crypt_r to generate the hash for the crypt-hash leaf passed with $0$cleartext

uint8 agt_min_passwd_len

minimum password length when setting crypt-hash variables

boolean agt_with_netconf

If 'true' then the corresponding protocol will be enabled.

Otherwise, the protocol will not be enabled. The incoming connection will be droped if the protocol is disabled. NETCONF is usually enabled

boolean agt_with_restconf

enable RESTCONF sessions

boolean agt_with_yang_api

enable YANG-API sessions (obsolete)

boolean agt_with_yp_shell

enable yp-shell sessions

boolean agt_with_yp_coap

enable YP-CoAP sessions (not supported)

boolean agt_with_yp_coap_dtls

enable YP-CoAP DTLS sessions (not supported)

boolean agt_with_netconf_tls

enable NETCONF over TLS sessions

log_debug_t agt_audit_log_console_level

log console level

log_debug_t agt_audit_log_level

audit log level

boolean agt_ha_enabled

enable YP-HA

boolean agt_ha_sil_standby

call SIL code in standby mode

uint16 agt_ha_port

port number is default 8088

const xmlChar *agt_ha_server_key

server-key to keep HA pools separate

const xmlChar *agt_ha_initial_active

active server to use if standby

boolean agt_simple_json_names

use simple JSON names instead of YANG JSON names

boolean agt_create_empty_npcontainers

LAST DATA SENT TO SUBSYSTEMS create empty NP containers d: true.

boolean agt_with_warnings

this field indicates if agt_record_warning will be allowed to set the error-severity field to warning

boolean agt_library_mode

this field indicates the server is operating in library mode It will look for YANG modules but only load them into its library.

boolean agt_with_config_id

this field indicates if the :config-id capability is enabled or not.

This is an enterprise URI and at least 1 opensource tool complains it is not a valid YANG module URI

boolean agt_no_nvstore

this field indicates that the server should not load or save using the normal APIs during transaction management.

The 'start' choice will be ignored (e.g., no-startup)) and the server will not attempt to load a startup-cfg.xml file. Transactions will not be saved to NV-storage at all. Any external NV-storage callbacks will be ignored.

Use this mode if NV-load and NV-storage are handled internally and not via the startup-cfg.xml file.

boolean agt_with_yang11_hello

this field indicates whether the NETCONF hello message should conform to the standard and leave out YANG 1.1 modules.

boolean agt_with_callhome

this field indicates that the IETF Callhome feature is enabled if true and WITH_CALLHOME is built into the image, then the server will attempt to connect to the callhome client servers specified in the callhome config (ietf-server module TBD)

uint16 agt_callhome_retry_interval

this field specifies the number of seconds to wait after a connect attempt to the callhome server has failed.

uint16 agt_callhome_retry_max

this field specifies the number of retry attempts the server should attempt to the callhome server before giving up.

The value 0 indicates the server should never give up.

const xmlChar *agt_sshd_path

set the sshd exectuable path for callhome default is /usr/sbin/sshd only set by vendor in agt_init1 phase

const xmlChar *agt_subsys_path

set the netconf subsystem for sshd exectuable path for callhome default is /usr/sbin/netconf-subsystem-pro only set by vendor in agt_init1 phase

const xmlChar *agt_sshd_config

set the sshd_config file to use for sshd exectuable for callhome default is $HOME/.yumapro/ch_sshd_config.

<ch-server-name> only set by vendor in agt_init1 phase

boolean agt_with_ocpattern

this flag enables/disables the special OpenConfig usage of the YANG pattern-statement.

If true then modules named openconfig-* will be checked as POSIX patterns, not YANG XSD patterns

boolean agt_fileloc_fhs

this flag enables FHS file locations for server data files

boolean agt_no_audit_log

this flag indicates the no-audit-log CLI parameter

ncx_msg_encoding_t agt_restconf_default_encoding

this enum indicates the restconf-default-encoding CLI parameter

boolean agt_startup_fallback

this flag indicates the startup-error parm is set to fallback

boolean agt_running_fallback

this flag indicates the running-error parm is set to fallback

boolean agt_with_snmp

this flag indicates that the snmp agent should be enabled

ncx_snmp_agt_role_t agt_snmp_agent_role

identify the SNMP agent native mode master|subagent

uint16 agt_snmp_subagent_priority

identify the SNMP subagent priority, what priority will be used for OID callbacks registration

boolean agt_useurl_tftp

the libcurl variables

with-url-tftp

boolean agt_useurl_ftp

with-url-ftp

boolean agt_sil_delete_children_first

sil-delete-children-first CLI parameter

boolean agt_trim_whitespace

trim-whitespace CLI parameter

const xmlChar *agt_netconf_tls_address

netconf-tls-address parameter

const xmlChar *agt_netconf_tls_certificate

netconf-tls-cerificate parameter

const xmlChar *agt_netconf_tls_key

netconf-tls-key parameter

uint16 agt_netconf_tls_port

netconf-tls-port parameter

const xmlChar *agt_netconf_tls_trust_store

netconf-tls-trust-store parameter

boolean agt_insecure_ok

insecure-ok parameter

const xmlChar *agt_cert_default_user

cert-default-user parameter

const xmlChar *agt_errmsg_lang

errmsg-lang parameter

boolean agt_startup_prune_ok

startup-prune-ok parameter

boolean agt_startup_create_ok

no CLI parameter!! set to TRUE! change in yp-system library or agt_profile.c

boolean agt_with_canonical

with-canonical parameter

boolean agt_with_modtags

with-modtags parameter

boolean agt_sil_invoke_for_defaults

sil-invoke-for-defaults parameter

boolean agt_with_gnmi

this flag indicates that the gNMI support should be enabled

boolean agt_ypserver_mode

this flag indicates the server is rnning as yp-controller and not netconfd-pro

boolean agt_sil_prio_reverse_for_deletes

sil-prio-reverse-for-deletes parameter

uint32 agt_audit_log_events

audit-log-events to control audit log content

boolean agt_sil_root_check_first

sil-root-check-first to do root check in edit-config before the SIL validate callbacks are invoked the old (only) behavior is 'false'

boolean agt_sil_wait_sa

No CLI parameter!! Set to TRUE in agt_profile.c the load-config will wait if agt_ncx_load_any_waiting() is true and this parameter is also true.

boolean agt_with_maint_mode

allow maintenance mode to be used

boolean agt_callhome_reconnect

CLI parameter callhome-reconnect.

boolean agt_sil_test_get_when

enable config=false when-stmt checking for GET1 and GET2 callback functions; if false then the callback is expected to check the when-stmt itself and return ERR_NCX_NO_INSTANCE if the when-stmts for the node are false; default is true

boolean agt_yuma_time_filter

use the yuma-time-filter module

boolean agt_yumaworks_getbulk

use the yumaworks-getbulk module

boolean agt_yumaworks_ids

use the yumaworks-ids module

boolean agt_use_db_lock

db-lock used only if WITH_YCONTROL=1 and CLI set

int32 agt_max_strlen

max-strlen parameter

boolean agt_with_yumaworks_callhome

with-yumaworks-callhome

boolean agt_with_yumaworks_config_change

with-yumaworks-config-change

boolean agt_with_yumaworks_event_stream

with-yumaworks-event-stream

agt_crl_mode_t agt_crl_mode

tls-crl-mode parameter

boolean agt_crl_missing_ok

tls-crl-missing-ok parameter

boolean agt_with_nmda

with-nmda

boolean agt_startup_skip_validation

startup-skip-validation

boolean agt_cvt_subtree_filter

convert-subtree-filterparameter

boolean agt_import_version_bestmatch

import-version-bestmatch parameter

boolean agt_with_yang_patch_running

with-yang-patch-running parameter

uint16 agt_push_min_period

push-min-period parameter

uint16 agt_push_min_dampening

push-min-dampening parameter

uint32 agt_push_max_periodic

push-max-periodic parameter

uint32 agt_push_max_operational

push-max-operational parameter

uint32 agt_push_simop_period

push-simop-period parameter

boolean agt_push_simop_enabled

push-simop-enabled parameter

boolean agt_push_simop_patch_update

push-simop-patch-update parameter

boolean agt_wait_datastore_ready

wait-datastore-ready parameter

boolean agt_return_status

return exit code or zero

boolean agt_use_yumaworks_cert_usermap

load yumaworks-cert-usermap module

boolean agt_remove_schema_aug_leafs

this flag indicates the deprecated schema list leafs added from yumaworks-system should be removed.

The nodes are not removed from the YANG module, but the server will not send these optional leafs in any response to a client. Fixes ODL bug which rejects session if an augmenting node is found in this list

obj_testflags_t agt_rootflags

root commit descendant test flags

boolean agt_load_done

server load-config done flag

boolean agt_load_validate_errors

server load-config had errors flag

boolean agt_load_rootcheck_errors

server load-config root-check had errors flag

boolean agt_load_top_rootcheck_errors

server load-config had top root-check errors flag

boolean agt_load_apply_errors

server load-config had apply phase errors flag

boolean agt_load_factory_fallback

server load-config allowed to use factory-fallback flag

boolean agt_load_def_startup_factory

server load-config factory flag

dlq_hdr_t agt_savedevQ

Q of malloced ncx_save_deviations_t.

dlq_hdr_t agt_commit_testQ

Q of malloced agt_commit_test_t.

xmlChar *agt_startup_txid_file

cached location of startup transaction ID file

xmlChar *agt_socket_address

strdup of socket-address CLI parameter

uint16 agt_socket_port

listen on this TCP port if TCP socket is enabled

xmlChar *agt_conf_dirspec

malloced string indicating the confdir pathspec in use

boolean agt_confdir_skipped
agt_ha_role_t agt_ha_role

saved HA role

boolean agt_defer_load

saved defer load config flag

boolean agt_with_grpc

this flag indicates that the gRPC support should be enabled

Datastore Fields

Field Name

CLI Override

Default

agt_autodelete_pdu_error

--autodelete-pdu-error

true

agt_create_empty_npcontainers

--create-empty-npcontainers

true

agt_backup_dir

none

$HOME/.yumapro/backups

agt_defaultStyle

--default-style

explicit

agt_defaultStyleEnum

--default-style

NCX_WITHDEF_EXPLICIT

agt_no_nvstore

--no-nvstore

not present

agt_running_error

--running-error

false

agt_save_config_system

none

false

agt_sil_validate_candidate

--sil-validate-candidate

true

agt_sil_skip_load

--sil-skip-load

false

agt_start

--with-startup

NCX_AGT_START_MIRROR

agt_startup_error

--startup-error

false

agt_system_sorted

--system-sorted

true

agt_targ

--target

NCX_AGT_TARG_CANDIDATE

Logging Fields

Field Name

CLI Override

Default

agt_audit_log_candidate

:ref:` --audit-log-candidate`

true

agt_audit_log_console_level

--audit-log-console-level

LOG_DEBUG_DEBUG

agt_audit_log_level

--audit-log-level

LOG_DEBUG_INFO

agt_logfile

--log

none

agt_logappend

--log-append

none

agt_log_acm_reads

none

false

agt_log_acm_writes

none

true

agt_log_level

--log-level

LOG_DEBUG_INFO

agt_pthread_log_level

--log-pthread-level

LOG_DEBUG_INFO

agt_syslog_log_level

--log-syslog-level

LOG_DEBUG_INFO

Notification Fields

Field Name

CLI Override

Default

agt_eventlog_size

--eventlog-size

1000

agt_maxburst

--max-burst

10

agt_notif_sequence_id

none

false

agt_ietf_system_notifs

--system-notifications

true

agt_use_notifications

--with-notifications

true

agt_yuma_system_notifs

--system-notifications

false

agt_log_event_drops

--log-event-drops

false

Access Control Fields

Field Name

CLI Override

Default

agt_accesscontrol

--access-control

enforcing

agt_acm_model

none

AGT_ACM_MODEL_IETF_NACM

agt_superuserQ

--superuser

none

agt_crypt_hash_prefix

none

$6$

agt_min_password_len

none

8

Protocol Capability and YANG Module Fields

Field Name

CLI Override

Default

agt_with_netconf

--with-netconf

true

agt_with_restconf

--with-restconf

true

agt_with_yang_api

--with-yang-api

false

agt_with_yp_shell

--with-yp-shell

true

agt_useurl

--with-url

true

agt_usevalidate

--with-validate

true

agt_use_ccommit

none

true

agt_use_cli

none

true if compiled WITH_CLI=1

agt_use_local_transport

none

true if compiled DEBUG=1

agt_use_netconf

none

true

agt_use_yangapi

none

true if compiled WITH_YANGAPI=1

agt_use_yuma_arp

none

true if compiled WITH_YUMA_ARP=1

agt_use_yuma_if

none

true if compiled WITH_YUMA_INTERFACES=1

agt_use_yuma_mysession

--module=yuma-mysession

false

agt_use_yuma_proc

none

true if compiled WITH_YUMA_PROC=1

agt_use_yumaworks_event_filter

--with-yumaworks-event-filter

true

agt_yumaworks_system

--with-yumaworks-system

true

agt_withdef_enabled

none

all bits enabled (15)

General Fields

Field Name

CLI Override

Default

agt_alt_names

--alt-names

true

agt_conffile

--config

/etc/yumapro/netconfd-pro.conf

agt_hello_timeout

--hello-timeout

600

agt_idle_timeout

--idle-timeout

3600

agt_indent

--indent

1

agt_lax_namespaces

none

true

agt_linesize

none

72

agt_match_names

--match-names

NCX_MATCH_EXACT

agt_max_sessions

--max-sessions

8

agt_sil_getbulk_max

--max-getbulk

10

agt_simple_json_names

--simple-json-names

false

agt_message_indent

--message-indent

-1

agt_ports

--port

830

agt_restconf_server_url

--restconf-server-url

http://localhost

agt_restconf_strict_accept

--restconf-strict-headers

false

agt_sil_missing_error

--sil-missing-error

false

agt_stream_output

none

true

agt_wildcards

--wildcard-keys

false

agt_with_config_id

--with-config-id

true

agt_with_warnings

--with-warnings

false

agt_xmlorder

--usexmlorder

false

agt_yangapi_server_url

--yangapi-server-url

http://localhost

agt_library_mode

--library-mode

false

High Availability Specific Fields

Field Name

CLI Override

Default

agt_ha_enabled

--ha-enabled

false

agt_ha_sil_standby

--ha-sil-standby

false

agt_ha_port

--socket-port

8088

agt_ha_server_key

--ha-server-key

none

agt_ha_initial_active

--ha-initial-active

none

example-system.c

The following example code can be found in the libsystem/src directory.

/*
 * Copyright (c) 2012 - 2020, YumaWorks, Inc., All Rights Reserved.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*  FILE: example-system.c

  Example External System Library

*********************************************************************
*                                                                   *
*                     I N C L U D E    F I L E S                    *
*                                                                   *
*********************************************************************/

#include <assert.h>
#include <stdlib.h>
#include <xmlstring.h>

#include "procdefs.h"
#include "agt.h"
#include "agt_acm.h"
#include "agt_acm_ietf.h"
#include "agt_acm_extern.h"
#include "agt_util.h"
#include "agt_cfg.h"
#include "agt_cb.h"
#include "agt_hook_util.h"
#include "dlq.h"
#include "example-system.h"
#include "log.h"
#include "log_vendor.h"
#include "log_vendor_extern.h"
#include "ncx.h"
#include "ncxconst.h"
#include "ncxmod.h"
#include "ncxtypes.h"
#include "obj.h"
#include "ses.h"
#include "status.h"
#include "typ_userdef.h"
#include "val.h"
#include "val_util.h"
#include "xml_util.h"


/********************************************************************
*                                                                   *
*                       C O N S T A N T S                           *
*                                                                   *
*********************************************************************/

/* this flag is used to enable the NACM external group examples */
// #define ADD_NACM_GROUP 1

/* this flag is used to enable the Session Hook callback examples */
// #define ADD_SESSION_HOOK 1

/* this flag is used to enable the canonical callback for an IPv6
 * address examples
 */
// #define ADD_IPV6_FN 1

/* this flag is used to enable the External ACM Hooks examples */
// #define ADD_ACM_EXTERN 1

/* this flag is used to enable Vendor log functions examples */
// #define ADD_VENDOR_LOG 1

/* this flag is used to enable External NV-Storage Handler Hooks examples */
// #define ADD_NV_STORE_HOOK 1

/* this flag is used to enable Transaction Management Hooks examples */
// #define ADD_TRANS_HOOK 1

/* this flag is used to enable YANG Object Template Callback examples */
// #define ADD_YANG_OBJ_TEMP 1

/* this flag is used to enable YANG Extension Handler Callback examples */
// #define ADD_YANG_EXTEN 1

/* this flag is used to enable RPC Command Complete examples */
// #define ADD_RPC_COMPLETE 1

/* this flag is used to enable the user-type callback examples */
//#define ADD_USER_TYPE 1
#define USERTYPE_MODULE (const xmlChar *)"iana-crypt-hash"
#define USERTYPE_TYPE   (const xmlChar *)"crypt-hash"

/********************************************************************
*                                                                   *
*                        F U N C T I O N S                          *
*                                                                   *
*********************************************************************/


/****************  Example Callback for an IPv6 address *******************/

#ifdef ADD_IPV6_FN
/********************************************************************
* FUNCTION my_ipv6_fn
*
* Canonical callback for an IPv6 address.
* Convert the inval to the canonical format for the type
*
* INPUTS:
*   typdef == type definition for the user defined type
*   val == input value to convert
*   cookie == cookie value passed to register function
*
* OUTPUTS:
*   val == can be changed to canonical format
*
* RETURNS:
*   status
*
*********************************************************************/
static status_t
    my_ipv6_fn (typ_def_t *typdef,
                val_value_t *val,
                void *cookie)
{
    (void)typdef;
    (void)cookie;

    log_debug("\nmy_ipv6_fn called ******************************");
    return NO_ERR;

}  /* my_ipv6_fn */

#endif   // ADD_IPV6_FN



#ifdef ADD_USER_TYPE
/********************************************************************
* FUNCTION dummy1_canonical_fn
*
* Canonical callback for a data type.
* Convert the inval to the canonical format for the type (not really)
*
* INPUTS:
*   typdef == type definition for the user defined type
*   val == input value to convert
*   cookie == cookie value passed to register function
*
* OUTPUTS:
*   val == can be changed to canonical format
*
* RETURNS:
*   status
*
*********************************************************************/
static status_t
    dummy1_canonical_fn (typ_def_t *typdef,
                         val_value_t *val,
                         void *cookie)
{
    (void)typdef;
    (void)cookie;

    log_debug("\ndummy1_canonical(%s) called *****", VAL_NAME(val));

    /* real function would change the value to canonical format */
    return NO_ERR;

}  /* dummy1_canonical_fn */


/********************************************************************
* FUNCTION dummy1_compare_fn
*
* Compare callback for a data type.
*
 * INPUTS:
 *   typdef == type definition for the user defined type
 *   val1 == input value 1 to comapre
 *   val2 == input value 2 to comapre
 *   cookie == cookie value passed to register function
 *   retval == address of return compare value
 * OUTPUTS:
 *   *retval == return compare value
 * RETURNS:
 *   status (ERR_NCX_SKIPPED if no compare done)
 */
static status_t
    dummy1_compare_fn (const typ_def_t *typdef,
                       const val_value_t *val1,
                       const val_value_t *val2,
                       void *cookie,
                       int *retval)
{
    (void)typdef;
    (void)cookie;

    log_debug("\ndummy1_compare(%s) called *****", VAL_NAME(val1));

    /* This must be a real compare function!
     * just use the default compare function
     * real function would compare the 2 values with different function
     */
    *retval = val_compare(val1, val2);

    return NO_ERR;

}  /* dummy1_compare_fn */


/********************************************************************
* FUNCTION dummy1_validate_fn
* user validation callback for a userdef type
* Invoked when the input data is checked for field validation
* such as xml_parse. Applies to RPC input and datastore nodes
* INPUTS:
*   typdef == type definition for the user defined type
*   inval == input value to convert
*   cookie == cookie value passed to register function
* RETURNS:
*   the validation status
*/
static status_t
    dummy1_validate_fn (typ_def_t *typdef,
                        val_value_t *val,
                        void *cookie)
{
    (void)typdef;
    (void)cookie;

    log_debug("\ndummy1_validate(%s) called *****", VAL_NAME(val));

    /* real function would test the value */

    return NO_ERR;

}  /* dummy1_validate_fn */

#endif   // ADD_USER_TYPE


/****************  Example External ACM Hooks *******************/

#ifdef ADD_ACM_EXTERN
/********************************************************************
* FUNCTION acm_extern_rpc
*
* Check if the specified user is allowed to invoke an RPC
*
* INPUTS:
*   msg == XML header in incoming message in progress
*   user == user name string
*   rpcobj == obj_template_t for the RPC method to check
*
* RETURNS:
*   TRUE if user allowed invoke this RPC; FALSE otherwise
*
*********************************************************************/
static boolean
    acm_extern_rpc (xml_msg_hdr_t *msg,
                    const xmlChar *user,
                    const obj_template_t *rpcobj)
{
    (void)msg;

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

    log_debug("\nChecking RPC access for user %s to operation '%s:%s'",
              user, modname, objname);

    // check access here

    log_debug("\nacm_extern_rpc: return OK\n");
    return TRUE;

} /* acm_extern_rpc */


/********************************************************************
* FUNCTION acm_extern_notif
*
* Check if the specified user is allowed to receive
* a notification event
*
* INPUTS:
*   user == user name string
*   notifobj == obj_template_t for the notification event to check
*
* RETURNS:
*   TRUE if user allowed receive this notification event;
*   FALSE otherwise
*********************************************************************/
static boolean
    acm_extern_notif (const xmlChar *user,
                      const obj_template_t *notifobj)
{
    const xmlChar *modname = obj_get_mod_name(rpcobj);
    const xmlChar *objname = obj_get_name(rpcobj);

    log_debug("\nChecking Notification access for "
              "user %s to event '%s:%s'",
              user, modname, objname);

    // check access here

    log_debug("\nacm_extern_notif: return OK\n");
    return TRUE;

} /* acm_extern_notif */


/********************************************************************
* FUNCTION acm_extern_write_fn
*
* Check if the specified user is allowed to access a value node
* The val->obj template will be checked against the val->editop
* requested access and the user's configured max-access
*
* INPUTS:
*   msg == XML header from incoming message in progress
*   newval  == val_value_t in progress to check
*                (may be NULL, if curval set)
*   curval  == val_value_t in progress to check
*                (may be NULL, if newval set)
*   val  == val_value_t in progress to check
*   editop == requested CRUD operation
*
* RETURNS:
*   TRUE if user allowed this level of access to the value node
*********************************************************************/
static boolean
    acm_extern_write (xml_msg_hdr_t *msg,
                      const xmlChar *user,
                      const val_value_t *newval,
                      const val_value_t *curval,
                      op_editop_t editop)
{
    (void)msg;
    (void)user;
    (void)newval;
    (void)curval;
    (void)editop;
    log_debug("\nacm_extern_write: return OK\n");
    return TRUE;

} /* acm_extern_write */


/********************************************************************
* FUNCTION acm_extern_read_fn
*
* Check if the specified user is allowed to read a value node
*
* INPUTS:
*   msg == XML header from incoming message in progress
*   user == user name string
*   val  == val_value_t in progress to check
*
* RETURNS:
*   TRUE if user allowed read access to the value node
*********************************************************************/
static boolean
    acm_extern_read (xml_msg_hdr_t *msg,
                     const xmlChar *user,
                     const val_value_t *val)
{
    (void)msg;
    (void)user;
    (void)val;
    log_debug("\nacm_extern_read: return OK\n");
    return TRUE;

} /* acm_extern_read */

#endif   // ADD_ACM_EXTERN


/****************  Example External NACM Group Hook *******************/

#ifdef ADD_NACM_GROUP
/********************************************************************
* FUNCTION nacm_external_group_cbfn
*
 * Get the list of group names for this username
 * These groups are added to the usergroup cache for the session
 * INPUTS:
 *   username: return the list of group names that this username
 *             is a member
 *   retgroups == address of return malloced string
 * OUTPUTS:
 *   *retgroups is set to a malloced string that will be parsed.
 *         It contains a whitespace delimited list of group named
 *            ' group1 group2 group3'
 *         The caller will free this string with m__free
 * RETURNS:
 *   status: if an error occurs the session will only use NACM groups
*********************************************************************/
static status_t
    nacm_external_group_cbfn (const xmlChar *username,
                              xmlChar **retgroups)
{
    if (retgroups == NULL) {
        return ERR_NCX_INVALID_VALUE;
    }

    (void)username;

    /* MUST use a function that allocates memory with m__getMem
     * Will be freed by the caller with m__free macro
     */
    *retgroups = xml_strdup("group1 group2 group5");
    if (*retgroups == NULL) {
        return ERR_INTERNAL_MEM;
    }

    return NO_ERR;

} /* nacm_external_group_cbfn */

#endif   // ADD_NACM_GROUP


/**************  Example External Log Vendor Hooks ***************/

#ifdef ADD_VENDOR_LOG
/********************************************************************
* FUNCTION log_vendor_send_fn
*
* Vendor function to consume log output (mandatory)
*
* INPUTS:
*   app == "Facility" (Yangcli or Netconfd)
*   level == Logging level (error, warn, info, debug, ..., debug4)
*   fstr == format string (like printf)
*   args == variable argument list
*
* RETURNS:
*   void
*********************************************************************/
static void
    log_vendor_send_fn (log_debug_app_t app,
                        log_debug_t level,
                        const char *fstr,
                        va_list args)
{
    (void)app;
    (void)level;
    (void)fstr;
    (void)args;
    return;

} /* log_vendor_send_fn */
#endif   // ADD_VENDOR_LOG


/***********  Example External NV-Storage Handler Hooks **********/

#ifdef ADD_NV_STORE_HOOK
#define EXAMPLE_CONFIG_SPEC (const xmlChar *)"/tmp/example-config.xml"
/********************************************************************
* FUNCTION example_nvsave
*
* Nvsave callback is invoked when some config needs to be saved
* to non-volatile storage
*
* INPUTS:
*    encoding == encoding format for the config (xml only allowed value)
*    filespec == filespec containing the config to save
*
* RETURNS:
*    status; error indicates NV-save failed somehow
*
*********************************************************************/
static status_t
  example_nvsave (ncx_display_mode_t encoding,
                  const xmlChar *filespec)
{
    status_t res = NO_ERR;

    if (filespec == NULL || *filespec == 0) {
        res = ERR_NCX_INVALID_VALUE;
    } else if (encoding != NCX_DISPLAY_MODE_XML) {
        res = ERR_NCX_INVALID_VALUE;
    } else {
        res = ncxmod_copy_text_file(filespec, EXAMPLE_CONFIG_SPEC);
    }

    return res;

}  /* example_nvsave */


/********************************************************************
* FUNCTION example_nvload
*
* Nvload callback invoked when some config needs to be read
* from non-volatile storage
*
* INPUTS:
*    encoding == address of encoding for the config
*    filespec == address of filespec containing the config that was loaded
*
* OUTPUTS:
*    *encoding == set to the enum for the encoding used in the config
*    *filespec == malloced filespec containing the config that was loaded
*
* RETURNS:
*    status; error indicates NV-load failed somehow
*    If return NO_ERR and *filespec == NULL then use the factory config
*
*********************************************************************/
static status_t
    example_nvload (ncx_display_mode_t *encoding,
                    xmlChar **filespec)
{
    log_debug("\nEnter example_nvload");

    *filespec = NULL;
    *encoding = NCX_DISPLAY_MODE_XML;

    status_t res = NO_ERR;

    if (ncxmod_test_filespec(EXAMPLE_CONFIG_SPEC)) {
        /* file exists so copy the filespec */
        *filespec = xml_strdup(EXAMPLE_CONFIG_SPEC);
        if (*filespec == NULL) {
            res = ERR_INTERNAL_MEM;
        }
    }

    return res;

}  /* example_nvload */

#endif   // ADD_NV_STORE_HOOK


/***********  Example Transaction Management Hooks **********/

#ifdef ADD_TRANS_HOOK
/********************************************************************
* FUNCTION example_transaction_start
*
* The Start Transaction function is the user/system
* callback that is invoked before any changes to the
* candidate database will be committed.
*
* INPUTS:
*    txcb == transaction control block in progress
*
* RETURNS:
*    status
*
*********************************************************************/
static status_t
    example_transaction_start (agt_cfg_transaction_t *txcb)
{
    (void)txcb;
    log_debug("\nEnter transaction_start example");
    return NO_ERR;

}  /* example_transaction_start */


/********************************************************************
* FUNCTION example_transaction_complete
*
* The Transaction Complete function is the
* user/system callback that is invoked after
* the transactions has been processed.
*
* INPUTS:
*    txcb == transaction control block in progress
*
* RETURNS:
*    none
*
*********************************************************************/
static void
    example_transaction_complete (agt_cfg_transaction_t *txcb)
{
    (void)txcb;
    log_debug("\nEnter transaction_complete example");
    return;

}  /* example_transaction_complete */

#endif   // ADD_TRANS_HOOK


/***********  Example YANG Object Template Callback **********/

#ifdef ADD_YANG_OBJ_TEMP
#define FL_ACME_1  0x1
/********************************************************************
* FUNCTION example_transaction_complete
*
* User function callback template when a YANG object is
* parsed by yang_obj.c. This API is invoked at the
* end of the resolve phase if the status is NO_ERR
* It is skipped if the object has errors detected at the time
*
* Callback is invoked to check extensions in an obj_template_t
* representing YANG data nodes
*
* Assume YANG module foo exists with extension acme1
*
* module foo {
*   prefix f;
*   ...
*   extension acme1 { ... }
*
*
* The extension is used inside object definitions. e.g:
*
*    leaf X {
*      f:acme1;
*      type string;
*    }
*
* Assume there is a vendor bit defined for the user flags field
* ncx_yang_obj_cbfn_t
*
*  Run an instrumentation-defined function
*  for a 'object parsed' event
*
* INPUTS:
*   mod == module that is being parsed now
*   obj == object being parsed
*
*********************************************************************/
static void
    example_obj_template_cbfn (ncx_module_t *mod,
                               struct obj_template_t_ *obj)
{
    /* optional: use the module to check cetain module names to
     * pre-filter the callback
     */
    (void)mod;

    /* get the appinfoQ for the object */
    dlq_hdr_t *appinfoQ = obj_get_appinfoQ(obj);
    if (appinfoQ == NULL) {
        return;   // error!
    }

    /* check the object template appinfoQ to see if the vendor
     * extensions are present
     */
    ncx_appinfo_t *appinfo =
        ncx_find_appinfo(appinfoQ,
                         (const xmlChar *)"f",
                         (const xmlChar *)"acme1");
    if (appinfo) {
        OBJ_USER_FLAGS(obj) |= FL_ACME_1;
    }

} /* example_obj_template_cbfn */

#endif   // ADD_YANG_OBJ_TEMP


/***********  Example YANG Extension Handler Callback **********/

#ifdef ADD_YANG_EXTEN
/********************************************************************
* FUNCTION example_ext_cbfn
*
* One YANG Extension Handler Callback
*
* Callback is invoked to check a specific extension in an
* obj_template_t, typ_template_t, typ_def_t
*
* Assume the same YANG module foo exists with extension acme1
*
* The example callback does the same task as the
* example_obj_template_cbfn, using the per-callback approach
*
* Handle the parsing and processing of an external statement
* using the associated YANG extension statement
*
* This callback is invoked when the external statement is
* first encountered. The current token is the argument string
* if any or the identifier token if none.
* The next token is expected to be a semi-colon or a left brace
* The callback is expected to parse the closing semi-colon or
* entire sub-section including starting brace
*
* INPUTS:
*   rawpcb == parser control block in progress (cast as void *)
*   mod == module being processed
*   tkc == token chain of module tokens parse in progress
*   ext == extension definition record (allows a handler to
*         process multiple extension types)
*   cookie == cbfn_cookie from the extension 'ext'
*   arg == argument string used in the external statement (if any)
*   node_type == type of node being processed; direct parent
*         statement of the external statement using the extension
*         If NULL, then the parent statement is the module itself,
*         and 'mod' should be used as the 'node' pointer
*   node == pointer to node indicated by node_type
* OUTPUTS:
*
* RETURNS:
*   status of processing
*
*********************************************************************/
static status_t
    example_ext_cbfn (void *rawpcb,  // struct yang_pcb_t_ *pcb
                      ncx_module_t *mod,
                      tk_chain_t *tkc,
                      struct ext_template_t_ *ext,
                      void *cookie,
                      const xmlChar *arg,
                      ncx_node_t node_type,
                      void *node)
{
    (void)rawpcb;
    (void)mod;
    (void)tkc;
    (void)ext;
    (void)cookie;
    (void)arg;

    /* ignore this extension in all contexts except object template */
    if (node_type != NCX_NT_OBJ) {
        return NO_ERR;
    }

    /* get the object template */
    obj_template_t *obj = (obj_template_t *)node;

    /* set the acme1 bit */
    OBJ_USER_FLAGS(obj) |= FL_ACME_1;

    return NO_ERR;

} /* example_ext_cbfn */

#endif   // ADD_YANG_EXTEN


/***********  Example RPC Command Complete Callback **********/

#ifdef ADD_RPC_COMPLETE
/********************************************************************
* FUNCTION example_command_complete_cbfn
*
* One RPC Command Complete Handler Callback
*
* The Command Complete callback is the user/system callback
* that is invoked after each client command is executed
* for the NETCONF or RESTCONF protocols.
*
* The Command Complete is typically used for retrieval
* operations (get, get-config, get-bulk) to release resources
* used during GET2 callbacks invoked during the operation.
*
* Max Callbacks: limited by memory only
*
* INPUTS:
*   scb == session control block making the request
*   msg == incoming rpc_msg_t in progress
*   command_modname == YANG module name of command that is completed
*   command_name == YANG RPC object name of command that is completed
*
* OUTPUTS:
*    none
*
* RETURNS:
*    none
*
*********************************************************************/
static void
    example_command_complete_cbfn (ses_cb_t *scb,
                                   rpc_msg_t *msg,
                                   const xmlChar *command_modname,
                                   const xmlChar *command_name)
{
    (void)command_modname;

    if (!xml_strcmp(command_name, (const xmlChar *)"get") ||
        !xml_strcmp(command_name, (const xmlChar *)"get-config") ||
        !xml_strcmp(command_name, (const xmlChar *)"get-bulk")) {

        /* cleanup our internal get cache data
         * example_clean_get2_cache(scb, msg);
         */
    }

} /* example_command_complete_cbfn */

#endif   // ADD_RPC_COMPLETE


/***********  Example Session Hook Callback **********/

#ifdef ADD_SESSION_HOOK
/********************************************************************
* FUNCTION example_session_hook
*
* The Session Hook callback is invoked when a session starts
* and ends.
* Use ses.h macros like SES_USERNAME(scb) to get
* data like client username
* Max Callbacks: No limit (except available heap memory)
*
* INPUTS:
*    ses_event == session event type
*    scb == session control block for the session event
*
* OUTPUTS:
*    none
*
* RETURNS:
*    none
*
*********************************************************************/
static void
    example_session_hook (ncx_ses_event_t ses_event,
                          const ses_cb_t *scb)
{
    assert(scb);

    /* event name is either session-start or session-end */
    const xmlChar *event_name = ncx_get_ses_event_str(ses_event);

    /* peer_addr should be set */
    const xmlChar *peer_addr = SES_PEERADDR(scb);
    if (peer_addr == NULL) {
        peer_addr = NCX_EL_LOCALHOST;
    }


    /* user_name should be set */
    const xmlChar *user_name = SES_MY_USERNAME(scb);
    if (user_name == NULL) {
        user_name = NCX_EL_NOBODY;
    }

    /* protocol name should be set */
    ncx_protocol_t protocol = ses_get_protocol(scb);
    const xmlChar *protocol_name = ncx_get_protocol_name(protocol);
    if (protocol_name == NULL) {
        protocol_name = NCX_EL_NONE;
    }

    log_info("\nGot %s event from host %s, user %s, protocol %s\n",
             event_name, peer_addr, user_name, protocol_name);

}  /* example_session_hook */

#endif  // ADD_SESSION_HOOK


/****************  Required System Library Hooks *******************/

/********************************************************************
* FUNCTION yp_system_init_profile
*
* system init server profile callback
*
* Initialize the server profile if needed
*
* INPUTS:
*  profile == server profile to change if needed
*
*********************************************************************/
void
    yp_system_init_profile (agt_profile_t *profile)
{
    (void) profile;
    log_debug("\nyp_system init profile\n");

    /* example: use an external ACM module */
    //profile->agt_acm_model = AGT_ACM_MODEL_EXTERNAL;

    /* example: set the with-defaults also-supported bits
     * from agt/agt.h:
     * bitmask of the with-defaults enumerations that should be
     * enabled in the server
     *    explicit: bit0
     *    trim: bit1
     *    report-all: bit2
     *    report-all-tagged: bit3
     *
     * uint8 agt_withdef_enabled;
     *
     * The basic-mode (agt_defaultStyle and agt_defaultStyleEnum)
     * will be added by the server automatically, so a value of 0
     * will not enable any 'also-supported' retrieval modes
     * for the 'with-defaults' leaf
     */
    //profile->agt_withdef_enabled = bit1 | bit3;

    /* add to enable callhome reconnect after a close-session */
    //profile->agt_callhome_reconnect = TRUE;

} /* yp_system_init_profile */


/********************************************************************
* FUNCTION yp_system_init1
*
* init1 system call
* this callback is invoked twice; before and after CLI processing
*
* INPUTS:
*   pre_cli == TRUE if this call is before the CLI parameters
*              have been read
*              FALSE if this call is after the CLI parameters
*              have been read
* RETURNS:
*   status
*
*********************************************************************/
status_t yp_system_init1 (boolean pre_cli)
{
    status_t res = NO_ERR;
    log_debug("\nyp_system init1 (%s)\n",
              pre_cli ? "pre-CLI" : "post-CLI");

    if (pre_cli) {

#ifdef ADD_VENDOR_LOG
        // example -- register vendor callback to consume logging output.
        /* Note that output will not be re-directed to the vendor stream
         * until AFTER --log-vendor is parsed by CLI processing or log_vendor
         * is parsed by config file processing.
         * Uncomment the following 2 lines to enable!!
         */
        log_vendor_extern_register_send_fn(log_vendor_send_fn);
#endif   // ADD_VENDOR_LOG

    } else {

#ifdef ADD_ACM_EXTERN
        /* example -- external NACM callbacks
         * load module for external module
         * with ncxmod_load_module
         * register the external ACM callbacks
         * this will have no affect unless the
         * yp_system_init_profile fn sets the
         * agt_acm_model to AGT_ACM_MODEL_EXTERNAL
         */
        agt_acm_extern_register_callbacks(acm_extern_rpc,
                                         acm_extern_notif,
                                         acm_extern_write,
                                         acm_extern_read);
#endif   // ADD_ACM_EXTERN


#ifdef ADD_NV_STORE_HOOK
        /* example -- register NV-storage handler to load/save config */
        res = agt_register_local_nv_handler(example_nvload,
                                            example_nvsave);
        if (res != NO_ERR) {
            return res;
        }
#endif   // ADD_NV_STORE_HOOK


#ifdef ADD_YANG_OBJ_TEMP
        /* example -- Register a YANG Object Template Callback */
        res = ncx_set_yang_obj_callback(example_obj_template_cbfn);
        if (res != NO_ERR) {
            return res;
        }
#endif   // ADD_YANG_OBJ_TEMP


#ifdef ADD_YANG_EXTEN
        /* example -- Register an Extension Handler Callback */
        res = ext_register_cbfn((const xmlChar *)"acme-ext",
                                (const xmlChar *)"acme1",
                                example_ext_cbfn,
                                NULL);   // cookie
        if (res != NO_ERR) {
            return res;
        }
#endif   // ADD_YANG_EXTEN


#ifdef ADD_IPV6_FN
        /* Example: register a type-specific callback
         * In real usage at least 1 of the 3 callback functions
         * must be set
         */
        res = typ_userdef_register(NCXMOD_IETF_INET_TYPES,
                                   (const xmlChar *)"ipv6-address-no-zone",
                                   NULL,         // validate_fn
                                   my_ipv6_fn,   // canonical_fn
                                   NULL,         // compare_fn
                                   NULL);        // cookie
        if (res != NO_ERR) {
            return res;
        }
#endif  // ADD_IPV6_FN

#ifdef ADD_USER_TYPE
        /* Example: register a type-specific callback
         * Show all 3 callbacks being used
         */
        res = typ_userdef_register(USERTYPE_MODULE,
                                   USERTYPE_TYPE,
                                   dummy1_validate_fn,
                                   dummy1_canonical_fn,
                                   dummy1_compare_fn,
                                   NULL);        // cookie
        if (res != NO_ERR) {
            return res;
        }
#endif   // ADD_USER_TPYE

#ifdef ADD_SESSION_HOOK
        /* Example: register a Session Hook callback
         * uncomment following to enable
         */
        res = agt_cb_session_hook_register(example_session_hook);
#endif  // ADD_SESSION_HOOK

    }
    return res;

}  /* yp_system_init1 */


/********************************************************************
* FUNCTION yp_system_init2
*
* init2 system call
* this callback is invoked twice; before and after
* load_running_config processing
*
* INPUTS:
* pre_load == TRUE if this call is before the running config
*            has been loaded
*            FALSE if this call is after the running config
*            has been loaded
* RETURNS:
*  status
*
*********************************************************************/
status_t
    yp_system_init2 (boolean pre_load)
{
    log_debug("\nyp_system init2 (%s)\n",
              pre_load ? "pre-load-config" : "post-load-config");

    if (pre_load) {
#ifdef ADD_NACM_GROUP
        /* example -- Register a NACM External Groups Callback
         * uncomment following to enable
         */
        agt_acm_ietf_register_group_cbfn(nacm_external_group_cbfn);
#endif  // ADD_NACM_GROUP


#ifdef ADD_RPC_COMPLETE
        /* initialize a Command Complete callback */
        res = agt_cb_command_complete_register(example_command_complete_cbfn);
#endif  // ADD_RPC_COMPLETE


#ifdef ADD_TRANS_HOOK
        /* example -- Register a Transaction Start callback */
        res = agt_cb_trans_start_register(example_transaction_start);

        /* example -- Register a Transaction Complete callback */
        res = agt_cb_trans_complete_register(example_transaction_complete);
#endif   // ADD_TRANS_HOOK

    } else {
        ;
    }

    return NO_ERR;

}  /* yp_system_init2 */


/********************************************************************
* FUNCTION yp_system_cleanup
*
* System cleanup callback
* this callback is invoked once during agt_cleanup
*
* INPUTS:
*   none
*
* RETURNS:
*   none
*
*********************************************************************/
void yp_system_cleanup (void)
{
    log_debug("\nyp_system cleanup\n");

#ifdef ADD_SESSION_HOOK
    /* Example: Unregister a Session Hook callback.
     * This is optional; will get deleted at shutdown
     * uncomment following to enable
     */
    agt_cb_session_hook_unregister(example_session_hook);
#endif  // ADD_SESSION_HOOK


#ifdef ADD_TRANS_HOOK
    /* example -- Unregister a Transaction Start callback */
    agt_cb_trans_start_unregister(example_transaction_start);

    /* example -- Unregister a Transaction Complete callback */
    agt_cb_trans_complete_unregister(example_transaction_complete);
#endif   // ADD_TRANS_HOOK

} /* yp_system_cleanup */


/* END example-system.c */