Using DAX to Read Configurations

SDK developers can both create new commands to add new functionality to the JUNOS system (see Creating a User Interface with DDL and ODL) and read the JUNOS system commands and configuration parameters that allow users to control the system.

This topic explains how to write the code that the system invokes during the config_read process invoked as part of initialization; for details, see Initializing an Application.

Note:
User interface functionality is available only on the Routing Engine. In an application written for the Multiservices PIC, the user interface code runs on the Routing Engine and the Routing Engine communicates with the Multiservices PIC(s)
Your callback code can specify a boolean check parameter, which the system uses to indicate whether it is in the process of checking the configuration. For example:

int ped_config_read (int check)
  ...
  if (!check ){
    <write error message to log>
  }
    
 

Your code should not write any files to the file system or perform any runtime verifications that depend on the current router state (for example, looking at what interfaces are available) if check is true.

To read the system configuration, you use the DDL Access (DAX) library, a set of function calls that access the object tree that defines the system objects (also called the object database). Applications use the DAX library to check the configurations provided by the user and to configure internal state once the configuration is accepted. The functions are located in your backing sandbox in sandbox/src/junos/lib/ddl/access.

For details about DDL, ODL, and DAX, see the UI Programmer's Guide, the ODL Guide, and the DDL Guide, all provided with this documentation set.

Writing DAX Code

To read the configuration values that have been specified in DDL for your system, you use the DAX API function calls to "walk" the nodes of the database tree and retrieve the values through the pointers.

The DAX accessor function you call depends on what information the caller has: a name as specified in the schema, an attribute ID (aid), and so on. Two types of pointers, which appear as function parameters, facilitate this data retrieval:

How to Use the DAX Functions to Retrieve Values

A daemon always reads the database in response to an event, typically the following:

There are three major ways to obtain data from the database. The simplest way, preferred if possible, is to use names. For example:

dax_get_string_by_name(dop, DDL_SYSTEM_HOSTNAME, &hostname);

To use names, you must first know the name of the attribute in the schema.

To retrieve an entity that is inside a list, you must iterate through the list. There are three ways to do this:

You can iterate by writing a loop in which you call dax_visit_container(). This function visits every node in the container (which is expensive when there is a very large number of nodes). At each node, you call a get-by-name function, which returns a cop (child object pointer, equivalent to the dop for that entity).

You have control of the looping, but you also have more responsibility: if you break out of the loop, you must deallocate the cop:

    while (dax_visit_container()) {
        if (some error)
             break;
    }

    dax_release_object(&cop);

The cop is automatically deallocated during iteration if each loop completes.

Another approach is to use the dax_walk_list() function. In this model, you provide a callback function that performs the same functions you would have performed inside the loop in the old model. At each node, the dax_walk_list() function calls your code back, and you do the necessary work in your own process. dax_walk_list() performs all the iteration, and it also performs the allocation and deallocation of the cop. You give up some control, but there is less bookkeeping for you to write.

The dax_walk_list() function can process nodes that have been changed or deleted in several ways. It can walk the following:

A daemon walks the configuration in order to update its internal state, so it is efficient to examine only what has been deleted, added, or changed. You pass flags to indicate which option you want.

Your callback function contains a switch statement specifying the actions to take depending on which case applies (changed, deleted, unchanged).

Finally, you can eliminate a large amount of coding by using dax_query(). With this function, instead of the nested for loops that would have been required to walk two, three, or more levels of the database hierarchy, you pass in a query that specifies the entire path to be examined. dax_query() traverses the database recursively and invokes your callback at each qualifying node.

For example, consider getting all the IP addresses configured on the router in the following path:

        interfaces
            unit
                family inet
                    address

In one approach, you would create nested loops:

        for all interfaces
                for all units
                        get family-inet
                                get address

Instead, with dax_query(), you create a query to iterate over all IPv4 addresses:

   interfaces $if unit $uint family inet address <*>

You pass this query to dax_query(), and it invokes the callback on each node. The callback mechanism and switch statement are the same as for dax_walk_list(): in fact, dax_query() calls a dax_walk() function internally.

The callback can optionally retrieve the interface dop. (Typically, the callback would need to know to which interface it should bind an address.)

In the query, the $ symbol represents a variable. The variable is typically used to bind to a container item. The <*> symbol represents a wildcard and is used if the caller does not need to retrieve the dops at the time of the callback.

Code Examples

This section contains code examples for some basic DAX tasks.

Setting up a Daemon to Read the Configuration

You open and close the configuration database as follows:

#include <feature/jexample_sequence.h>
#include <feature/jexample_out.h>

