Learn YANG - Full Tutorial for Beginners

Learn YANG - Full Tutorial for Beginners
Profile Picture Small
Matt Albrecht, January 3rd 2020

As I'm sure you've noticed, we are living in a time of explosive growth for the network automation industry. A core component of modern automation technology is the language YANG (Yet Another Next Generation). In today's tutorial, let's do a deep dive and learn the language from scratch.

So, what is YANG?

In short, YANG is a language used to describe data models of network devices. The language is maintained by NETMOD - an IETF working group. The latest version of YANG is 1.1, and the full specification of the language is documented in RFC 7950.

With that said, what is a data model of a network device?

To answer that, let's imagine a hypothetical scenario where your friend asks you what IP interface attributes can be configured on a specific router. You might say:

"Well, to configure an interface on this router, you need to supply: an interface name, an IP address, and a subnet mask. You also need to enable the interface - the router will keep the interface disabled if you don't."

Now, as simple as this response sounds, what we just did was describe a data model for an IP interface. A YANG model will do the same but uses strict syntax rules to make the model standardized and easy to process with computers.

It's all about describing the data structure

Note that in our response to our friend, we didn't provide any specific values for an interface - we only described the data structure.

This is an important distinction to note as it will make the bigger picture much easier to understand when we later talk about NETCONF.

Let's learn the YANG syntax

Let's redo our IP interface example, but this time we'll describe the model using YANG rather than plain English.

I'll be working on Ubuntu 18.04 for this demo. Before we write our YANG model, let's install an excellent python utility called "pyang", which will allow us to interact with our model. We'll also need two dependencies for the utility to work.

# Install pyang dependencies
sudo apt-get install -y xsltproc libxml2-utils

# Clone the pyang repo
git clone https://github.com/mbj4668/pyang

# Install pyang
cd pyang
sudo python setup.py install

With that done, let's create a new file for defining our YANG model. The file extension must be "yang". I'll name my file:

ultraconfig-interfaces.yang

We'll now open the file in a text editor.

Writing our first YANG statements

At the top of the file, we'll add a "module" statement followed by the name of our module and a braces block. The module name must match the name of our file.

module ultraconfig-interfaces {
  
}

All of the content we now add to the file will go inside the module braces.

Next, let's add header information, meta-information, and revision history to our module. You'll notice that strings are terminated by semicolons rather than newline characters - this allows long strings to be written over multiple lines preserving readability.

yang-version 1.1;

namespace
  "http://ultraconfig.com.au/ns/yang/ultraconfig-interfaces";

prefix if;

organization 
  "Ultra Config Pty Ltd";

contact
  "Support: <https://ultraconfig.com.au/contact/>";

description
  "This YANG module has been created for the purpose of a tutorial.
   It defines the model for a basic ethernet interface";

revision "2020-01-03" {
  description
    "Initial Revision";
  reference
    "Learn YANG - Full Tutorial for Beginners";
}

The labels yang-version, namespace, organization, etc are known as "statements" in YANG terminology. Let's go over the function of each statement below.

  • yang-version - Identifies the YANG language specification that the module will conform to. We'll ensure our module conforms to YANG 1.1 which is defined in RFC 7950.
  • namespace - This is an XML namespace that must be unique for the module. We used a URL but you can use a URN, URI or any other unique identifier here. The namespace specified here must match the namespace on any XML objects which conform to our YANG model.
  • prefix - A short and unique string to identify our module. This prefix may be used in other YANG modules to import definitions contained in this one.
  • organization - A string identifying the entity responsible for the module.
  • contact - Contact details for the entity responsible for the module.
  • description - A description of the module.
  • revision - Used for version control. Each edit to a YANG module will add a new revision statement detailing the changes in sub-statements.

With the header & meta information now out of the way, we can move on to our module definition.

How to create a custom data type

The YANG language includes a set of built-in data types. The language also allows, however, the ability for developers to define custom data types.

We'll do that now for our IPV4 address and subnet mask. Both of these attributes should conform to the "dotted-quad" data type defined below. We'll add this definition to our YANG module.

