Chapter 6 - Configuration Mode Examples

UI Infrastructure Programmer's Guide

Chapter 6 - Configuration Mode Examples

In this chapter, an example, 'jnx-example-data' is taken from the JUNOS package, jnx-example. The DDL input is explained in section 6.1.1.

Additionally, jnx-example is augmented with a new function and 2 attributes are added, one for users to enter a valid username (string-type) and one for a host-address (ipaddr-type).

The dax_ access function to read the added configuration at commit time is included in section 6.2.

6.1 Example: example-data

6.1.1. DDL input

As mentioned in chapter 2, there are run-mgd-bsd and run-cli-bsd available for easier UI development. In this chapter, we are going create some configuration mode commands and in the next chapter, some operational mode commands, using some common DDL keywords.

jnx-exampled is used for testing purposes. Objects 'traceoptions' and 'jnx-example-data' are already defined under object 'jnx-example'. We are going to first explain how the objects and attributes defined under jnx-example-data work, and then add some more data structure.

    object jnx-example {
        help "Example service configuration";
        flag remove-empty;
        notify jnx-exampled;
        define DDLAID_JNX_EXAMPLE;
        require jnx-example;

        /* This node and its subnodes require jnx-exampled to be up
           and running */
        action enable-process DNAME_JNX_EXAMPLED;

        object traceoptions {
            ....
        }

        /*  
           Dummy configuration data
         */
        object jnx-example-data {
            help "Example data entries";
            flag setof list;

            attribute data-name {
                flag identifier nokeyword;
                help "Example data identifier";
                type ranged string 1..127;
                cname "ex_data_name";
            }

            attribute "description" {
                help "General description of data";
                type ranged string 1..127;
                cname "ex_data_desc";
            }

            attribute "type" {
                help "Example data type";
                cname "ex_data_type";
                type enum uint {
                    choice none {
                        help "No type";
                        define JNX_EXAMPLE_DATA_TYPE_NONE;
                    }
                    choice fat {
                        help "Fat data";
                        define JNX_EXAMPLE_DATA_TYPE_FAT;
                    }
                    choice skinny {
                        help "Skinny data";
                        define JNX_EXAMPLE_DATA_TYPE_SKINNY;
                    }
                }
                default skinny;
            }

            attribute "value" {
                help "Example data value";
                type ranged string 1..127;
                cname "ex_data_value";
            }
        }
    }

Under 'jnx-example', the first level has 'traceoptions' and 'jnx-example-data'.

        user@router# set jnx-example ?                         
        Possible completions:
        + apply-groups         Groups from which to inherit configuration data
        + apply-groups-except  Don't inherit configuration data from these groups
        > jnx-example-data     Example data entries
        > traceoptions         Example service trace options
        [edit]
        user@router#

The object 'jnx-example-data' has 'flag setof list' and the first attribute, 'data-name' has 'flag identifier nokeyword'. Hence, 'jnx-example-data' may consist of multiple instances, which are identified by the names of the various <data-name> provided by users. The other 3 attributes, 'description', 'type', and 'value', defined at the same level as 'attribute data-name' in the input file, are one level below the data-name instances.

        user@router# set jnx-example jnx-example-data first ?
        Possible completions:
          <[Enter]>            Execute this command
        + apply-groups         Groups from which to inherit configuration data
        + apply-groups-except  Don't inherit configuration data from these groups
          description          General description of data
          type                 Example data type
          value                Example data value
          |                    Pipe through a command
        [edit]
        user@router#

Under 'jnx-example-data', there are the 3 attributes:

'description': a string that is between 1 to 127 characters long 'type' : an enumerated unsigned-integer identified by 'none', 'fat' and 'skinny' and 'skinny' is the default value 'value' : a string that is between 1 to 127 characters long

        user@router# set jnx-example-data first value 3

        [edit]
        user@router# set jnx-example-data second type fat

        [edit]
        user@router# commit
        commit complete

        [edit]
        user@router# show jnx-example
        jnx-example-data first {
            value 3;
        }
        jnx-example-data second {
            type fat;
        }

        [edit]
        user@router#

