Chapter 4. Rendering Agents

Table of Contents

4.1. Introduction
4.2. Data Model
4.3. Using the CLIs
4.4. Using NETCONF

4.1. Introduction

In this chapter we reintroduce the links.yang model from the Yang chapter and see how we can use that model to render all northbound interfaces.

This chapter is an overview, and all concepts touched upon in this chapter are thoroughly described in later chapters.

4.2. Data Model

The links.yang data model introduced in the "Yang" chapter defines a set of interfaces. Compiling the links.yang file into an .fxs file is the first required step.

module links {
    namespace "http://example.com/ns/link";
    prefix link;

    import ietf-yang-types {
        prefix yang;
    }


    grouping LinkFlagsType {
        leaf UP {
            type empty;
        }
        leaf NOARP {
            type empty;
        }
        leaf BROADCAST {
            type empty;
        }
        leaf MULTICAST {
            type empty;
        }
        leaf LOOPBACK {
            type empty;
      }
        leaf NOTRAILERS {
            type empty;
        }
    }

    typedef QueueDisciplineType {
        type enumeration {
            enum pfifo_fast;
            enum noqueue;
            enum noop;
            enum htb;
        }
    }
    container config {
        container links {
            list link {
                key name;
                unique addr;
                max-elements 1024;
                leaf name {
                    type string;
                }
                container flags {
                    uses LinkFlagsType;
                }
                leaf addr {
                    type yang:mac-address;
                    mandatory true;
                }
                leaf brd {
                    type yang:mac-address;
                    mandatory true;
                }
                leaf mtu {
                    type uint32;
                    default 1500;
                }
            }
        }
        container queueDisciplines {
            list queueDiscipline {
                key linkName;
                max-elements 1024;
                leaf linkName {
                    type leafref {
                        path "/config/links/link/name";
                    }
                }
                leaf type {
                    type QueueDisciplineType;
                    mandatory true;
                }
                leaf length {
                    type uint32;
                }
            }
        }
        container linkLimitations {
            list linkLimitation {
                key linkName;
                leaf linkName {
                    type leafref {
                        path "/config/links/link/name";
                    }
                }
                container limitations {
                    leaf only10Mbps {
                        type boolean;
                        default false;
                    }
                    leaf onlyHalfDuplex {
                        type boolean;
                        default false;
                    }
                }
            }
        }
        container defaultLink {
            leaf linkName {
                type leafref {
                    path "/config/links/link/name";
                }
            }
        }
    }
}

The command to compile the YANG file is confdc -c links.yang Thus in our Makefile we have:

      all:   links.fxs

      %.fxs:  %.yang
        $(CONFD_DIR)/bin/confdc -c $*.yang
        

The Makefile requires the UNIX environment variable CONFD_DIR to be set to the directory where ConfD is installed.

Once we have the .fxs file, it's time to start ConfD. To do that we have some initial steps that must be taken care of first.

We must have a configuration file for ConfD it self. This file is usually referred to as confd.conf. There is a multitude of things we can configure in confd.conf(5) but for this initial example we'll just focus on the things we need to get the links.yang example to work. We can copy the confd.conf file from etc/confd/confd.conf relative to the installation directory of ConfD and add "." to the loadPath

Now with our newly compiled links.yang file we can start ConfD as:

# source /path/to/installed_confd/confdrc
# confd --foreground --verbose -c ./confd.conf
      

This starts ConfD in the foreground with all log messages displayed on stdout. This is a convenient way of running ConfD during development.

4.3. Using the CLIs

The first thing we can try out is how a Juniper style CLI looks and feels on our data model. The ConfD CLI(s) are entirely rendered from the data model. It's possible to extend and tweak the looks of the CLI in several ways, but for now, we try just what we get from the plain data model. The ConfD CLI is run through a small C program called confd_cli which is typically used as a login shell on the target host. During development, it's usually more convenient to just invoke that program directly from the UNIX prompt. The confd_cli program communicates with the ConfD daemon over the loopback socket.

Here is an actual session:

# confd_cli
Welcome to the ConfD CLI
admin connected from 127.0.0.1 using console on buzz
admin@buzz 17:37:17> configure
Entering configuration mode private
[ok][2009-03-17 17:37:26]

[edit]
admin@buzz 17:37:26% set config links link eth0 addr 00:12:3f:7d:b0:32 brd 00:1
2:3f:7d:b0:32
[ok][2009-03-17 17:37:48]

[edit]
admin@buzz 17:37:48% set config links link eth0 flags BROADCAST
[ok][2009-03-17 17:38:05]

[edit]
admin@buzz 17:38:05% set config links link eth0 flags
Possible completions:
  BROADCAST  LOOPBACK  MULTICAST  NOARP  NOTRAILERS  UP
admin@buzz 17:38:05% set config links link eth0 flags LOOPBACK
[ok][2009-03-17 17:38:25]

[edit]
admin@buzz 17:38:25% commit
Commit complete.
[ok][2009-03-17 17:38:31]

[edit]
admin@buzz 17:38:31% exit
[ok][2009-03-17 17:38:34]
admin@buzz 17:38:34> show configuration config
links {
    link eth0 {
        flags {
            UP;
            BROADCAST;
            LOOPBACK;
        }
        addr 00:12:3f:7d:b0:32;
        brd  00:12:3f:7d:b0:32;
    }
}
[ok][2009-03-17 17:38:44]
admin@buzz 17:38:44> exit
#

Thus from the data model we got a fully functional Juniper like CLI.

We can also get a Cisco like CLI towards the same data model:

# confd_cli -C
Welcome to the ConfD CLI
admin connected from 127.0.0.1 using console on buzz
buzz# show running-config config
config links link eth0
 flags UP
 flags BROADCAST
 flags LOOPBACK
 addr 00:12:3f:7d:b0:32
 brd  00:12:3f:7d:b0:32
!
buzz# config
Entering configuration mode terminal
buzz(config)# config links link <TAB>
Possible completions:
  <name:string>  eth0
buzz(config)# config links link eth0 <TAB>
Possible completions:
  addr  brd  flags  mtu  <cr>
buzz(config)# config links link eth0 mtu 1200
buzz(config-link-eth0)# commit
Commit complete.
buzz(config-link-eth0)#
buzz(config)# config <TAB>
Possible completions:
  defaultLink  linkLimitations  links  queueDisciplines
buzz(config)# config defaultLink linkName eth0
buzz(config)# commit
Commit complete.
      

The rendered CLIs are highly capable containing all features expected by a modern CLI.

The data model contains a top level config container. This makes sense from a data modeling perspective since it wraps all configuration items inside a container. However, we may wish to do away with the container in the CLI. We want to issue the command defaultLink linkName eth0 as opposed to the command config defaultLink linkName eth0.

The ConfD CLI can be tweaked in a myriad different ways. The typical development cycle is to define the data model to be as succinct and understandable as possible. This makes life easier for the application programmers who write C/C++ code that access the data model. Once the YANG model is good, we tweak the CLI to become what we want.

In this tiny example we write a really small CLI modification file:

<clispec xmlns="http://tail-f.com/ns/clispec/1.0" style="c">
  <operationalMode>
  </operationalMode>
  <configureMode>
    <modifications>
      <dropElem src="config"/>
    </modifications>
  </configureMode>
</clispec>
      

Save that file as mods.cli and compile that file as:

# confdc -c mods.cli
      

This results in a file called mods.ccl that needs to be put in the load path of ConfD. We then re-launch the Cisco CLI:

# confd_cli -C
Welcome to the ConfD CLI
admin connected from 127.0.0.1 using console on buzz
buzz# config
Entering configuration mode terminal
buzz(config)# links link eth0 <TAB>
Possible completions:
  addr  brd  flags  mtu  <cr>
buzz(config)# links link eth0 mtu ?
Possible completions:
  <unsignedInt>[1200]
buzz(config)# links link eth0 mtu 1400
buzz(config-link-eth0)# commit
Commit complete.
buzz(config-link-eth0)#
      

4.4. Using NETCONF

NETCONF is a powerful protocol that can be used to programmatically reconfigure a device. We're still running ConfD with the links.yang data model.

The easiest way to interact with the NETCONF agent in ConfD is to use a small python based program called netconf-console that ships with ConfD. Let's just run it from the UNIX prompt and see what we get:

# netconf-console --user=admin --password=admin --get-config -x '/config'
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">
  <data>
    <config xmlns="http://example.com/ns/link">
      <links>
        <link>
          <name>eth0</name>
          <flags>
            <UP/>
            <BROADCAST/>
            <LOOPBACK/>
          </flags>
          <addr>00:12:3f:7d:b0:32</addr>
          <brd>00:12:3f:7d:b0:32</brd>
          <mtu>1400</mtu>
        </link>
      </links>
      <queueDisciplines/>
      <linkLimitations>
        <linkLimitation>
          <linkName>eth0</linkName>
          <limitations>
            <only10Mbps>true</only10Mbps>
          </limitations>
        </linkLimitation>
      </linkLimitations>
      <defaultLink>
        <linkName>eth0</linkName>
      </defaultLink>
    </config>
  </data>
</rpc-reply>

The above command uses the python paramiko ssh client to establish an SSH session to ConfD. It then issues a NETCONF get-config RPC to retrieve all configuration data found below the XPath /config, i.e. this is precisely the data we have just entered in the CLI. The command netconf-console --get-config is a great way to extract a backup of the entire configuration of the device.

If we want to manipulate the configuration with the netconf-consoleprogram , we must prepare some XML data that can be sent as an edit-config and feed it to netconf-console.

Here is an example, save the following to a file, set-mtu.xml.

  <edit-config>
    <target>
      <running/>
    </target>
    <config xmlns="http://example.com/ns/link">
      <config>
        <links>
          <link>
            <name>eth0</name>
            <mtu>1100</mtu>
          </link>
        </links>
      </config>
    </config>
  </edit-config>

Then run this with:

# netconf-console --user=admin --password=admin --rpc set-mtu.xml
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">
  <ok/>
</rpc-reply>

It's also fairly instructive to directly connect to the agent using OpenSSH as in:

# ssh -s -p 2022 admin@localhost netconf
admin@localhost's password:
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:capability:writable-running:1.0</capability>
<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>
<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.0</capability>
<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>
<capability>urn:ietf:params:netconf:capability:url:1.0?scheme=ftp,file</capability>
<capability>urn:ietf:params:netconf:capability:validate:1.0</capability>
<capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>
<capability>http://example.com/ns/link</capability>
<capability>http://tail-f.com/ns/aaa/1.1</capability>
</capabilities>
<session-id>14</session-id></hello>

The agent replies with the capabilities it supports.

4.4.1. Generating Java classes for JNC

It is of course entirely possible to use the netconf-console program to XML script towards a NETCONF agent. An alternative is to use a Java library such as JNC to interact with the NETCONF agent. JNC is in the Open SOurce and can by found on github at https://github.com/tail-f-systems/JNC/.