Creating a User Interface with DDL and ODL

This topic provides an overview of how an SDK developer can use the Data Definition Language to create user-interface commands and store them as database objects, use the Output Description Language to create XML tags for the command output.

This topic also shows how to test your DDL and ODL interface in your build environment using the sdk-ui-sim tool.

Note:
User interface functionality is available only on the Routing Engine. In a Services SDK application, the user interface code runs on the Routing Engine and the Routing Engine communicates with the Multiservices PIC(s).

Using DDL

To create new commands, you use the Data Definition Language (DDL). DDL defines the syntax, options, and execution details for all operational commands and configuration statements. You specify the structure on which all of the commands and configuration statements are based in the DDL files and the system follows it when executing commands and building the configuration database. That structure, an object tree, is the basis for all DDL functionality.

DDL definitions can be written for commands in two modes:

You define your configuration commands in a file with the suffix .cnf.dd and your operational commands in a file with the suffix .cmd.dd, and save these files in your development sandbox at sandbox/src/lib/ddl/input.

DDL Example

The following DDL defines database objects for the show ifinfod interface aliases command, which returns all configured aliases the administrator defined for interfaces, together with the physical interface names to which each alias corresponds.

 command aliases {
                     help "Show ifinfo interfaces aliases";
                     action execute DNAME_JNX_IFINFOD;
                 }
                argument level {
                    flag nokeyword explicit;

                    type enum int {
                        choice detail {
                            help "Display detailed information";
                            value OPT_DETAIL;
                        }
                        choice brief {
                            help "Display brief information (default)";
                            value OPT_BRIEF;
                        }
                        choice summary {
                            help "Display summary information ";
                            value OPT_SUMMARY;
                        }
                        choice up {
                            help "Display interfaces that are physically UP";
                            define OPT_UP;
                        }
                        choice down {
                            help "Display interfaces that are physically DOWN";
                            define OPT_DOWN;
                        }
                    }
                    default brief;
                }

                argument interface-name {
                    flag nokeyword;
                    type string;
                    help "Name of physical interface or alias";
                }
            }
        }
    }

Your application code includes a function to handle the command and output the results to the user; that code is shown later in this topic in ODL Example.

Using ODL

The command-line interface (CLI) communicates with the management daemon (mgd) using JUNOScript, an implementation of XML designed specifically for describing the configuration and status of Juniper Networks routers. The API implementation programs the JUNOS modules to generate XML-tagged output rather than formatted ASCII output. The JUNOS Output Description Language (ODL) defines the tags to generate.

You write ODL definitions to define an XML Document Type Definition (DTD) for the data elements that represent router configuration and status information, and save the file with the suffix .odl in sandbox/src/lib/odl/input in your development sandbox.

The ODL compiler is a program called .odc. When you include the appropriate options on the odc command line (usually in the makefile that builds your application), the compiler generates the library files and other data needed by various programs. There are three types of output:

ODL Example

The following is the ODL definition for a show ifinfod interface aliases command, which returns all configured aliases the administrator defined for interfaces, together with the physical interface names to which each alias corresponds. The DDL definitions to create the database objects in this example are shown in DDL Example.

tag jnx-ifinfo-interfaces-aliases {
    flag root;
    tag jnx-ifinfo-aliases {
        flag multiple;
            tag jnx-ifinfo-interface-name {
                type string;
            }
            tag jnx-ifinfo-interface-status {
                type string;
            }
            tag jnx-ifinfo-interface-alias {
                type string;
            }
    }
    format jnx-ifinfo-interfaces-aliases-format {
        header "InterfaceName   Status       Alias
";
        picture "@<<<<<<<<<<<<<    @<<<<<<    @<<<<<<<<<<<<
";
        line { 
            field jnx-ifinfo-interface-name;
            field jnx-ifinfo-interface-status;
            field jnx-ifinfo-interface-alias;
        }
    }
}

User Interface Processing in the Application

When the makefile that builds the application runs the ODL compiler, the compiler generates variables for the tags, which you then reference in your application code.

In your code, each function handling a command must return JUNOScript (XML) for communicating with mgd through the management socket. There is an API to help write the JUNOScript response; the API is available in your backing sandbox at src/junos/lib/junoscript/h/junoscript/xmlrpc.h.

For example, the following function in your application is invoked when the user enters show ifinfo interface aliases in the CLI. Note that the ODL compiler capitalizes the tag names and prepends the identifier ODCI_, and your code must reference the tag names this way.