typedef dotted-quad {
  type string {
    pattern
      '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
        + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
  }
  description
    "Four octets written as decimal numbers and
     separated with the '.' (full stop) character.";
}

You may be thinking, why not just use a string instead of defining a custom data type?

While this would work, it would certainly be a bad practice. In all programming, it is always best to add constraints on the lowest layers to avoid a later reliance on higher layers for error checking.

Our "dotted-quad" definition is quite simple once you get the idea. Our definition says:

"Define a new type called dotted-quad. A value will conform to this data type if it is a string and matches the regular expression defined in the pattern statement."

Understanding configuration data and state data

Moving on, let's now add a "container" for our interfaces.

container interfaces {

}

Our "interfaces" container will hold the child nodes for our configuration data and state data.

Let's distinguish between these two data types below.

  • Configuration Data - These are read/write configuration fields. For our interface example, this would be the interface name, IP address, subnet mask, and admin enabled/disabled.
  • State Data - These are read-only operational data fields. For our interface example, this could include a packet counter and an operational state (physically up or down).

Again, remember that YANG modules will only define the structure of configuration and state data. It will not contain an instantiated value of the data.

Adding our configuration data

We'll now add a list to our container for defining our interface configuration data.

list interface {
  key "name";
  leaf name {
    type string;
    mandatory "true";
    description
      "Interface name. Example value: GigabitEthernet 0/0/0";
  }
  leaf address {
    type dotted-quad;
    mandatory "true";
    description
      "Interface IP address. Example value: 10.10.10.1";
  }
  leaf subnet-mask {
    type dotted-quad;
    mandatory "true";
    description
      "Interface subnet mask. Example value: 255.255.255.0";
  }
  leaf enabled {
    type boolean;
    default "false";
    description
      "Enable or disable the interface. Example value: true";
  }
}

Our interface configuration data is quite readable. We have four leaf nodes which define the attributes of an interface. These are labelled with the identifiers "name", "address", "subnet-mask" and "enabled".

Three of the leaf nodes are marked as mandatory. The "enabled" node is optional and will have a default value of "false" if not specified.

You'll also notice that the data type of the "address" and "subnet-mask" is "dotted-quad". This matches the identifier of our earlier definition.

Adding our state data

Let's now add our state data to the interfaces container.

list interface-state {
  config false;
  key "name";
  leaf name {
    type string;
    description
      "Interface name. Example value: GigabitEthernet 0/0/0";
  }
  leaf oper-status {
    type enumeration {
      enum up;
      enum down;
    }
    mandatory "true";
    description
      "Describes whether the interface is physically up or down";
  }
}

Looking at the state data you'll notice that one key difference is the "config" statement which is set to false. This indicates that the child nodes belonging to the list are read-only.

You'll also notice the enumeration data type that we hadn't used before. This is another built-in type, which allows us to restrict the valid values for the "oper-status" node to a finite set; in our case, the operational status will only ever be up or down.

That concludes the construction of our YANG module for an interface. We may now proceed to interact with the module using pyang.

How to validate a YANG module

The first cool trick to learn with pyang is how to do basic validation. Run the command below to ensure the YANG module is syntactically correct.

pyang ultraconfig-interfaces.yang

If all is well the output should come out clean. If there are any syntax errors in the module the command will print a message explaining the issue.

To demonstrate this, let's introduce a typographical error by replacing the type "enumeration" with the word spelled incorrectly, "inumeration". Running the validation command again will highlight the error.

ultraconfig-interfaces.yang:69: error: type "inumeration" not found in module "ultraconfig-interfaces"

Alright, now that we have confirmed that, let's fix our spelling mistake.

How to view the schema tree

The next great trick to learn is how to view the schema tree of your YANG module. The schema tree is a summarised visual form of our YANG data model. You can view the tree by adding the format specifier option to your command.

pyang -f tree ultraconfig-interfaces.yang

Here's how the output will look for our example.

module: ultraconfig-interfaces
  +--rw interfaces
     +--rw interface* [name]
     |  +--rw name           string
     |  +--rw address        dotted-quad
     |  +--rw subnet-mask    dotted-quad
     |  +--rw enabled?       boolean
     +--ro interface-state* [name]
        +--ro name           string
        +--ro oper-status    enumeration

