r6 - 17 Feb 2012 - 12:00:11 - MartinBjoerklundYou are here: TWiki >  Main Web > DhcpTutorial

Introduction

In this tutorial, we will introduce many YANG concepts by developing a module to configure and monitor a DHCP server. For this task, we will base our data model on the configuration of the ISC DHCP reference implementation. For the sake of a simpler example, we will model a subset of the complete configuration only.

We will model a few global parameters, subnets and shared-networks, and for monitoring purposes a list of active leases.

An example dhcp.conf file of what we want to model:

# Sample configuration file for ISC dhcpd

default-lease-time 600;
max-lease-time 7200;

subnet 10.254.239.0 netmask 255.255.255.224 {
  range dynamic-bootp 10.254.239.10 10.254.239.20;
  option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
  max-lease-time 1200;
}

shared-network 224-29 {
  subnet 10.17.224.0 netmask 255.255.255.0 {
    range 10.17.224.10 10.17.224.250;
    option routers rtr-224.example.org;
  }
  subnet 10.0.29.0 netmask 255.255.255.0 {
    range 10.0.29.10 10.0.29.230;
    option routers rtr-29.example.org;
  }
}

The module header

A YANG module starts with a couple of mandatory statements. First, we have to give a unique name to our module so that others can refer to it. Second, we have to define an XML namespace URI for the module. There is a one-to-one mapping between the module name and the namespace name. The module name is used to identify the module in other YANG modules, and the namespace URI is used to identify the module in XML.

module dhcp {
  namespace "http://yang-central.org/ns/example/dhcp";
  prefix dhcp;

We also declare a short prefix for the module. It is used as a hint to other module developers when they import our module. To see an example of this, we use the import statement to import the standard YANG modules:

  import ietf-yang-types { prefix yang; }
  import ietf-inet-types { prefix inet; }

Note the prefixes above. In order to refer to the yang-module from now on, we use the prefix, e.g. the statement:

  type yang:date-and-time;

refers to the date-and-time type defined in the yang-types module.

We use the prefix defined in the module itself, e.g. in the yang-types module, the prefix is defined as yang. You can use which prefix you want in your import, as long as it is unique within the module, but by using the prefix from the module, your module will be easier to read for others.

Next, we also have to add two more administrative statements:

  organization
    "yang-central.org";
  description
    "Partial data model for DHCP, based on the config of
     the ISC DHCP reference implementation."; 

Data definitions

Now we can start defining the data. First, at the top-level we add a structural container 'dhcp'. This container is strictly speaking not necessary, but it is considered good modeling practise to group all data definitions in a module under a single container. This makes NETCONF filtering simpler to use because you can get all data from one module with a single filter element.

