../_images/logo.png

YumaPro yp-show API Guide

yp-show External Interface

The yangcli-pro client application supports an external vendor library that is loaded at boot-time by the yangcli_pro application. This library code is run in the context of the main yangcli-pro process. It is used to hook vendor-specific functions into the yangcli application,.

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

  • initialization

  • cleanup

  • hook in external yangcli model

  • hook in external yangcli interface

The default external show library is located in called 'libyp_show.so', it is located in the library path, such as /usr/lib/yumapro/libyp_show.so.

There is an example library in the 'libshow' directory of the YumaPro source tree. The 'example-show.cpp' file in the 'src' directory contains some stub code demonstrating how this library is used.

Mandatory YANG Module Definitions

For any added show commands, then there has to be a YANG module to define it. This module must be loaded into the application using the 'ncxmod_load_module' function.

Example initialization:

static ncx_module_t *mymod = NULL;
status_t foo_init (void)
{
    status_t res =
        ncxmod_load_module(mod_name, mod_revision, NULL, &mymod);

}

Use the 'ycli_show_extern_register_callbacks' function to register the cli show functions at init time.

/********************************************************************
* FUNCTION ycli_show_extern_register_callbacks
* Register the external callbacks for show implementation
* INPUTS:
*    module   == YANG module used for this function
*    showfn_keyword == key word used for this function.
*    showfn ==  show function callback
* RETURNS:
*   status of the function registration.
*
*********************************************************************/

extern status_t ycli_show_extern_register_callbacks (
                        (const xmlChar *) "yangcli-pro",
                        (const xmlChar *) "cache",
                        your_show_cache_function);

Example:

Mandatory yp-show API Functions

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

  • yp_show_init: Initialization function

  • yp_show_cleanup: Cleanup function

The following example shows the 'example-show.h' contents from the 'libshow/src' directory:

#ifndef _H_example_show
#define _H_example_show
/*
 * Copyright (c) 2012 - 2022, 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_status_enum
#include "status_enum.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief show init callback
 *
 * This callback is invoked twice; before and after CLI processing
 *
 * @return status: error will abort startup
 */
extern status_t yp_show_init (void);


/* show cleanup callback
 * this callback is invoked once during agt_cleanup
 */
extern void yp_show_cleanup (void);


#ifdef __cplusplus
}  /* end extern 'C' */
#endif


#endif

Terminal Customization APIs

The yp-show library can contain callbacks for altering the display output that is rendered to the standard output in interactive mode. This callback can alter the output before it is displayed to the user.

Command output is processed in the following order:

  1. Raw command output using the specified display mode

  2. yp-show terminal hook to alter output text

  3. pipe command filtering

  4. --More-- pagination

Register Callbacks

There are 2 callbacks that are registered, using one function from yangcli_term.h:

/********************************************************************
* FUNCTION ycli_register_term_callback
*
* Register a custom callback to process to server response
* or command output before it is processed by Pipe and More
*
* INPUTS:
*   test_callback == test command callback
*   exec_callback == exec command callback
*********************************************************************/
extern void
    ycli_register_term_callback (term_test_cbfn_t test_callback,
                                 term_cbfn_t exec_callback);

Test Function

The term_test_cbfn_t callback is invoked to determine if the command output should be processed by the “exec” callback.

/* external term handler test callback
* FUNCTION term_api_test_fn
*
* This API tests whether the term_api_fn is needed for this
* command or not;
*
* INPUTS:
*   session_cb == session control block
*   command_name == name of command being invoked or replied
*   reply_output == TRUE if being called from reply handler
*                   FALSE if being called from local command output
* RETURNS:
*    TRUE if command output API hook needed
*    FALSE if ommand output API hook not needed
*********************************************************************/
typedef boolean
    (*term_test_cbfn_t) (void *session_cb,
                         const char *command_name,
                         boolean reply_output);

Exec Function

The term_cbfn_t callback is invoked to alter the terminal output