The "rw" acronym is short for read-write. The "ro" acronym is short for read-only.

The rest of the tree is quite self-explanatory. The question mark next to the "enabled" node indicates that this object is optional.

Introducing "yang2dsdl"

Let's now move on to another awesome utility called "yang2dsdl". The program is bundled into the pyang project and should have been added to your system path upon installation.

The utility is short for "YANG to DSDL (Document Schema Definition Languages)". The primary purpose of this tool is to convert YANG modules to DSDL schemas but we can also use it to validate instances of data to ensure they conform to a YANG module.

How to validate a data instance

This will be easier to understand with an example. Below is an XML instance of the YANG module which we defined today.

<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <interfaces xmlns="http://ultraconfig.com.au/ns/yang/ultraconfig-interfaces">
    <interface>
      <name>GigabitEthernet 0/0/0</name>
      <address>10.10.10.1</address>
      <subnet-mask>255.255.255.0</subnet-mask>
    </interface>
    <interface>
      <name>GigabitEthernet 0/0/1</name>
      <address>192.168.1.1</address>
      <subnet-mask>255.255.255.0</subnet-mask>
    </interface>
  </interfaces>
</data>

If you are familiar with NETCONF this XML data object will look familiar to you. This is what a payload looks like in a NETCONF request to edit the configuration of a network device.

We'll now use yang2dsdl to ensure the XML object is valid. Save the XML object to a file. We called our file:

data.xml

We can now run the command below which will generate the DSDL schemas of our YANG module and validate our data instance.

yang2dsdl -v data.xml ultraconfig-interfaces.yang

The program output is shown below.

== Generating RELAX NG schema './ultraconfig-interfaces-data.rng'
Done.

== Generating Schematron schema './ultraconfig-interfaces-data.sch'
Done.

== Generating DSRL schema './ultraconfig-interfaces-data.dsrl'
Done.

== Validating grammar and datatypes ...
data.xml validates

== Adding default values... done.

== Validating semantic constraints ...
No errors found.

We can see that no errors were found.

We'll now put an invalid IP address into our XML file and rerun the validation. You'll get an error like the one below.

== Validating grammar and datatypes ...
data.xml:5: element address: Relax-NG validity error : Error validating datatype string
data.xml:5: element address: Relax-NG validity error : Element address failed to validate content
data.xml:3: element interface: Relax-NG validity error : Invalid sequence in interleave
data.xml:3: element interface: Relax-NG validity error : Element interface failed to validate content
Relax-NG validity error : Extra element interface in interleave
data.xml:3: element interface: Relax-NG validity error : Element interfaces failed to validate content
data.xml fails to validate

There we go, that's everything essential you need to learn about YANG!

With a solid grasp of the concepts of the YANG language, you'll find that automation solutions built on top of the NETCONF protocol become demystified.

NETCONF is the protocol for sending and receiving configuration data and state data of network devices. And YANG is the language that describes the structure of this data.

I hope you enjoyed this tutorial as much as I enjoyed making it. You can download the full YANG module we developed today using the link below.

Download: ultraconfig-interfaces.yang

I'll also leave you with a few other resources.

  • IETF YANG Modules - The IETF maintains a large number of YANG modules. Check them out on NETCONF central.
  • OpenConfig YANG Modules - The OpenConfig working group maintains a large number of YANG modules on GitHub.
  • pyang - Command-line reference.
  • yang2dsdl - Command-line reference.

And as always, stay tuned for future tutorials!

ULTRA CONFIG GENERATOR

NETCONF is great at pushing configuration to network devices. But how do you automate the generation of network config?

Have you heard of Ultra Config Generator? If you haven't, I highly recommend you check it out.

We designed the product to allow network engineers to generate and automate network configuration in a highly flexible, efficient and elegant manner. Our customers love the application and I hope that you will too.

Take care until next time!

Ultra Config


JOIN THE DISCUSSION

Subscribe to the Blog

Subscribe now and never miss a new post!