Error Handling

The server has several APIs and built-in mechanisms for generating error responses for client requests. This is done is a protocol-independent way. The error reporting procedures and data sre protocol-specific, but these are handled automatically by the server.

RPC Error Example

Assuming the “interfaces” container preexist in the datastore. To invoke the callback the following RPC can be sent. This example contains an error so a RESTCONF <errors> block will be sent to the client.

<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <edit-config>
    <target>
      <candidate/>
    </target>
    <config>
      <interfaces xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
        <interface>
          <name>not-supported</name>
        </interface>
     </interfaces>
    </config>
  </edit-config>
</rpc>

The server may reply with the following:

<errors xmlns:ncx="http://netconfcentral.org/ns/yuma-ncx"
  xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
  <error>
    <error-type>rpc</error-type>
    <error-tag>operation-not-supported</error-tag>
    <error-app-tag>no-support</error-app-tag>
    <error-message xml:lang="en">operation not supported</error-message>
    <error-info>
      <error-number>273</error-number>
    </error-info>
  </error>
</errors>

RPC Error Message

NETCONF has several standard error fields that are returned in an <rpc-error> response. These are defined in RFC 6241, section 4.3.

RESTCONF defines its own structure to return for errors, but it has the same child fields as NETCONF. Only the container structure is different. These are defined in RFC 8040.

error-type

Mandatory field set by the server indicating the protocol layer where the error occurred.

One of:

  • transport

  • rpc

  • protocol

  • application

The 'ncx_layer_t' enumeration defined in ncx/ncxtypes.h is used for this field.

enum ncx_layer_t

Enumeration of NETCONF protocol layers.

Values:

enumerator NCX_LAYER_NONE

not set

enumerator NCX_LAYER_TRANSPORT

transport layer

enumerator NCX_LAYER_RPC

RPC operation layer.

enumerator NCX_LAYER_OPERATION

protocol operation layer

enumerator NCX_LAYER_CONTENT

application layer

error-tag

Mandatory enumeration for the type of error that occurred. This field is set by the server depending on the 'status_t' of the error. The values are defined in RFC 6241, Appendix A.

One of:

  • in-use

  • invalid-value

  • too-big

  • missing-attribute

  • bad-attribute

  • unknown-attribute

  • missing-element

  • bad-element

  • unknown-element

  • unknown-namespace

  • access-denied

  • lock-denied

  • resource-denied

  • rollback-failed

  • data-exists

  • data-missing

  • operation-not-supported

  • operation-failed

  • malformed-message

The 'rpc_err_t' enumeration defined in ncx/rpc_err.h is used for this field.

enum rpc_err_t

enumerations for NETCONF standard errors

Values:

enumerator RPC_ERR_NONE

not set

enumerator RPC_ERR_IN_USE

in-use

enumerator RPC_ERR_INVALID_VALUE

invalid-value

enumerator RPC_ERR_TOO_BIG

too-big

enumerator RPC_ERR_MISSING_ATTRIBUTE

missing-attribute

enumerator RPC_ERR_BAD_ATTRIBUTE

bad-attribute

enumerator RPC_ERR_UNKNOWN_ATTRIBUTE

unknown-attribute

enumerator RPC_ERR_MISSING_ELEMENT

missing-element

enumerator RPC_ERR_BAD_ELEMENT

bad-element

enumerator RPC_ERR_UNKNOWN_ELEMENT

unknown-element

enumerator RPC_ERR_UNKNOWN_NAMESPACE

unknown-namespace

enumerator RPC_ERR_ACCESS_DENIED

access-denied

enumerator RPC_ERR_LOCK_DENIED

lock-denied

enumerator RPC_ERR_RESOURCE_DENIED

resource-denied

enumerator RPC_ERR_ROLLBACK_FAILED

rollback-failed

enumerator RPC_ERR_DATA_EXISTS

data-exists

enumerator RPC_ERR_DATA_MISSING

data-missing

enumerator RPC_ERR_OPERATION_NOT_SUPPORTED

operation-not-supported

enumerator RPC_ERR_OPERATION_FAILED

operation-failed

enumerator RPC_ERR_PARTIAL_OPERATION

partial-operation, deprecated; not used

enumerator RPC_ERR_MALFORMED_MESSAGE

malformed-message

error-severity

Mandatory field set by the server. The value error is (almost) always used because no standard error-tag values are defined with a severity of warning.

One of:

  • error

  • warning

The 'rpc_err_sev_t' enumeration defined in ncx/rpc_err.h is used for this field.

enum rpc_err_sev_t

enumerations for NETCONF standard error severities

The protocol does not actually use RPC warnings and these are not supported or used in the server

Values:

enumerator RPC_ERR_SEV_NONE

not set

enumerator RPC_ERR_SEV_WARNING

warning

enumerator RPC_ERR_SEV_ERROR

error (only used value)

Note

The warning enumeration can be used even though it is not standard. The --with-warnings CLI parameter must be used to enable this feature in the server. If set then the agt_record_warning API function can be used to record a warning instead of an error.

error-app-tag

Optional field identifying the application-specific error. There are some standard values defined by YANG that must be used in certain error conditions.

The server will use these values even if error-app-tag values are defined for the specific errors instead:

  • must-violation

  • instance-required

  • missing-choice

  • missing-instance

Reserved NETCONF error-app-tag Values

Several values are defined in NETCONF standards. These cannot be redefined in the server.

#define RPC_ERR_APPTAG_UNIQUE_FAILED   (const xmlChar *)"data-not-unique"
#define RPC_ERR_APPTAG_MAX_ELEMS       (const xmlChar *)"too-many-elements"
#define RPC_ERR_APPTAG_MIN_ELEMS       (const xmlChar *)"too-few-elements"
#define RPC_ERR_APPTAG_MUST            (const xmlChar *)"must-violation"
#define RPC_ERR_APPTAG_INSTANCE_REQ    (const xmlChar *)"instance-required"
#define RPC_ERR_APPTAG_CHOICE          (const xmlChar *)"missing-choice"
#define RPC_ERR_APPTAG_INSERT          (const xmlChar *)"missing-instance"
#define RPC_ERR_APPTAG_RANGE           (const xmlChar *)"not-in-range"
#define RPC_ERR_APPTAG_VALUE_SET       (const xmlChar *)"not-in-value-set"
#define RPC_ERR_APPTAG_PATTERN         (const xmlChar *)"pattern-test-failed"
#define RPC_ERR_APPTAG_DATA_REST       (const xmlChar *)"data-restriction-violation"
#define RPC_ERR_APPTAG_DATA_NOT_UNIQUE (const xmlChar *)"data-not-unique"
#define RPC_ERR_APPTAG_NO_MATCHES      (const xmlChar *)"no-matches"
#define RPC_ERR_APPTAG_NOT_NODESET     (const xmlChar *)"not-a-node-set"
#define RPC_ERR_APPTAG_LOCKED          (const xmlChar *)"locked"
#define RPC_ERR_APPTAG_COMMIT          (const xmlChar *)"outstanding-confirmed-commit"

Reserved YumaPro error-app-tag Values

Several values are defined by YumaPro conventions and are built into the server implementation. These cannot be redefined in the server.

#define RPC_ERR_APPTAG_GEN_WARNING      (const xmlChar *)"general-warning"
#define RPC_ERR_APPTAG_GEN_ERROR        (const xmlChar *)"general-error"
#define RPC_ERR_APPTAG_INT_ERROR        (const xmlChar *)"internal-error"
#define RPC_ERR_APPTAG_IO_ERROR         (const xmlChar *)"io-error"
#define RPC_ERR_APPTAG_MALLOC_ERROR     (const xmlChar *)"malloc-error"
#define RPC_ERR_APPTAG_LIMIT_REACHED    (const xmlChar *)"limit-reached"
#define RPC_ERR_APPTAG_LIBXML2_ERROR    (const xmlChar *)"libxml2-error"
#define RPC_ERR_APPTAG_SQL_ERROR        (const xmlChar *)"sql-error"
#define RPC_ERR_APPTAG_SSH_ERROR        (const xmlChar *)"ssh-error"
#define RPC_ERR_APPTAG_BEEP_ERROR       (const xmlChar *)"beep-error"
#define RPC_ERR_APPTAG_HTML_ERROR       (const xmlChar *)"html-error"
#define RPC_ERR_APPTAG_DATA_INCOMPLETE  (const xmlChar *)"data-incomplete"
#define RPC_ERR_APPTAG_DATA_INVALID     (const xmlChar *)"data-invalid"
#define RPC_ERR_APPTAG_DUPLICATE_ERROR  (const xmlChar *)"duplicate-error"
#define RPC_ERR_APPTAG_RESOURCE_IN_USE  (const xmlChar *)"resource-in-use"
#define RPC_ERR_APPTAG_NO_ACCESS        (const xmlChar *)"no-access"
#define RPC_ERR_APPTAG_RECOVER_FAILED   (const xmlChar *)"recover-failed"
#define RPC_ERR_APPTAG_NO_SUPPORT       (const xmlChar *)"no-support"

Reserved YANG Push error-app-tag Values

Several values are defined by Subscribed Notifications (RFC 8639) and YANG Push (RFC 8641). These are built into the server implementation and used if the "yang-push" bundle is loaded into the server. These cannot be redefined in the server.

/*** ietf-subscribed-notifications ***/

#define RPC_ERR_APPTAG_NOTIF_DSCP_UNAVAILABLE \
    (const xmlChar *)"ietf-subscribed-notifications:dscp-unavailable"

#define RPC_ERR_APPTAG_NOTIF_ENCODING_UNSUPPORTED \
    (const xmlChar *)"ietf-subscribed-notifications:encoding-unsupported"

#define RPC_ERR_APPTAG_NOTIF_FILTER_UNAVAILABLE \
    (const xmlChar *)"ietf-subscribed-notifications:filter-unavailable"

#define RPC_ERR_APPTAG_NOTIF_FILTER_UNSUPPORTED \
    (const xmlChar *)"ietf-subscribed-notifications:filter-unsupported"

#define RPC_ERR_APPTAG_NOTIF_INSUFFICIENT_RESOURCES \
    (const xmlChar *)"ietf-subscribed-notifications:insufficient-resources"

#define RPC_ERR_APPTAG_NOTIF_NO_SUCH_SUBSCRIPTION \
    (const xmlChar *)"ietf-subscribed-notifications:no-such-subscription"

#define RPC_ERR_APPTAG_NOTIF_REPLAY_UNSUPPORTED \
    (const xmlChar *)"ietf-subscribed-notifications:replay-unsupported"

#define RPC_ERR_APPTAG_NOTIF_STREAM_UNAVAILABLE \
    (const xmlChar *)"ietf-subscribed-notifications:stream-unavailable"

/*** ietf-yang-push errors ***/

#define RPC_ERR_APPTAG_PUSH_CANT_EXCLUDE \
    (const xmlChar *)"ietf-yang-push:cant-exclude"

#define RPC_ERR_APPTAG_PUSH_DATASTORE_NOT_SUBSCRIBABLE \
    (const xmlChar *)"ietf-yang-push:datastore-not-subscribable"

#define RPC_ERR_APPTAG_PUSH_NO_SUCH_RESYNC \
    (const xmlChar *)"ietf-yang-push:no-such-subscription-resync"

#define RPC_ERR_APPTAG_PUSH_ON_CHANGE_UNSUPPORTED \
    (const xmlChar *)"ietf-yang-push:on-change-unsupported"

#define RPC_ERR_APPTAG_PUSH_ON_CHANGE_SYNC_UNSUPPORTED \
    (const xmlChar *)"ietf-yang-push:on-change-sync-unsupported"

#define RPC_ERR_APPTAG_PUSH_PERIOD_UNSUPPORTED \
    (const xmlChar *)"ietf-yang-push:period-unsupported"

#define RPC_ERR_APPTAG_PUSH_UPDATE_TOO_BIG \
    (const xmlChar *)"ietf-yang-push:update-too-big"

#define RPC_ERR_APPTAG_PUSH_SYNC_TOO_BIG \
    (const xmlChar *)"ietf-yang-push:sync-too-big"

#define RPC_ERR_APPTAG_PUSH_UNCHANGING_SELECTION \
    (const xmlChar *)"ietf-yang-push:unchanging-selection"

error-path

Optional field containing an XPath absolute path expression identifying the node that caused the error. Will be present if the user input can be associated with the error.

This field is generated by the server depending on the parameters passed to the "record error" API functions.

error-message

Optional field containing a short error message. The server always generates an error-message field. A default error message for the Return Status will be returned if no custom message has been configured or provided by a SIL or SIL-SA callback function.

error-info

This optional field is a container that has child data nodes representing additional error information. Some NETCONF errors require that certain error-info child nodes be present, depending on the error-tag value

The following 'error-tag' values have specific error-info elements that are required:

  • bad-attribute

  • bad-element

  • bad-namespace

  • session-id

The server will generate the required data automatically for these errors.

Error Handling APIs

The server has several APIs for setting error information that will be returned to a client in an <rpc-error> message. Each API allows different amounts of context-specific information included in the error response.

../_images/error_data_components.png

Return Status

The 'status_t' enumeration returned by the SIL or SIL-SA code will be checked. If not error information has been recorded, then the server will construct an <rpc-error> based on the return status

SET_ERROR

The 'SET_ERROR' macro is for internal programming errors only. DO NOT USE!

agt_record_error

The 'agt_record_error' API allows the basic error fields to be set

agt_record_error_errinfo

The 'agt_record_error_errinfo' API allows all fields to be set, including over-riding the default error-message and error-app-tag fields.

agt_record_warning

The 'agt_record_warning' API allows a warning to be sent instead of an error

Return Status (status_t)

The 'status_t' is used to convey internal or protocol error conditions.

  • The internal status codes are defined in ncx/status_enum.h

  • The value 0 (NO_ERR) is used to indicate success.

  • Values 1 to 999 are error codes.

  • Values 1000 to 1999 are warning codes

  • Values 2000 to 2999 are system return codes

  • Error strings are defined in netconf/src/ncx/status.c in the 'get_error_string' function for each enumeration defined in status_enum.h

  • Each status_t enumeration is mapped to an 'error-tag' value in the file netconf/src/agt/agt_rpcerr.c in the function 'get_rpcerr'. This mapping also includes the default 'error-app-tag' value

  • The default 'error-message' and 'error-app-tag' values can be overridden using the YANG statements with the same name. These fields can also be specified internally (without adding these YANG statements) using the 'agt_record_error_errinfo' function instead of the 'agt_record_error' function.

const char *get_error_string(status_t res)

Get the error message for a specific internal error.

Parameters:

res -- internal status_t error code

Returns:

const pointer to error message

SET_ERROR

The 'SET_ERROR' macro is for programming errors, not client errors! The macro is defined in ncx/status.h and it is for flagging internal errors only. This macro must not be used for normal errors.

Warning

Do not use the SET_ERROR macro to return normal errors. This macro will cause “assert(0)” to be invoked, halting program execution.

This macro is used in some programming constructs to detect certain programming errors.

For example, switch statements for "enum" switches are required to have a "default" clause in YumaPro code. All known enum values must be handled. If a new enum is defined then the SET_ERROR will be hit if the switch has not been updated properly to handle the new value.

switch (cbtyp) {
case AGT_CB_VALIDATE:
    /* description-stmt validation here */
    break;
case AGT_CB_APPLY:
    /* database manipulation done here */
    break;
case AGT_CB_COMMIT:
    /* device instrumentation done here */
    switch (editop) {
    case OP_EDITOP_LOAD:
        break;
    case OP_EDITOP_MERGE:
        break;
    case OP_EDITOP_REPLACE:
        break;
    case OP_EDITOP_CREATE:
        break;
    case OP_EDITOP_DELETE:
        break;
    default:
        res = SET_ERROR(ERR_INTERNAL_VAL);
    }
    break;
case AGT_CB_ROLLBACK:
    /* undo device instrumentation here */
    break;
default:
    res = SET_ERROR(ERR_INTERNAL_VAL);
}

agt_record_error

NETCONF and RESTCONF use a common data structure to report errors, defined in RFC 6241#section-4.3.

Server errors are recorded with a set of APIs that allow complete control of the <rpc-error> contents.

The 'agt_record_error' function is one of the main error handling functions used in SIL or SIL-SA code.

If a specific error is not recorded by SIL code when an EDIT1, EDIT2, or EDIT3 callback is invoked. then the server will record a generic error, based on the 'status_t' returned by the SIL code.

void agt_record_error(ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_node_t *xmlnode, ncx_node_t parmtyp, const void *error_info, ncx_node_t nodetyp, void *error_path)

Generate an rpc_err_rec_t and save it in the msg.

This is the main SIL or SIL-SA API to return an <rpc-error> to the NETCONF or RESTCONF client.

Parameters:
  • scb -- [inout]

    session control block

    NULL and no stats will be recorded

    scb->stats may be updated if scb non-NULL

  • msghdr -- [inout]

    XML msg header with error Q

    NULL, no errors will be recorded!

    msghdr->errQ has error message added if not NULL and no malloc errors

  • layer -- netconf layer error occured. Used for <error-type> field.

  • res -- internal error code. Will get mapped to <error-tag> and <error-app-tag>

  • xmlnode --

    XML node causing error. Will be used for <bad-element> and maybe error <error-path>

    NULL if not available

  • parmtyp -- type of node in 'error_info'

  • error_info --

    error data, specific to 'res'. Used to generate <error-info> sub-elements.

    NULL if not available (then nodetyp ignored)

  • nodetyp -- type of node in 'error_path'

  • error_path --

    internal data node with the error. Used to generate the <error-path> field

    NULL if not available or not used

Example:

/* if error: set the res, errorstr, and errorval parms */
if (res != NO_ERR) {
    agt_record_error(scb,               // session control block
                     RPC_MHDR(msg),     // xml_msg_hdr for the request state
                     NCX_LAYER_CONTENT, // <error-type> enum
                     res,               // internal status code
                     NULL,              // XML node (parser mode)
                     NCX_NT_STRING,     // node type for error_info parm
                     errorstr,          // node value for <error-info> struct
                     NCX_NT_VAL,        // node type for the error_path parm
                     errorval);         // node for the <error-path> field
}

This API function generates an rpc_err_rec_t and save it in the message.

agt_record_error_errinfo

The agt_record_error_errinfo function can be used to report extended error information.

In order to add any extended error information to the response message, the agt_record_error_errinfo function should be used. This function is the same as the previous one if the errinfo parameter is set to NULL.

void agt_record_error_errinfo(ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_node_t *xmlnode, ncx_node_t parmtyp, const void *error_info, ncx_node_t nodetyp, void *error_path, const ncx_errinfo_t *errinfo)

Generate an rpc_err_rec_t and save it in the msg Use provided error fields.

This is the main SIL or SIL-SA API to return an <rpc-error> to the NETCONF or RESTCONF client.

Parameters:
  • scb -- [inout]

    session control block

    NULL and no stats will be recorded

    scb->stats may be updated if scb non-NULL

  • msghdr -- [inout]

    XML msg header with error Q

    NULL, no errors will be recorded!

    msghdr->errQ has error message added if not NULL and no malloc errors

  • layer -- netconf layer error occured. Used for <error-type> field.

  • res -- internal error code. Will get mapped to <error-tag> and <error-app-tag>

  • xmlnode --

    XML node causing error. Will be used for <bad-element> and maybe error <error-path>

    NULL if not available

  • parmtyp -- type of node in 'error_info'

  • error_info --

    error data, specific to 'res'. Used to generate <error-info> sub-elements.

    NULL if not available (then nodetyp ignored)

  • nodetyp -- type of node in 'error_path'

  • error_path --

    internal data node with the error. Used to generate the <error-path> field

    NULL if not available or not used

  • errinfo -- error info record to use (may be NULL)

agt_record_warning

The agt_record_warning function can be used to report a warning instead of an error.

An <rpc-error> element will still be used, however, the <error-severity> will be set to warning instead of error

void agt_record_warning(ses_cb_t *scb, xml_msg_hdr_t *msghdr, ncx_layer_t layer, status_t res, const xml_node_t *xmlnode, ncx_node_t parmtyp, const void *error_parm, ncx_node_t nodetyp, void *error_path, const ncx_errinfo_t *errinfo)

Generate an rpc_err_rec_t and save it in the msg.

Not actually supported in NETCONF protocol because no standard error-tag values are defined for severity=warning

Use the provided error info record for <rpc-error> fields Set the error-severity field to warning instead of error but only if agt_with_warnings is TRUE

Parameters:
  • scb -- [inout]

    session control block

    NULL and no stats will be recorded

    scb->stats may be updated if scb non-NULL

  • msghdr -- [inout]

    XML msg header with error Q

    NULL, no errors will be recorded! msghdr->errQ has error message added if not NULL and no malloc errors

  • layer -- netconf layer error occured. Used for <error-type> field.

  • res -- internal error code. Will get mapped to <error-tag> and <error-app-tag>

  • xmlnode --

    XML node causing error. Will be used for <bad-element> and maybe error <error-path>

    NULL if not available

  • parmtyp -- type of node in 'error_info'

  • error_parm --

    error data, specific to 'res'. Used to generate <error-info> sub-elements.

    NULL if not available (then nodetyp ignored)

  • nodetyp -- type of node in 'error_path'

  • error_path --

    internal data node with the error. Used to generate the <error-path> field

    NULL if not available or not used

  • errinfo -- error info record to use (may be NULL)

SIL Creation of Custom Error Messages And AppInfo

The server maintains data structures for error information including the ncx_errinfo_t struct.

struct ncx_errinfo_t

YANG error info statement struct used to override default error handling in the server.

Public Members

dlq_hdr_t qhdr

queue header

xmlChar *descr

description-stmt (not saved on server)

xmlChar *ref

reference-stmt (not saved on server)

xmlChar *error_app_tag

error-app-tag field

xmlChar *error_message

error-message field

boolean seen

for yangdiff

Normally this structure is static and derived from YANG statements.

It can also be provided at runtime by a SIL function using the agt_record_error_errinfo function.

This feature must be used with care because the strings within the structure and the structure itself must persist until the server has sent the error response message.

Only the error_app_tag and error_message fields will be checked. Do not set any other fields in this structure.

ncx_errinfo_t *ncx_new_errinfo(void)

Malloc and init a new ncx_errinfo_t.

Returns:

pointer to malloced ncx_errinfo_t, or NULL if memory error

void ncx_free_errinfo(ncx_errinfo_t *err)

Scrub the memory in a ncx_errinfo_t by freeing all the sub-fields, then free the errinfo struct.

Parameters:

err -- ncx_errinfo_t data structure to free

These strings are malloced (not const!) so the struct has to be setup by the SIL code.

The following example API shows how the ncx_errinfo_t struct can be created.