/* external term handler callback
* FUNCTION term_api_fn
*
* This API alters the command or server output so
* yangcli_term can process the More and Pipe commands
*
* INPUTS:
*   session_cb == session control block
*   in_filespec == input filespec for API to process
*   out_filespec == output file for API to write output
* RETURNS:
*    status
*********************************************************************/

typedef status_t
    (*term_cbfn_t) (void *session_cb,
                    const char *in_filespec,
                    const char *out_filespec);

yp-show Example

The following code can be found in the libshow/src/example-show.cpp source file.

Registration:

extern "C" status_t yp_show_init (void)
{
    status_t res = NO_ERR;

    log_debug("\nyp_show init\n");

    ycli_register_term_callback(term_api_test_fn, term_api_fn);
    return NO_ERR;
}

Example test callback:

/* example terminal output test API */
static boolean
    term_api_test_fn (void *session_cb,
                      const char *command_name,
                      boolean reply_output)
{
    /* current session control block if needed */
    session_cb_t *cb = (session_cb_t *)session_cb;
    (void)cb;

    if (reply_output == FALSE) {
        return FALSE;
    }

    if (!strcmp(command_name, "get") ||
        !strcmp(command_name, "get-config")) {

        return TRUE;
    }

    return FALSE;

}  /* term_api_test_fn */

Example exec callback:

/* example terminal output API
 * This API alters the command or server output so
 * yangcli_term can process the More and Pipe commands
 */
static status_t
    term_api_fn (void *session_cb,
                 const char *in_filespec,
                 const char *out_filespec)
{
    /* current session control block if needed */
    session_cb_t *cb = (session_cb_t *)session_cb;
    (void)cb;

    /* get file into a buffer
     * this is just one way to process the input;
     * a line-by-line approach could be used to save memory
     */
    xmlChar *in_buff = NULL;
    status_t res = ncx_file_to_buffer(in_filespec, &in_buff);
    if (in_buff == NULL || res != NO_ERR) {
        m__free(in_buff);
        return res;
    }

    /* change all the '{' and '}' chars to spaces just
     * as an example of altering the output
     */
    xmlChar *p = in_buff;
    while (*p) {
        if (*p == '{' || *p == '}') {
            *p = ' ';
        }
        p++;
    }

    /* write altered buffer out to a file */
    res = ncx_buffer_to_file(out_filespec, in_buff);

    m__free(in_buff);

    return res;

}  /* term_api_fn */

yangcli-pro Interface

The yangcli-pro client application supports external yangcli functions.

The external callback functions must be registered with yangcli-pro.

External Show Callback Functions

The function 'ycli_show_extern_register_callbacks' in 'yangcli/yangcli_show_extern.h' is used by the external code to register its own show callback functions. The show functions are used by the yangcli-pro.

/********************************************************************
* FUNCTION ycli_show_extern_register_callbacks
*
* Register the external callbacks for show implementation
*
* INPUTS:
*    module   == YANG module used for this function
*    showfn_keyword == key word used for this function.
*    showfn ==  show function callback
*    cookie == context pointer (may be null)
* RETURNS:
*   status of the function registration.
*
*********************************************************************/
extern status_t ycli_show_extern_register_callbacks(
                                    const xmlChar* module,
                                    const xmlChar* keyword,
                                    ycli_show_extern_fn_t showfn,
                                    void *cookie);

If the external method is selected in the yangcli-pro initialization then the callback function MUST be provided.

The function must be registered:

  • show fn: Invoke the a vendor specific function. The yangcli_show_extern_fn_t template is used for this callback.

The following code snippet shows the API template definitions from yangcli/yangcli_show_extern.h.

/********************************************************************
*
* Callback yangcli_show_extern_fn_t to handle external show
* functions.
*
* INPUTS:
*   server_name == The current server name.
*   rpc == RPC method for the show command being processed.
*   line == CLI line input.
*   session_name == The name of the current session.
*   valset == valset filled in with parameters for the specified RPC
*   mode == help_mode_t(none, brief, normal, full)
*   cookie == context pointer passed in register time, (may be null)
*
* RETURNS:
*   Status: NO_ERR or error.
*********************************************************************/
typedef status_t
    (*yangcli_show_extern_fn_t) (const xmlChar *server_name
                                    obj_temp_t    *rpc,
                                    const xmlChar *line,
                                    const xmlChar *session_name
                                    const val_value_t *valset,
                                    help_mode_t mode,
                                    void *cookie);

Example External yangcli Command

The following example code is available in 'example-show.cpp'. It shows some dummy external functions and how they are registered during initialization.

/*
 * Copyright (c) 2012 - 2017, 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-show.c

  Example External Show Library

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

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

/* always include procdefs.h before all other YumaPro files */
#include "procdefs.h"

/* these H files can be in any order */
#include "example-show.h"
#include "log.h"
#include "ncx.h"
#include "ncxmod.h"
#include "ncxtypes.h"
#include "obj.h"
#include "status.h"
#include "val.h"
#include "xml_util.h"
#include "yangcli.h"
#include "yangcli_cmd.h"
#include "yangcli_control.h"
#include "yangcli_libshow.h"
#include "yangcli_session_cb.h"
#include "yangcli_term.h"



#define MODNAME (const xmlChar *)"example-fan"
#define FAN (const xmlChar *)"fan"
#define DIAGNOSTICS (const xmlChar *)"diagnostics"
#define EXAMPLE_CMDSTR (const xmlChar *)"example-cmd"

/* comment out to disable show fan command */
#define SHOW_FAN 1

/* comment out to disable show version command */
#define SHOW_VER 1

/* remove comment to enable term display hook example */
// #define TERM_API 1

/* comment out to disable top-level command example-cmd */
#define EXAMPLE_CMD 1

static ncx_module_t *mymod = NULL;


/************  Example External Yangcli external call Hooks ****************/

#ifdef SHOW_FAN
/********************************************************************
* FUNCTION show_fan
*
* INPUTS:
*    server_name == the server name.
*    rpc == RPC method for the show command
*    line == CLI input in progress
*    session_name == the current session name.
*    valset == value set to check if not NULL
*    mode ==  HELP_MODE_NONE, HELP_MODE_BRIEF, HELP_MODE_NORMAL,
*             HELP_MODE_DETAIL
*    cookie == context pointer (may be null)
* RETURNS:
*   status
*********************************************************************/
static status_t
     show_fan (
              const xmlChar *server_name,
              obj_template_t *rpc,
              const xmlChar *line,
              const xmlChar *session_name,
              val_value_t *valset,
              help_mode_t mode,
              void *cookie)
{
    /* parms not used */
    (void)server_name;
    (void)rpc;
    (void)line;
    (void)session_name;
    (void)cookie;

    /* get the fan number
     * yangcli returns a union type as a string!
     */
    const xmlChar *fan_num = (const xmlChar *)"1";
    val_value_t *fanval = val_find_child(valset, MODNAME, FAN);
    if (fanval) {
        if (VAL_TYPE(fanval) == NCX_BT_STRING) {
            if (!val_all_whitespace(VAL_STR(fanval))) {
                /* fan_num should be a valid number string */
                fan_num = VAL_STR(fanval);
            }
        } else {
            // some sort of error
            ;
        }
    } // else error mandatory parm

    /* get the value, there is no value to get for 'empty' */
    val_value_t *diagval =
        val_find_child(valset, MODNAME, DIAGNOSTICS);

    /* print banner for fan */
    log_write("\nReport for Fan %s", fan_num);
    log_write("\n  put fan status here...");

    if (mode != HELP_MODE_BRIEF) {

        /* only normal of full modes print diagnostics */
        log_write("\n  put more fan status here...");

        if (mode == HELP_MODE_FULL) {
            log_write("\n  put even more fan status here...");
        }

        if (diagval) {
            /* print last diagnostics report */
            log_write("\nLast Diagnostics Report for Fan %s:", fan_num);
        }
    }

    log_write("\n");

    server_cb_t *server_cb = get_default_server_cb();
    if (server_cb == NULL) {
        return ERR_NCX_OPERATION_FAILED;
    }

    session_cb_t *session_cb = get_cur_session_cb(server_cb);
    if (session_cb == NULL) {
        return ERR_NCX_OPERATION_FAILED;
    }

    if (!session_connected(session_cb)) {
        /* cannot send commands to server */
        return NO_ERR;
    }

    /* get data from the server */
    xmlChar *getstring =
        xml_strdup((const xmlChar *)"sget /modules-state/module-set-id");
    if (!getstring) {
        return ERR_INTERNAL_MEM;
    }


    status_t res =
        conn_command(server_cb, session_cb, getstring, FALSE, FALSE);
    if (res != NO_ERR) {
        log_error("\nError: send %s failed %s",
                  getstring, get_error_string(res));
    }
    m__free(getstring);

    getstring =
        xml_strdup((const xmlChar *)"sget /netconf-state/sessions --nofill");
    if (!getstring) {
        return ERR_INTERNAL_MEM;
    }

    res = conn_command(server_cb, session_cb, getstring, FALSE, FALSE);
    if (res != NO_ERR) {
        log_error("\nError: send %s failed %s",
                  getstring, get_error_string(res));
    }
    m__free(getstring);

    return res;

} /* show_fan */