boolean
mydaemon_read_config (boolean do_check) 
{
    const char *err;
    boolean status;
    int myint;

    err = dax_open_db(NULL, DAE_MYDAE, PACKAGE_NAME, SEQUENCE_NUMBER, do_check);
    if (err) {
        dax_error(NULL, "Opening configuration database: %s", error);
        return FALSE;
    }

    status = mydaemon_config_knobs(do_check);

    err = dax_close_db());
    if (err) {
        dax_error(NULL, "Closing configuration database: %s", err);
    }

    return status;
}

Fetching Values Using the dax_get() Functions

The following is an example of fetching values. Also see the documentation for all dax_get_ functions in the JUNOS SDK Reference Library documentation.

/*
 * Reads configuration under [system knobs1].  If  do_check is
 * TRUE, we simply do a check, otherwise we configure the knobs.
 */
boolean
mydaemon_config_knobs (boolean do_check)
{
    int knob_int;
    char knob_str[KNOB_STR_SZ];
    const char *knob_path[] = { "system", "knobs", NULL };
    ddl_handle_t *dop;

    if (!dax_get_object_by_path(NULL, knob_path, &dop, FALSE)) {
        if (!do_check) {
            /*
             * knobs not configured, do something about it.
             */
        }

        return TRUE;
    }
    
    knob_int = DEFAULT_KNOB_INT;
    dax_get_int_by_name(dop, "knob-int", &knob_int);

    strncpy(knob_str, DEFAULT_KNOB_STR, sizeof(knob_str));
    dax_get_stringr_by_name(dop, "knob-str", &knob_str, sizeof(knob_str));

    dax_release_object(&dop);

    if (do_check)
        return TRUE;

    /*
     * Not a check set internal state.
     */
    mydaemon_set_knob(knob_int, knob_str);

    return TRUE;
}

Walking a List

Lists, also called containers, are defined in the DDL as type setof xxx_object by the flag setof statement. The delta-list flag is required if you want mgd to store delete information. Otherwise, the configuration UI treats a delete operation as if you had deleted the entire list and recreated it: that is, it sends a DAX_ITEM_DELETE_ALL message, then a DAX_ITEM_CHANGED message for all configured items.

For example, the following is a DDL file that configures keys for some configuration operations.

knob.cnf.ddl file:

  object knob-keys {
      flag autosort list delta-list;

      attribute knob-key-name {
          flag nokeyword identifier;
          type string;
      }

      attribute key-string-value {
          type string;
      }
  }

To gather the key-string-values for the knob-keys list, you can use the dax_walk_list() function. The following example walks the list of knob-keys using dax_walk_list(). ( Note that error checking is not implemented, to keep the code example small.)

/*
 * Callback to handle each knob-key
 */
static int
knobs_walk_keys_cb (dax_walk_data_t *dwd, ddl_handle_t *dop, int action, void *data)
{
    ddl_node_t *dnp;
    char knob_key_name[KNOB_NAME_SZ];

    /*
     * Grab ident of knob_key
     */
    knob_key_name[0] = '\0';
    if (dop)
        dax_get_stringr_by_ident(dwd, dop, 0, key_name, sizeof(key_name));
    
    switch (action) {
    case DAX_ITEM_DELETE_ALL:
        /*
         * All items under knobs-keys were deleted handle it.
         */
        break;

    case DAX_ITEM_DELETE:
        /*
         * One item got deleted
         */
        break;

    case DAX_ITEM_CHANGED:
    case DAX_ITEM_UNCHANGED:
        /*
         * One item changed, get the identifier and handle.
         */
        if (knob_key_name[0]) {
            if (dax_get_string_by_name(dop, "knob-string-value", &key_str)) {
                knob_configure(knob_key_name, key_str);
            }
        }
        break;

    default:
        break;
    }

    return DAX_WALK_OK;
}

boolean
mydaemon_read_knobs (ddl_handle_t *dop)
{
    /*
     * Walks the delta list for knobs
     */
    dax_walk_list(dop, DAX_WALK_DELTA, knobs_walk_keys_cb, NULL);

    return TRUE;
}

2007-2009 Juniper Networks, Inc. All rights reserved. The information contained herein is confidential information of Juniper Networks, Inc., and may not be used, disclosed, distributed, modified, or copied without the prior written consent of Juniper Networks, Inc. in an express license. This information is subject to change by Juniper Networks, Inc. Juniper Networks, the Juniper Networks logo, and JUNOS are registered trademarks of Juniper Networks, Inc. in the United States and other countries. All other trademarks, service marks, registered trademarks, or registered service marks are the property of their respective owners.
Generated on Sun May 30 20:26:47 2010 for Juniper Networks Partner Solution Development Platform JUNOS SDK 10.2R1 by Doxygen 1.4.5