The complete syntax and usage information for the macros shown here (such as XML_OPEN, XML_ELT, and XML_CLOSE) is available in the ODL Guide included with this documentation set.

static int
show_ifinfod_aliases (mgmt_sock_t *msp, parse_status_t *csb __unused,
		      char *unparsed __unused)
{
    ifinfod_intf_t *ifinfod_node;

    ifinfod_create_socket(LOW_PORT);
    ifinfod_create_socket(HIGH_PORT);

    XML_SUBTREE_OPEN(msp, ODCI_JNX_IFINFO_INTERFACES_ALIASES);

    for (ifinfod_node = ifinfod_node_first(); ifinfod_node ;
	 ifinfod_node = ifinfod_node_next(ifinfod_node)) {

        XML_OPEN(msp, ODCI_JNX_IFINFO_ALIASES);
        XML_ELT(msp, ODCI_JNX_IFINFO_INTERFACE_NAME,
		                   "%s", ifinfod_node->intf_name);

        switch (ifinfod_node->intf_status) {
            case 0 :
                XML_ELT(msp, ODCI_JNX_IFINFO_INTERFACE_STATUS, "%s", "DOWN");
                break;
            case 1 :
                XML_ELT(msp, ODCI_JNX_IFINFO_INTERFACE_STATUS, "%s", "UP");
                break;
            default :
                XML_ELT(msp, ODCI_JNX_IFINFO_INTERFACE_STATUS, "%s", "UNKOWN");
                break;
        }

        XML_ELT(msp, ODCI_JNX_IFINFO_INTERFACE_ALIAS,
		"%s", ifinfod_node->intf_alias_name);
        XML_CLOSE(msp, ODCI_JNX_IFINFO_ALIASES);
    }

        XML_CLOSE(msp, ODCI_JNX_IFINFO_INTERFACES_ALIASES);
   
  return 0;
}
   

Testing the User Interface

In addition to building the whole JUNOS package and loading it on a real JUNOS router, you can use the JUNOS SDK UI simulator package (junos-sdk-ui-sim-release.tgz) to test new UI commands in your build environment.

Note:
The following information supercedes the documentation for run-cli-bsd and run-mgd-bsd in the UI Programmer's Guide.
sdk-ui-sim is a shell script that calls a version of mgd and the CLI built to run in your build environment. You install the script separately from your JUNOS software. For example:

user@host% pkg_add -R -p /usr/sandboxes/mysb/ junos-sdk-ui-sim-10.2I20100121_0607-signed.tgz
pkg_add: package junos-sdk-sb has no origin recorded
pkg_add: package junos-sdk-toolchain has no origin recorded
Verified MD5 checksum of junos-sdk-ui-sim-10.2I20100121_0607.tgz
Installing in /usr/sandboxes/mysb/
user@host% cd junos-sdk/ui-sim-10.2I20100121_0607/
user@host% ls -lrt
total 10
-r-xr-xr-x  1 user  group  3711 Jan 19 00:48 sdk-ui-sim
-r--r--r--  1 user  group  1103 Jan 20 05:42 ui-sim-mtree.dist
drwxr-xr-x  3 user  group   512 Jan 21 00:32 etc
drwxr-xr-x  4 user  group   512 Jan 21 00:32 usr

If you do not specify an install path with the -p option, the script appears by default in /usr/local/junos-sdk/ui-sim-release/ (These instructions show the location where you installed ui-sim as sim-dir.)

To test DDL and ODL, the script needs the following:

The syntax for running sdk-ui-sim is as follows:

$./sdk-ui-sim -p path-to-test-dir -x [start/stop]

path-to-test-dir cannot exceed 108 characters.

For example:

$./sdk-ui-sim -p /usr/sandboxes/mysb/test-dir -x start

When the script runs, you should see the standard CLI user interface.

Sample basic output is as follows:

user@host% ./sdk-ui-sim -p /usr/sandboxes/mysb/test-dir -x start

./config missing (created)
./etc/db missing (created)
./etc/db/pkg missing (created)
./var missing (created)
./var/db missing (created)
./var/db/config missing (created)
./var/db/scripts missing (created)
./var/etc missing (created)
./var/etc/filters missing (created)
./var/log missing (created)
./var/run missing (created)
./var/run/db missing (created)
./var/run/db/private missing (created)
Running  /usr/sandboxes/mysb/test-dir/test/usr/sbin/mgd-bsd
mgd: commit complete
Running  /usr/sandboxes/mysb/test-dir/test/usr/sbin/cli