#endif  // SHOW_FAN


#ifdef SHOW_VER
/********************************************************************
* FUNCTION show_version
*
* INPUTS:
*    server_name == the server name.
*    rpc == RPC method for the show command
*    line == CLI input in progress
*    session_name == the current session name.
*    valset == value set to check if not NULL
*    mode ==  HELP_MODE_NONE, HELP_MODE_BRIEF, HELP_MODE_NORMAL,
*             HELP_MODE_DETAIL
*    cookie == context pointer (may be null)
* RETURNS:
*   status
*********************************************************************/
static status_t
     show_version (
              const xmlChar *server_name,
              obj_template_t *rpc,
              const xmlChar *line,
              const xmlChar *session_name,
              val_value_t *valset,
              help_mode_t mode,
              void *cookie)
{
    /* parms not used */
    (void)server_name;
    (void)rpc;
    (void)line;
    (void)session_name;
    (void)cookie;

    /* no input to get for <version> leaf
     * just print version line
     */
    log_write("\nExample Version: 10.1");

    if (mode != HELP_MODE_BRIEF) {

        /* only normal of full modes print diagnostics */
        log_write("\n  Release: A4");

        if (mode == HELP_MODE_FULL) {
            log_write("\n  Firmware: R12.4.001");
        }
    }

    log_write("\n");

    return NO_ERR;

} /* show_version */

#endif  // SHOW_VER


#ifdef TERM_API

/* example terminal output test API
 */
static boolean
    term_api_test_fn (void *session_cb,
                      const char *command_name,
                      boolean reply_output)
{
    /* current session control block if needed */
    session_cb_t *cb = (session_cb_t *)session_cb;
    (void)cb;

    if (reply_output == FALSE) {
        return FALSE;
    }

    if (!strcmp(command_name, "get") ||
        !strcmp(command_name, "get-config")) {

        return TRUE;
    }

    return FALSE;

}  /* term_api_test_fn */


/* example terminal output API
 * This API alters the command or server output so
 * yangcli_term can process the More and Pipe commands
 */
static status_t
    term_api_fn (void *session_cb,
                 const char *in_filespec,
                 const char *out_filespec)
{
    /* current session control block if needed */
    session_cb_t *cb = (session_cb_t *)session_cb;
    (void)cb;

    /* get file into a buffer
     * this is just one way to process the input;
     * a line-by-line approach could be used to save memory
     */
    xmlChar *in_buff = NULL;
    status_t res = ncx_file_to_buffer(in_filespec, &in_buff);
    if (in_buff == NULL || res != NO_ERR) {
        m__free(in_buff);
        return res;
    }

    /* change all the '{' and '}' chars to spaces just
     * as an example of altering the output
     */
    xmlChar *p = in_buff;
    while (*p) {
        if (*p == '{' || *p == '}') {
            *p = ' ';
        }
        p++;
    }

    /* write altered buffer out to a file */
    res = ncx_buffer_to_file(out_filespec, in_buff);

    m__free(in_buff);

    return res;

}  /* term_api_fn */


#endif  // TERM_API


#ifdef EXAMPLE_CMD
/********************************************************************
 * FUNCTION do_example_cmd (local RPC)
 *
 * Do Example Command
 *
 * INPUTS:
 *    server_cb == server control block to use
 *    session_cb == session control block to use
 *    rpc == RPC method for the example-cmd command
 *    line == CLI input in progress
 *    len == offset into line buffer to start parsing
 *
 * RETURNS:
 *   status
 *********************************************************************/
static status_t
    do_example_cmd (server_cb_t *server_cb,
                    session_cb_t *session_cb,
                    obj_template_t *rpc,
                    const xmlChar *line,
                    uint32 len)
{
    (void)rpc;

    status_t res = NO_ERR;
    val_value_t *valset =
        get_valset(server_cb, session_cb, rpc, &line[len], &res);
    if (valset && (res == NO_ERR)) {
        val_value_t *parm1 =
            val_find_child(valset, MODNAME, (const xmlChar *)"parm1");
        if (parm1) {
            log_debug("\nGot parm1=%s", VAL_STR(parm1));
        }

        val_value_t *parm2 =
            val_find_child(valset, MODNAME, (const xmlChar *)"parm2");
        if (parm2) {
            log_debug("\nGot parm2=%d", VAL_INT32(parm2));
        }

        /* do something with the parameters */
    }

    val_free_value(valset);

    return res;

}  /* do_example_cmd */
#endif  // EXAMPLE_CMD


/****************  Required Show Library Hooks *******************/

/* show init callback
 * init show call
 * INPUTS: void
 * RETURNS:
 *  status; error will abort startup
 */
extern "C" status_t yp_show_init (void)
{
    status_t res = NO_ERR;

    log_debug("\nyp_show init\n");

#ifdef TERM_API
    ycli_register_term_callback(term_api_test_fn, term_api_fn);
#endif  // TERM_API

#if defined(SHOW_FAN) || defined(SHOW_VER) || defined(EXAMPLE_CMD)
    /*
     * Example: load a fan module for show fan function.
     */
    res = ncxmod_load_module(MODNAME,
                             NULL,   // revision
                             NULL,   // savedevQ
                             &mymod);

#endif  // SHOW_FAN or SHOW_VER or EXAMPLE_CMD

    /*
     * Example: Register a show function with yangcli-pro
     * module name: example-fan for example-fan.yang
     * key word: fan
     * function: show_fan
     */

    if (res == NO_ERR) {
#ifdef SHOW_FAN
        /* Create a cookie context if any or NULL */
        void *cookie_show_fan = NULL;
        ycli_show_extern_register_callbacks(MODNAME,
                                            (const xmlChar *)"fan",
                                            show_fan,
                                            cookie_show_fan);
#endif  // SHOW_FAN

#ifdef SHOW_VER
        /* Create a cookie context if any or NULL */
        void *cookie_show_ver = NULL;
        ycli_show_extern_register_callbacks(MODNAME,
                                            (const xmlChar *)"version",
                                            show_version,
                                            cookie_show_ver);
#endif  // SHOW_VER

#ifdef EXAMPLE_CMD
        server_cb_t *server_cb = get_default_server_cb();
        if (server_cb == NULL){
            res = ERR_NCX_NOT_FOUND;
        } else {
            res = register_command(server_cb,
                                   MODNAME,
                                   (const xmlChar *)"example-cmd",
                                   do_example_cmd,
                                   TRUE,   // is_top_cmd
                                   TRUE,   // yangcli_ok
                                   TRUE,   // ypshell_ok
                                   FALSE); // ypserver_ok (not used)
        }
#endif  // EXAMPLE_CMD

    } else {
       log_debug("\nyp_show_init: return ERROR\n");
    }

    return res;

}  /* yp_show_init */


/* show cleanup callback
 * this callback is invoked once during yancli_cleanup
 */
extern "C" void yp_show_cleanup (void)
{
    log_debug("\nyp_show cleanup\n");

} /* yp_show_cleanup */


/* END example-show.c */

yp-yangcli-show Library

The yp-show library contains vendor code for global APIs that are not specific to 1 YANG module.

The mandatory to implement callback functions for yangcli application initialization and cleanup are described in this section.

Copy and Setup libshow subtree

The default library created by building libshow is 'libyp_show-example.so'. This is not the correct filename, in order to prevent the real yap-show library from being overwritten by the empty example library, if “make install” is run.

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

Instructions are in the src/Makefile:

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

Change the string 'yp-show-example' to 'yp-show' in the src/Makefile in the copy of libshow.

yp_show_init

This function is used to initialize the show commands in the yangcli-pro application during initialization.

/* yangcli show init callback
 *
 * INPUTS:
 *
 * RETURNS:
 *  status; error will abort startup
 */
extern status_t yp_show_init (void);

yp_show_cleanup

This function is used to cleanup the yangcli-pro show commands when the application is shutting down or restarting.

/* yangcli show cleanup callback
 * this callback is invoked once during yangcli_cleanup
 */
extern void yp_show_cleanup (void);

yangmap External Interface

The yangmap feature allows one YANG model to be mapped to another YANG model to simplify or stabilize the client-facing command interface, in “config term” mode.

+--------------+      +---------+     +--------------+
| source model |  <-> | yangmap | <-> | target model |
+--------------+      +---------+     +--------------+

Each yangmap file contains the following information

  • source module name(s)

  • target module name(s)

  • automap flag

  • set of nodemaps (source to target)

    • set of keymaps (source to target key)

    • set of childmaps (source to target leaf)

When a yangmap is active, the source model will be used if the target models are supported in a session.

If the server advertises all the target modules in the yangmap, then these modules will be hidden in config term mode.

Instead, the source models will be injected into the session and these modules will be used instead of the target modules.

The “show modules” and “show objects” commands will hide the target modules.

--yangmap CLI Parameter

The --yangmap CLI parameter specifies a YANG mapping file that contains a <yangmap> instance document.

This can be placed in the yangcli-pro.conf config file.

This mapping is used to convert the source model to the target model. There is no support at this time to convert the target model to the source model.

leaf-list yangmap {
  description
    "Specifies a yangmap XML file that should be loaded
     into the program. See yumaworks-yangmap.yang for
     details on using YANG mappings in config term mode.";
  type string;   // filespec
}

Example yangcli-pro.conf:

yangcli-pro {

    yangmap /home/lab/yangmaps/test-ex1-yangmap.xml

}

yumaworks-yangmap YANG Module

The yumaworks-yangmap.yang module contains the data structures used for this feature.

yangmap Example

This simple example shows how the yangmap feature is configured and used.

Source Model

The YANG module 'test-ex2.yang' is used as the source model:

module test-ex2 {

    namespace "http://yumaworks.com/ns/test-ex2";

    prefix "e2";

    description
      "Example sourcet module for yangmap
       test-ex1-yangmap.xml creates the following mapping:

       logicalp -> /logical-ports/logical-port
         id     ->                    name
         nest   ->                    nest2
          A     ->                      name
          B     ->                      N2
          C     ->                      N3
      ";

    revision 2018-02-22;

    // test source (client-facing) data model
    list logicalp {
        key "id";   // map to name in test-ex1
        leaf id { type string; }
        list nest {
          key A;
          leaf A { type string; }
          leaf B { type int32; }
          leaf C { type uint32; }
        }
    }

}

Target Model

The YANG module 'test-ex1.yang' is used as the target model:

module test-ex1 {

    namespace "http://yumaworks.com/ns/test-ex1";

    prefix "e1";

    description
      "Example target module for yangmap";

    revision 2018-02-22;

    // test target (hidden from client) data model
    container logical-ports {
      list logical-port {
        key "name";
        leaf name { type string; }

        list nest2 {
          key name;
          leaf name { type string; }
          leaf N2 { type int32; }
          leaf N3 { type uint32; }
        }
      }
    }

}

YANGMAP File

The XML file 'test-ex1-yangmap.xml' contains the mapping information for this yangmap:

<yangmap>
  <source-module>test-ex2</source-module>
  <target-module>test-ex1</target-module>
  <nodemap>
    <source-node>/test-ex2:logicalp</source-node>
    <target-node>/test-ex1:logical-ports/logical-port</target-node>
    <target-keys>
      <keymap>
        <key-node>/test-ex1:logical-ports/logical-port/name</key-node>
        <source-node>/test-ex2:logicalp/id</source-node>
      </keymap>
    </target-keys>
  </nodemap>
  <nodemap>
    <source-node>/test-ex2:logicalp/nest</source-node>
    <target-node>/test-ex1:logical-ports/logical-port/nest2</target-node>
    <target-keys>
      <keymap>
        <key-node>/test-ex1:logical-ports/logical-port/name</key-node>
        <source-node>/test-ex2:logicalp/id</source-node>
      </keymap>
      <keymap>
        <key-node>/test-ex1:logical-ports/logical-port/nest2/name</key-node>
        <source-node>/test-ex2:logicalp/nest/A</source-node>
      </keymap>
    </target-keys>
    <childmap>
      <source-child>B</source-child>
      <target-node>/test-ex1:logical-ports/logical-port/nest2/N2</target-node>
    </childmap>
    <childmap>
      <source-child>C</source-child>
      <target-node>/test-ex1:logical-ports/logical-port/nest2/N3</target-node>
    </childmap>
  </nodemap>
</yangmap>

External Commands

External commands can be supported by yangcli-pro and yp-shell.

This support is limited to the functionality available to existing yangcli-pro commands:

  • Access local data structures

  • Access local files

  • Generate log output

  • Send 1 or 2 commands to the server

  • Override or add to the existing reply output processing

YANG for Command Definitions

The YANG module definition is used for several tasks:

  • process command line input, including command name and input parameters

  • control tab key completion

  • provide help text for commands and input parameters

The YANG syntax is restricted for CLI command purposes:

  • must statement validation is not done

  • unique statement validation is not done

Example Command Definition:

rpc example-cmd {
  description "Example external command";
  input {
    leaf parm1 {
      type string;
      description "The first example parameter";
    }
    leaf parm2 {
      type int32;
      description "The second example parameter";
    }
  }
}

yp-show API Functions

There are a limited number of API functions available to add an external command.

A yangcli-pro or yp-shell command requires at least 3 components:

  1. YANG module “RPC statement” defining the command syntax

  2. Callback function to do the command when requested by user input

  3. Call to the registration function to add the command into the program

External Command Callback Function

The callback function for the external command must use the “yangcli_command_cbfn_t” definition in yangcli.h:

/* Callback template for a local command
 *
 * Handles the command line for the specified command
 *
 * INPUTS:
 *   server_cb == server control block to use
 *   session_cb == session control block to use
 *   rpc == object template for the command
 *   line == input command line to execute
 *   len == offset into the line to start processing
 *          this can be > 0 if the command is the left-hand-side
 *          of an assignment statement
 * RETURNS:
 *    status
 */
typedef status_t
    (*yangcli_command_cbfn_t) (server_cb_t *server_cb,
                               session_cb_t *session_cb,
                               obj_template_t *rpc,
                               const xmlChar *line,
                               uint32 len);

The following example callback can be found in example-fan.cpp

/********************************************************************
 * FUNCTION do_example_cmd (local RPC)
 *
 * Do Example Command
 *
 * INPUTS:
 *    server_cb == server control block to use
 *    session_cb == session control block to use
 *    rpc == RPC method for the example-cmd command
 *    line == CLI input in progress
 *    len == offset into line buffer to start parsing
 *
 * RETURNS:
 *   status
 *********************************************************************/