Let's make some changes to see what the other knobs do.

During the display, each jnx-example-data instance has the keyword 'jnx-example-data' preceding the instance name. By adding the 'flag homogeneous', we can group all the instance together and change the presentation.

...src/junos/lib/ddl/jnx-exampled.cnf.dd:

        object jnx-example-data {
            help "Example data entries";
            flag setof list;
            flag homogeneous;

            attribute data-name {
                flag identifier nokeyword;
                help "Example data identifier";
                type ranged string 1..127;
                cname "ex_data_name";
            }

            ....
        }

CLI:
        user@router# show jnx-example
        jnx-example-data {
            first {
                value 3;
            }
            second {
                type fat;
            }
        }

        [edit]
        user@router#

6.2. Example: allowed-user

6.2.1. DDL input

Next, we will add an 'allowed-user' object, and 2 different attributes, 'username' and 'host-address'. If neither 'username' nor 'host-address' is defined, it is better to have 'allowed-user' removed because by itself, it does not have any meaning.

'username' is a string field which contains the name of only one user who uses services provided by example. usernames are limited to 127-char long.

'host-address' is an ipv4 address field that contains the ip-address of the workstation that are allowed to access this JUNOS device. If 'host-address' is specified, 'username' has to be defined.

The resulting configuration looks like this:

        user@router# show jnx-example
        allowed-user {
            username user;
            host-address 172.16.21.1;
        }

        [edit]
        user@router#

To achieve these configurations,

...src/junos/lib/ddl/input/jnx-exampled.cnf.dd

        object jnx-example {
            ....

            object allowed-user {
                help "Example user entries";
                flag remove-empty;

                attribute username {
                    help "User ID";
                    type ranged string 1 .. 127;
                }

                attribute host-address {
                    help "Host IP Address";
                    must (".. username");
                    must-message "username has to be defined";
                    type ipv4addr;
                }
            }
        }

Testing the new configuration

        $ run-mgd-bsd&
        Running  /tmp/user/run/8.4/test/mgd-bsd
        runmgd: commit complete
        $ run-cli-bsd
        Running  /tmp/rcheh/run/8.4/test/usr/sbin/cli

        user@router> configure

        [edit]
        user@router# set jnx-example allowed-user host-address 1.2.3.4

        [edit]
        user@router# show jnx-example
        allowed-user {
            ##
            ## Warning: username has to be defined
            ##
            host-address 1.2.3.4;
        }

        [edit]
        user@router# set jnx-example allowed-user username euser

        [edit]
        user@router# commit
        commit complete

        [edit]
        user@router# show jnx-example
        allowed-user {
            username euser;
            host-address 1.2.3.4;
        }

        [edit]
        user@router#

6.3 Accessing Configuration Database using DAX

For jnx-exampled, function exampled_config_read in jnx-exampled_config.c is used to read the configuration database during commit time if any configuration pertaining to the jnx-example section is changed (i.e. notify-bit for jnx-exampled is set).

...src/junos/usr.sbin/jnx-exampled/jnx-exampled_config.h

        #define EX_USER_STR_SIZE 127
        typedef struct exampled_user_s {
            char                username[EX_USER_STR_SIZE];
            struct in_addr      addr;
        } exampled_user_t;
        exampled_user_t ex_user;