At this point, you can use the normal CLI commands. The following sections show how to test your own commands.

Note:
For more information about the build environment referenced here, see Build and Packaging Procedure and Updating System Tables to Describe Your Application. For details about DDL and ODL and the architecture behind them, see the UI Programmer's Guide, the DDL Guide, and the ODL Guide, which are provided with this documentation set. For additional introductory and conceptual information, see System and User Interface Configuration in the Fundamentals topics, and Creating the User Interface, in the Hello World code example.

Testing DDL Commands

Follow these steps to test your DDL commands.

  1. Add the new DDL source (.dd) file and makefile to sandbox/src/lib/ddl/input .

  2. Add your daemon XML file to sandbox/src/lib/ddl/feature .

  3. In sandbox/src, compile the DDL using the command mk hostuilibs.

  4. Copy the generated DDL library, libprovider-prefix-appname-dd.so and the generated .dml file to sim-dir/ui-sim-release/usr/lib/dd.

  5. Start sdk-ui-sim.

DDL Testing Example

The following example shows a sample DDL source file, a daemon XML file, and a build makefile, followed by the steps the user takes to test the code.

test-example.cnf.dd:

#include "common_include.dd"
#include "trace_include.dd"

#define DNAME_TEST_EXAMPLE "test-example"

daemon-names test-example;

object juniper-config {
	flag no-struct;
    
	object test-example {
		help "Test Example";
		flag remove-empty;
		notify DNAME_TEST_EXAMPLE;
	
 		/* init test-example daemon if this config exists */
		action enable-process DNAME_TEST_EXAMPLE;
	
		object test-string {
			help "Test string value";
	    
			attribute hello {
				help "String value";
				type string;
			}
		}
	
		object test-range-number {
			help "Test input types";
			attribute number {
				help "Test number with ranged statement";
				type ranged int 1 .. 511;
			}
		
 	
		/*
		* Trace options configuration:
		*/
	
		object traceoptions {
			help " Trace options";
			require trace;
			flag remove-empty;
	    
			/* From trace_include.dd */
			TRACEOPTIONS_FILE;
			SYSLOG_ATTRIBUTE;
			TRACEOPTIONS_LEVEL_ATTRIBUTE(hidden internal);
	    
		object "flag" {
			help "Tracing flag parameters";
			flag setof list;
			flag oneliner; /* put name on same line in output */
			attribute flag-name {
				flag identifier nokeyword;
					type enum uint {
						choice "all" {
							help "Trace everything";
							define TEST_EXAMPLE_TRACEFLAG_ALL;
							}
						}
					}
				}
			}
		}
    
		object test-dep {
			help "Testing dependencies";
			must ("test-group gid");
			must-message "To configure test-dep, test-group gid must be set";
		}
    
		object test-group {
			help "test-group section";
	
		object gid {
			help "Group ID";
	    
			attribute gname {
				help "Group Name";
				type string;
			}
		
			attribute id {
				type enum int {
					choice apg {
						help "APG user";
						value 1;
					}
					choice ipg {
						help "IPG user";
						value 2;
						}
					}
				}
			}
		}
	}

test-example.input.mk:

# File used to define the daemon to mgd for system startup and
# control
DAEMON_XML += test-example.xml

# The name of the feature that the ddl library will be created for
INPUT_FEATURES += ${PROVIDER_PREFIX}-test-example

${PROVIDER_PREFIX}-test-example_INPUT_FILES += \
	test-example.cnf.dd

test-example.xml:

<sdk-bundle>
  <daemon-table>
    <default-package>test-example</default-package>

    <daemon-entry>
      <name>test-example</name>
      <ui-name>test-example-service</ui-name>
      <binary>INSTALLDIR/opt/sbin/test-example</binary>
      <description>DDL testing process</description>
      <system-process-failover acceptable="exclude"/>
      <optional/>
      <checkconf/>
      <mode-master/>
      <noversion/>
    </daemon-entry>

  </daemon-table>

  <require-table>

    <require-entry>
      <name>test-example</name>
      <help>test-example configuration</help>
      <configure/>
      <view/>
    </require-entry>
  </require-table>

</sdk-bundle>

Sample user session:

user@host% pwd
/usr/sandboxes/mysb/test-dir/src

user@host% ./mk hostuilibs
:
:
:

user@host% cp ../obj-freebsd7-i386/lib/ddl/feature/test-example-dd.so
                  /usr/sandboxes/mysb/sim-dir/ui-sim-10.2I20100121_0607/usr/lib/dd/

user@host% cp ../obj-freebsd7-i386/lib/ddl/feature/test-example.dml
/usr/sandboxes/mysb/sim-dir/ui-sim-10.2I20100121_0607/usr/lib/dd/

user@host%

user@host% cd /usr/sandboxes/mysb/sim-dir/ui-sim-10.2I20100121_0607/

user@host% ./sdk-ui-sim -p /usr/sandboxes/mysb/test-dir -x start
Running  /usr/sandboxes/mysb/test-dir/test/usr/sbin/mgd-bsd
mgd: commit complete
Running  /usr/sandboxes/mysb/test-dir/test/usr/sbin/cli
 

At this point, you can try your new commands.

Testing ODL

Follow these steps to test your ODL code.

  1. Add the new ODL source file to sandbox/src/lib/odl/input .

  2. In sandbox/src, compile the ODL using the command mk hostuilibs.

  3. Copy the generated ODL library, libprovider-prefix-appname-render.so to sim-dir/ui-sim-release/usr/lib/render

  4. Construct a new XML output file to correspond to the ODL file.

  5. Restart sdk-ui-sim.

  6. When the CLI prompt appears, verify your display by entering the following command: file render path-to-XML-file/xml-filename.xml.

  7. In the XML file to be rendered, change junos:style="detail" to junos:style="brief".

  8. Re-start sdk-ui-sim

ODL Testing Example

The following example shows some test ODL source code and XML output file, followed by the steps the user takes to test the code.

ODL source code:

/*
 * This odl file defines a DTD called junos-test-example.dtd
 */

#define DNAME_TEST_EXAMPLED "test-exampled"
dtd junos-test-example;

/*
 * Create tags for summary information
 */
tag test-example-summary-information {
    flag root;

    tag test-example-info {
        flag multiple;

        tag test-example-info-if {
            type string;
            formal-name "Interface";
            help "Name of interface";
        }

        tag test-example-info-addr {
            type string;
            formal-name "Destination";
            help "Destination address";
        }

        tag test-example-info-state {
            type string;
            formal-name "State";
            help "State of the request";
        }
    }

    style detail {
        format test-example-info-detail-format {
            fmtflag blank-line;
            flag leading colon comma space; 

            line {
                field test-example-info-if;
            }

            indent 2;
            line {
                field test-example-info-addr;
            }
            indent 2;
            line {
                field test-example-info-state;
            }
        }
    }
    style brief {
        format test-example-info-brief-format {
            header "
 
Interface                               Address
";

            picture "
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
";

            line {
                field test-example-info-if;
                field test-example-info-addr;
            }
        }
    }
}

XML output file xml-test-example.xml:

<rpc-reply xmlns:junos="http://xml.juniper.net/junos/10.2I0/junos">
<test-example-summary-information xmlns="http://xml.juniper.net/junos/10.2I0/junos-test-example" junos:style="detail" >
    <test-example-info>
	<test-example-info-if>ge-1/0/0</test-example-info-if>
	<test-example-info-addr>100.100.100.1</test-example-info-addr>
	<test-example-info-state>up</test-example-info-state>
    </test-example-info>
</test-example-summary-information>
<cli>
    <banner></banner>
</cli>
</rpc-reply>

Sample user session:

user@host% pwd
/usr/sandboxes/mysb/test-dir/src

user@host% ./mk hostuilibs
:
:
:
user@host% cp ../obj-freebsd7-i386/lib/odl/xmltags/test-example-render.so
/usr/sandboxes/mysb/sim-dir/ui-sim-10.2I20100121_0607/usr/lib/render/

user@host% cd /usr/sandboxes/mysb/sim-dir/ui-sim-10.2I20100121_0607/

user@host% ./sdk-ui-sim -p /usr/sandboxes/mysb/test-dir -x start

Running  /usr/sandboxes/mysb/test-dir/test/usr/sbin/mgd-bsd
mgd: commit complete
Running  /usr/sandboxes/mysb/test-dir/test/usr/sbin/cli

user@host>

The display before setting junos:style="brief" is as follows:

user@host> file render /usr/sandboxes/mysb/test-example.tst
Interface: ge-1/0/0
  Destination: 100.100.100.1
  State: up

The display after setting junos:style="brief" is as follows:

user@host> file render /usr/sandboxes/mysb/test-example.tst

Interface name                               Address
ge-1/0/0                                100.100.100.1

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