/* return an ncx_errinfo that must be freed with ncx_free_appinfo */
static ncx_errinfo_t *
    make_errinfo (const xmlChar *apptag,
                 const xmlChar *errmsg)
{
    ncx_errinfo_t *errinfo = ncx_new_errinfo();
    if (errinfo == nullptr) {
        return ERR_INTERNAL_MEM;
    }
    if (apptag != nullptr) {
        errinfo->error_app_tag = xml_strdup(apptag);
        if (errinfo->error_app_tag == nullptr) {
            ncx_free_errinfo(errinfo);
            return ERR_INTERNAL_MEM;
        }
    }
    if (errmsg != nullptr) {
        errinfo->error_message = xml_strdup(errmsg);
        if (errinfo->error_message == nullptr) {
            ncx_free_errinfo(errinfo);
            return ERR_INTERNAL_MEM;
        }
    }
    return errinfo;
}

SIL-SA Creation of Custom Error Messages And AppInfo

The following API provides a way to set a Custom Error message. In order to set a custom Error Message to be sent to the server the following API can be used. This API is only available to EDIT callbacks.

The following YANG module is used in this example:

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

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

  container test-silsa-errors {                      // EDIT2 CB
    presence "Presence";

    leaf test-error {
      type string;
    }
  }
}

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

In this case there is an API 'sil_sa_set_error_msg' to set up a custom error to be returned to the server.

void sil_sa_set_error_msg(rpc_msg_t *msg, const xmlChar *strval)

Set Error Message string in case of error.

Parameters:
  • msg -- rpc msg to use to find keys

  • strval -- string value to use for error message


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

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

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

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

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

    switch (cbtyp) {
    case AGT_CB_VALIDATE:
        /* description-stmt validation here */
        break;
    case AGT_CB_APPLY:
        /* database manipulation done here */
        break;
    case AGT_CB_COMMIT:
        /* device instrumentation done here */
        switch (editop) {
        case OP_EDITOP_LOAD:
            break;
        case OP_EDITOP_MERGE:
            break;
        case OP_EDITOP_REPLACE:
            break;
        case OP_EDITOP_CREATE:

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

                /* TRIGGER ERROR */
                res = ERR_NCX_OPERATION_NOT_SUPPORTED;

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

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

} /* u_err_test_silsa_errors_edit */

Adding <error-info> Data to an Error Response

The NETCONF <rpc-error> and RESTCONF <errors> data structures allow error data to be reported to the client. This data can be located in a container called “error-info”. Typically, the server adds an “error-number” leaf to this container, but there are several NETCONF errors that require specific error data to be returned.

Step 1) Define the error-info Data

The new API functions require the following H files, which need to be included into the SIL C file:

#include “agt_util.h”
#include “rpc.h”

The data returned to an <error-info> record should be defined in a real YANG module.

In the example below, an abstract leaf called 'testdata' is defined:

leaf testdata {
  ncx:abstract;
  type int32;
  description
    "Example of template for user <error-info> data";
}

Step 2) Create a Data Node and Add it to the Error Response.

The 'rpc_msg_add_error_data' function can be used to add custom <error-info> child nodes to an error response.

Only SIL Callbacks Can Use This Function
void rpc_msg_add_error_data(rpc_msg_t *msg, val_value_t *val)

Add error data to the response message.

Used by the server only

Add a val_value_t node that will be used in the error response sent to the client

Parameters:
  • msg -- rpc_msg_t to add data into

  • val -- malloced error data node to add to error record error-info !!! This value node will be consumed and freed later !!!

The data created must be malloced so it can be freed later with the 'val_free_value' function.

The example from sil_error/src/sil-error.c shows an instance of testdata being created:

/********************************************************************
* FUNCTION add_user_error_data
*
* Add an example error-info data node to the error that is returned
* to the client
*
* INPUTS:
*    msg == RPC message to add user error data
* RETURNS:
*   status
********************************************************************/
static status_t
    add_user_error_data (rpc_msg_t *msg)
{
    /* get the abstract leaf template */
    obj_template_t *testdata_obj =
        obj_find_template_top(sil_error_mod,
                              sil_error_mod->name,
                              (const xmlChar *)"testdata");

    if (testdata_obj == NULL) {
        return ERR_NCX_DEF_NOT_FOUND;
    }

    /* a real add_user_data callback would probably get a system
     * value instead of a constant; use constant here
     */
    const xmlChar *valuebuff = (const xmlChar *)"42";

    status_t res = NO_ERR;
    val_value_t *testdata_val =
        val_make_simval_obj(testdata_obj, valuebuff, &res);

    if (testdata_val) {
        rpc_msg_add_error_data(msg, testdata_val);
    }

    return res;

}  /* add_user_error_data */

This step is done BEFORE the agt_record_error (or other variant) is called by the SIL code:

if (res != NO_ERR) {

     /* add user data to the message before calling agt_record_error */
     (void)add_user_error_data(msg);

     agt_record_error(
         scb,
         &msg->mhdr,
         NCX_LAYER_CONTENT,
         res,
         NULL,
         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
         errorval,
         (errorval) ? NCX_NT_VAL : NCX_NT_NONE,
         errorval);
 }

Example Error Responses

The “sil-trigger” object is used to show how error-info data is added to the error response.

The error is triggered by setting /sil-phase to apply and /sil-trigger to any value. This will force an error to generated upon the appropriate edit.

NETCONF Error Example

> merge /sil-trigger value=2

This might cause the following <rpc-error> response in NETCONF:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply message-id="4" trace-id="4"
 xmlns:silerr="http://www.netconfcentral.org/ns/sil-error"
 xmlns:ncx="http://netconfcentral.org/ns/yuma-ncx"
 xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <rpc-error>
  <error-type>application</error-type>
  <error-tag>operation-failed</error-tag>
  <error-severity>error</error-severity>
  <error-app-tag>general-error</error-app-tag>
  <error-path>/silerr:sil-trigger</error-path>
  <error-message xml:lang="en">operation failed</error-message>
  <error-info>
   <error-number>274</error-number>
   <testdata xmlns="http://www.netconfcentral.org/ns/sil-error">42</testdata>
  </error-info>
 </rpc-error>
</rpc-reply>

RESTCONF Error Example

The following example shows how RESTCONF will return the user error data:

If any errors occur while attempting to invoke the operation, then an "errors" data structure is returned with the appropriate error status.

XML Example

The client might send the following POST request message to invoke a "reboot" operation:

POST /restconf/operations/example-ops:reboot HTTP/1.1
Accept: application/yang-data+xml
Content-Type: application/yang-data+xml

<input xmlns="https://example.com/ns/example-ops">
  <delay>-33</delay>
  <message>Going down for system maintenance</message>
  <language>en-US</language>
</input>

The server might respond with an "invalid-value" error in default XML format:

HTTP/1.1 400 Bad Request
Date: Mon, 25 Apr 2012 11:10:30 GMT
Server: example-server
Content-Type: application/yang-data+xml

<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
  <error>
    <error-type>protocol</error-type>
    <error-tag>invalid-value</error-tag>
    <error-path xmlns:err="https://example.com/ns/example-ops">err:input/err:delay</error-path>
    <error-message>Invalid input parameter</error-message>
  </error>
</errors>

JSON Example

If the client specifies application/yang-data+json in the Accept header entry, the server will respond in JSON format.

A lock-denied error might be sent as follows:

HTTP/1.1 409 Conflict
Date: Mon, 23 Apr 2012 17:11:00 GMT
Server: example-server
Content-Type: application/yang-data+json

{
   "ietf-restconf:errors": {
     "error": [
       {
         "error-type": "protocol",
         "error-tag": "lock-denied",
         "error-message": "Lock failed, lock already held"
       }
     ]
   }
 }