static status_t
    do_example_cmd (server_cb_t *server_cb,
                    session_cb_t *session_cb,
                    obj_template_t *rpc,
                    const xmlChar *line,
                    uint32 len)
{
    (void)rpc;

    status_t res = NO_ERR;
    val_value_t *valset =
        get_valset(server_cb, session_cb, rpc, &line[len], &res);
    if (valset && (res == NO_ERR)) {
        val_value_t *parm1 =
            val_find_child(valset, MODNAME, (const xmlChar *)"parm1");
        if (parm1) {
            log_debug("\nGot parm1=%s", VAL_STR(parm1));
        }

        val_value_t *parm2 =
            val_find_child(valset, MODNAME, (const xmlChar *)"parm2");
        if (parm2) {
            log_debug("\nGot parm2=%d", VAL_INT32(parm2));
        }

        /* do something with the parameters */
    }

    val_free_value(valset);

    return res;

}  /* do_example_cmd */

Key steps:

  • the input line is parsed. This should be done even if no input parameters to make sure no extra parameters are present.

  • a val_value_t tree is produced representing a container of all the input parameters that were parsed

  • the parameters are checked with “val_find_child”

  • the callback does the work required for the command

  • the val_value_t for the input parameters is freed

Register an External Command

The “register_command” function is called from the yp_show_init function to register an external command:

/********************************************************************
* FUNCTION register_command
*
* INPUTS:
*    server_cb == server control block to use
*    module == module name containing the RPC method
*    ycli_command_name == RPC method name
*    command_fn == pointer to callback function for this command
*    is_top_cmd == TRUE if opt-level command
*    yangcli_ok == TRUE if OK for yangcli to use it
*    ypshell_ok == TRUE if OK for yp-shell to use it
*    ypserver_ok == TRUE if OK for yp-server to use it
*
* RETURNS:
*    status of the operation
*********************************************************************/
extern status_t
    register_command (
              server_cb_t *server_cb,
              const xmlChar *module,
              const xmlChar *ycli_command_name,
              yangcli_command_cbfn_t command_fn,
              boolean is_top_cmd,
              boolean yangcli_ok,
              boolean ypshell_ok,
              boolean ypserver_ok);

Usage Example: (from example-fan.cpp)

server_cb_t *server_cb = get_default_server_cb();
if (server_cb == NULL){
    res = ERR_NCX_NOT_FOUND;
} else {
    res = register_command(server_cb,
                           MODNAME,
                           (const xmlChar *)"example-cmd",
                           do_example_cmd,
                           TRUE,   // is_top_cmd
                           TRUE,   // yangcli_ok
                           TRUE,   // ypshell_ok
                           FALSE); // ypserver_ok (not used)
}

Usage Notes:

  • The YANG module identified by “module” must already be loaded

  • The command name cannot be a duplicate of any command in yangcli-pro.yang

  • The command name cannot be a duplicate of any other external command

  • The “is_top_cmd” field must be set to TRUE

  • The “ypserver_ok field is ignored but should be set to FALSE

Example YANG Module

The example-fan.yang module is included here for reference:

module example-fan {

    yang-version 1.1;
    namespace "http://example.com/ns/example-fan";
    prefix fan;
    import yangcli-pro { prefix yp; }

    organization  "Example, Inc.";
    contact "Support <support@example.com>.";

    description
       "Example show command extension for yangcli-pro";

    revision 2018-03-17 {
       description
         "Add example-cmd";
    }

    revision 2017-12-04 {
       description
         "Change to YANG 1.1 to allow empty in case";
    }

    revision 2014-12-05 {
       description
         "Initial version";
    }

    /* add a case to the fan show command, e.g., 'show fan 1'
     * must not clash with other case names in the showtype choice
     */
    augment "/yp:show/yp:input/yp:showtype" {
        case fan {
            leaf fan {
                type union {
                  type empty;
                  type uint32 {
                    range "1 .. max";
                  }
                }
                mandatory true;
                description
                  "The fan number to show or the primary fan if
                   no value is given.";
            }
            leaf diagnostics {
                type empty;
                description
                    "If present, display extra diagnostics info";
            }
        }
        case version {
          leaf version {
            description "Example.com version";
            type empty;
          }
        }
    }

    rpc example-cmd {
      description "Example external command";
      input {
        leaf parm1 {
          type string;
          description "The first example parameter";
        }
        leaf parm2 {
          type int32;
          description "The second example parameter";
        }
      }
    }
}