...src/junos/usr.sbin/jnx-exampled/jnx-exampled_config.c

        exampled_user_t ex_user;

        boolean
        exampled_config_read (boolean check)
        {
            ddl_handle_t *top = NULL;
            ddl_handle_t *dop = NULL;
            const char   *ex_user_config_name[] = { DDLNAME_EXAMPLE,
                                                    DDLNAME_EXAMPLE_ALLOWED_USER,
                                                    NULL };
            const char   *ex_data_config_name[] = { DDLNAME_EXAMPLE,
                                                    DDLNAME_EXAMPLE_EXAMPLE_DATA,
                                                    NULL };
            const char   *ex_config_name[] = { DDLNAME_EXAMPLE, NULL };
            char          ex_name[EX_DATA_STR_SIZE];
            char          descr[EX_DATA_STR_SIZE];
            char          value[EX_DATA_STR_SIZE];
            u_int32_t     type, status = TRUE;

            /*
               read trace configuration
             */
            junos_trace_read_config(check, ex_config_name);

            /*
               read example allowed-user configuration
             */
            if (dax_get_object_by_path(NULL, ex_user_config_name, &top, FALSE)) {

                if (dax_is_changed(top) == FALSE) {
                /* if top hasn't changed, ignore this config object */
                    goto next;
                }

                while (status && dax_visit_container(top, &dop)) {
                    if (dax_get_stringr_by_name(dop, 
                        DDLNAME_JUNIPER_CONFIG_EXAMPLE_ALLOWED_USER_USERNAME,
                        ex_user->username, 
                        sizeof(ex_user.username) ) == FALSE) {
                        ex_user->username[0] = '\0';
                    }
                    if (dax_get_ipaddr_by_name(dop, 
                        DDLNAME_JUNIPER_CONFIG_EXAMPLE_ALLOWED_USER_HOST_ADDRESS,
                        ex_user->addr, 
                        sizeof(ex_user.addr) ) == FALSE) {
                        ex_user->addr = 0;
                    }
                }
            } else {
                ex_user_clear();
            }

        next:
            /*
               read example data configuration
             */
            if (dax_get_object_by_path(NULL, ex_data_config_name, &top, FALSE)) {

                if (dax_is_changed(top) == FALSE) {
                    /* if top hasn't changed, ignore this config object */
                    goto exit;
                }

                /* clear stored config data, & repopulate with new config data */
                ex_data_clear();

                while (status && dax_visit_container(top, &dop)) {
                    if (dax_get_stringr_by_aid(dop, EX_DATA_NAME, ex_name,
                                               sizeof(ex_name) ) == FALSE) {
                        /* this attribute is mandatory -- got to have an index */
                        status = FALSE;
                    }

                    if (dax_get_stringr_by_aid(dop, EX_DATA_DESC, descr,
                                               sizeof(descr)) == FALSE) {
                        /* if not configured, set the value to NULL */
                        descr[0] = '\0';
                    }

                    if (dax_get_stringr_by_aid(dop, EX_DATA_VALUE, value,
                                               sizeof(value)) == FALSE) {
                        /* if not configured, set the value to NULL */
                        value[0] = '\0';
                    }

                    if (dax_get_uint_by_aid(dop, EX_DATA_TYPE, &type) == 0) {
                        /*
                           this value has a default defined, so there must be
                           something configured
                         */
                        status = FALSE;
                    }

                    if (status) {
                        /*
                           add ex_data to patricia tree
                         */
                        ex_data_add(ex_name, descr, value, type);
                    }
                }           
            } else {
                ex_data_clear();
            }

        exit:
            if (top) {
                dax_release_object(&top);
            }

            if (dop) {
                dax_release_object(&dop);
            }
            return status;
        }

This function reads the configuration section under 'jnx-example' in the database. It first reads 'traceoptions' configured under 'jnx-example', with junos_trace_read_config(), followed by the 'example-data' settings, which is done inside this function.

As one can see, the first step to read the 'example-data' configuration is to set top to point to the beginning of example-data using dax_get_object_by_path().

Then, dax_is_changed() is called to checked whether 'example-data' configuration has been modified since the last commit. If there is, dax_visit_container() is called to point dop to the new 'example-data' settings. Then, 'example-data name', and the corresponding 'description', 'value', and 'type' settings are read and stored to the patricia tree using ex_data_add().

Using the patricia tree is one way to store the user configuration settings for the daemons. For 'allowed-user', we store it in a structure, exampled_user_t, which contains a char-array (username) and an struct ip_addr (addr).

Instead of dax_get_ by aid, dax_get_stringr_by_name and dax_get_ipaddr_by_name are used. Depending whether 'allowed-user username' and 'allowed-user host-address' are defined, the values are stored in the memory or cleared.


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:23:10 2010 for JUNOS UI Programmer's Guide by Doxygen 1.4.5