  container dhcp {
    description
      "configuration and operational parameters for a DHCP server.";

Next we have two global parameters, max-lease-time and default-lease-time. We use the leaf statement to define them:

    leaf max-lease-time {
      type uint32;
      units seconds;
      default 7200;
    }
    leaf default-lease-time {
      type uint32;
      units seconds;
      default 600;
    }

Now we need to model subnets and shared-networks. We note that a shared-network also can contain subnets. Thus it makes sense to define the subnets as a resuable structure. We do this with the grouping statement. Since there can be zero or more subnets, we define them as a list in YANG:

  grouping subnet-list {
    description "A reusable list of subnets";
    list subnet {

Each subnet is identified by its ip and mask. In the YANG standard module inet-types, there is a type ip-prefix which can be used instead of using a separate ip and mask. We define the ip-prefix as a leaf, and declare that this leaf is the list's key:

      key net;
      leaf net {
        type inet:ip-prefix;
      }

A subnet has an optional range definition. If a range is given, it means that the clients on the subnet get addresses dynamically. If a range specification is not given, the clients have to be configured statically. A range has a dynamic-bootp flag, and a low and high address. We model the optional range structure as a container with a presence statement:

      container range {
        presence "enables dynamic address assignment";
        leaf dynamic-bootp {
          type empty;
          description 
            "Allows BOOTP clients to get addresses in this range";
        }
        leaf low {
          type inet:ip-address;
          mandatory true;
        }
        leaf high {
          type inet:ip-address;
          mandatory true;
        }
      }

The presence statement means that if the range container is present in the configuration datastore, then dynamic address assignment is enabled.

The dynamic-bootp leaf is of type empty. This means that the existance of leaf carries all meaning; there is no additional value to the leaf.

Both the leaf low and high are marked as mandatory. This means that if the optinal range container exists in the data store, then both low and high must exist in order for validation to succeed.

Each subnet can also specify some DHCP protocol related options, such as which routers the clients should use, and the domain-name of the subnet. We collect all these options under a container dhcp-options:

      container dhcp-options {
        description "Options in the DHCP protocol";
        leaf-list router {
          type inet:host;
          ordered-by user;
          reference "RFC 2132, sec. 3.8";
        }
        leaf domain-name {
          type inet:domain-name;
          reference "RFC 2132, sec. 3.17";
        }
      }

Note that the container dhcp-options does not have a presence statement. This means that the container is there only to provide structure to the data model. The container itself doesn't carry any meaning. We could as well have defined all options, router and domain-name, directly under the subnet node.

The list of routers that is sent to the clients are sent in order of preference. I.e. a client that receives a list of routers from a DHCP server, should try with the first one, then the second one and so on. This concept is supported in YANG, by declaring that a list or leaf-list is ordered-by user. For such a list, the order of the list entries is semantically important. For other lists, such as the subnet list we are defining, the order has no semantic meaning, and thus this list is not ordered-by user.

Also note that we model the list of routers as a leaf-list. A leaf-list is like an array of a simple type, which is what is needed here.

Finally, each subnet may define it's own max-lease-time, which overrides the global one. Thus, we add a leaf to the subnet:

      leaf max-lease-time {
        type uint32;
        units seconds;
        default 7200;
      }

Now we have a reusable grouping subnet-list which can be used in the dhcp container and in the shared-network list:

  container dhcp {
    description
      "configuration and operational parameters for a DHCP server.";

    leaf max-lease-time { ... }
    leaf default-lease-time { ... }

    uses subnet-list;
  
    container shared-networks {
      list shared-network {
        key name;

        leaf name { type string; }
        uses subnet-list;
      }
    }
  }

Adding integrity constraints

Even in this simple data model, there are several semantic integrity constraints that must hold in order for a configuration to be valid. For example, the default-lease-time must never be larger than the max-lease-time.

YANG supports adding formal integrity constraints with the must statement. As an example, we can add a must constraint to the default-lease-time:

    leaf default-lease-time {
      type uint32;
      units seconds;
      must 'current() <= ../max-lease-time' {
        error-message
          "The default-lease-time must be less than max-lease-time";
      }
      default 600;
    }

Note how we also give an error-message to the constraint. If a NETCONF validation fails due to this constraint, this error-message string will be used by the server in the <rpc-error>.

Operational parameters

At any given time, a DHCP server has a set of active leases. This is not configuration data, but we can define this in YANG as operational data. We do this by adding a container status under the dhcp container, and mark it as being non-configuration:

    container status {
      config false;

When a container is marked as non-config, all its children inherits this property, so we don't have to specify this on all leafs.

The active leases is a list with the leased ip address as key, the start and stop time, and the client's MAC address and type of hardware:

      list leases {
        key address;
      
        leaf address { type inet:ip-address; }
        leaf starts { type yang:date-and-time;}
        leaf ends { type yang:date-and-time; }
        container hardware {
          leaf type {
            type enumeration {
              enum "ethernet";
              enum "token-ring";
              enum "fddi";
            }
          }
          leaf address { type yang:phys-address; }
        }
      }

Complete module

module dhcp {
  namespace "http://yang-central.org/ns/example/dhcp";
  prefix dhcp;

  import ietf-yang-types { prefix yang; }
  import ietf-inet-types { prefix inet; }

  organization
    "yang-central.org";
  description
    "Partial data model for DHCP, based on the config of
     the ISC DHCP reference implementation.";

  container dhcp {
    description
      "configuration and operational parameters for a DHCP server.";

    leaf max-lease-time {
      type uint32;
      units seconds;
      default 7200;
    }

    leaf default-lease-time {
      type uint32;
      units seconds;
      must 'current() <= ../max-lease-time' {
        error-message
          "The default-lease-time must be less than max-lease-time";
      }
      default 600;
    }
    
    uses subnet-list;
  
    container shared-networks {
      list shared-network {
        key name;

        leaf name {
          type string;
        }
        uses subnet-list;
      }
    }

    container status {
      config false;
      list leases {
        key address;
      
        leaf address {
          type inet:ip-address;
        }
        leaf starts {
          type yang:date-and-time;
        }
        leaf ends {
          type yang:date-and-time;
        }
        container hardware {
          leaf type {
            type enumeration {
              enum "ethernet";
              enum "token-ring";
              enum "fddi";
            }
          }
          leaf address {
            type yang:phys-address;
          }
        }
      }
    }
  }

  grouping subnet-list {
    description "A reusable list of subnets";
    list subnet {
      key net;
      leaf net {
        type inet:ip-prefix;
      }
      container range {
        presence "enables dynamic address assignment";
        leaf dynamic-bootp {
          type empty;
          description 
            "Allows BOOTP clients to get addresses in this range";
        }
        leaf low {
          type inet:ip-address;
          mandatory true;
        }
        leaf high {
          type inet:ip-address;
          mandatory true;
        }
      }
      
      container dhcp-options {
        description "Options in the DHCP protocol";
        leaf-list router {
          type inet:host;
          ordered-by user;
          reference "RFC 2132, sec. 3.8";
        }
        leaf domain-name {
          type inet:domain-name;
          reference "RFC 2132, sec. 3.17";
        }
      }
      
      leaf max-lease-time {
        type uint32;
        units seconds;
        default 7200;
      }
    }
  }
}

XML snippet

The following is an XML snippet which contains the same data as the dhcp.conf file above, matching the YANG module:

  <dhcp xmlns="http://yang-central.org/ns/example/dhcp">
    <max-lease-time>7200</max-lease-time>
    <default-lease-time>600</default-lease-time>
    
    <subnet>
      <net>10.254.239.0/27</net>
      <range>
        <dynamic-bootp/>
        <low>10.254.239.10</low>
        <high>10.254.239.20</high>
      </range>
      <dhcp-options>
        <router>rtr-239-0-1.example.org</router>
        <router>rtr-239-0-2.example.org</router>
      </dhcp-options>
      <max-lease-time>1200</max-lease-time>
    </subnet>

    <shared-networks>
      <shared-network>
        <name>224-29</name>
        <subnet>
          <net>10.17.224.0/24</net>
          <range>
            <low>10.17.224.10</low>
            <high>10.17.224.250</high>
          </range>
          <dhcp-options>
            <router>rtr-224.example.org</router>
          </dhcp-options>
        </subnet>
        <subnet>
          <net>10.0.29.0/24</net>
          <range>
            <low>10.0.29.10</low>
            <high>10.0.29.230</high>
          </range>
          <dhcp-options>
            <router>rtr-29.example.org</router>
          </dhcp-options>
        </subnet>
      </shared-network>
    </shared-networks>
  </dhcp>

-- MartinBjoerklund - 12 Nov 2007

Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r6 < r5 < r4 < r3 < r2 | More topic actions
Main.DhcpTutorial moved from Main.DHCPTutorial on 13 Nov 2007 - 12:48 by MartinBjoerklund - put it back
 
YANG
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback