Understanding YANG

Introduction to the YANG modeling language


YANG models are at the heart of SR OS. The MD-CLI, the configuration and state information, and the interfaces to the software are all modeled/derived from YANG models. The tutorial runs through creating a simple YANG model to illustrate how YANG works.


What is YANG?

YANG is a language that is designed to be readable by both humans and machines in order to model configuration and state information. YANG is rapidly becoming the standard way to model network devices and network device information. YANG is defined in the following RFCs:


  • RFC 6020 - YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)
  • RFC 6021 - Common YANG Data Types
  • RFC 7950 - The YANG 1.1 Data Modeling Language


A YANG model defines a tree structure and data is mapped into this tree.  A model is defined in a text file and comprises a module and, optionally, submodules, which when compiled together form the tree.


Tutorial - Create your own YANG model

This tutorial assumes you have access to a device that will allow you to edit files and to install the open source pyang tool on GitHub.  The tutorial does not provide details on how to install pyang.


This tutorial will create a YANG model defining an engineer, how they commute to work, and the types of foods that they can eat, whether in the home or at the office.


YANG type definitions

In addition to the standard type definitions detailed in the RFCs, personalized type definitions can be created within YANG modules (the term "modules" is used interchangeably in this tutorial to mean modules or submodules).  


Create a file called engineer_types.yang.  The contents of the file should be as follows:


module engineer_types {
    yang-version 1.1;
    namespace "urn:nokia.com:srexperts:types";
    prefix "types";
    revision 2019-05-06;
    typedef age_type {
        description "Engineers start work at 18 and should be retired by 110";
        type int8 {
            range "18 .. 110";
        }
        units years;
    }
    typedef transportation_type {
        description "Method of transportation";
        type enumeration {
            enum foot;
            enum bicycle;
            enum car;
            enum bus;
            enum train;
            enum boat;
            enum aeroplane;
        }
    }
}


This file contains a number of required elements that are required to define a YANG module:


  • A module name - This name is defined in the module engineer_types section, with engineer_types being the name of the new YANG module
  • A prefix - This is the short name that can be used within YANG modules to quickly reference the modules
  • A revision number - This is in the format of a date and should be adjusted when a change is made to the module. The revision number is returned to the operator when some specific RPCs are made (more details on these RPCs are in other tutorials on the developer portal).
  • A yang-version - This is not the version number of the module but the version number of the YANG definition that the author has written the module to. Currently this would be 1.0 or 1.1 (if this is a new module then starting with the latest version is advisable)


This module also contains two elements called typedef. These are, as the name would suggest, type definitions. They define custom types through the use of standardized YANG elements. Standardized types as defined in the RFCs could have been used but for the purposes of this tutorial we will define our own.


In the YANG file created, age_type and transportation_type have been added to represent the age of the engineer and the method of transportation for the engineer's commute. The typedef statement is followed directly by the name of the type.  


typedef <name>


Taking each custom type in turn:


age_type

Describes the age of the engineer between the legal working age and retirement age (using an example).  We write this description into the YANG module using the description keyword.


typedef age_type {
    description "Engineers start work at 18 and should be retired by 110";
}


The age range is now defined using the range keyword. We will use age 18 as the legal working age and age 110 as the retirement age (let's hope we can all retire before then!). The .. characters are special and mean between.


typedef age_type {
    type int8 {
        range "18 .. 110";
    }
}


The last element defined for the age_type type definition provides the contextual unit of the range numbers. In this type definition the age is in years.


typedef age_type {
    units years;
}


transportation_type 

Describes the transportation method the engineer uses to commute to work. A simple description is added to the type as in the previous example.  


typedef transportation_type {
    description "Method of transportation";
}


For the transportation method, there are a small number of set options for this type and so an enumeration (or enum) will be used. 


type enumeration {
    enum foot;
    enum bicycle;
    enum car;
    enum bus;
    enum train;
    enum boat;
    enum aeroplane;
}


Creating the main YANG module

Time to create the main YANG file that will be the root of the module. There is nothing specific in YANG that defines the root; this is just dependent on which YANG file you compile.

Create a file called engineer.yang containing the following:


module engineer {
    yang-version 1.1;
    namespace "urn:nokia.com:srexperts:engineer";
    prefix "engineer";
    import engineer_types {
      prefix "types";
    }
    revision 2019-05-06;
    container engineer {
        description "Its me";
        leaf name {
            mandatory true;
            type string;
        }
        leaf age {
            type types:age_type;
        }
        leaf commute {
            type types:transportation_type;
        }
    }
}


As this is a new YANG module (file), then this file needs to have the same mandatory fields in it:


  • module name
  • yang-version number
  • prefix
  • revision date


Importing other YANG modules


As you recall, a module containing custom type definitions was already created. In order to use these types within this YANG module the engineer_types module needs to be imported.


To import a module the import statement is used along with the module name of the YANG module being imported. It is good practice to use the module name as the YANG filename (pyang and other similar tools will flag a warning if the names are not the same).


The import statement can include sub-options. We will define a prefix to be used within this YANG module to reference the external module (in this tutorial this prefix will be called types), this allows for a shorthand notation within this YANG module.


import <imported_module_name> {
    prefix "<local_prefix>";
}


In our tutorial example this looks as follows:


import engineer_types {
    prefix "types";
}


Now that the engineer_types module has been imported, we can use the prefix, followed by a colon (:) to reference it inside this module.  Examples of this will be shown later.


Containers and Leaves


The next elements in the module are:


  • container - This can be thought of as a branch in a tree
  • leaf - This is, as its name would suggest, the end of a branch (the leaf) where our information will sit


Containers and leaves should be used hierarchically. Containers can exist inside containers, however, containers cannot exist inside leaves (leaf). 

A container is defined in the same way as the module name and the import statement.  


container <container_name> {
    ## Your information will go here
}


The first container that will be created is the engineer container. 


container engineer {
    description "Its me";
}


As with the other items created so far a short description should be added.


Inside this container a number of information elements, or leaves (leaf), will be placed. First, the engineer needs a name. Creating a leaf is the same as creating any other element; the keyword leaf is used and then the leaf name is given. In this case, the leaf name is name.


leaf name {
    mandatory true;
    type string;
}


Each leaf has a type. In this case, the type is the built-in YANG type, string. More details about the built-in YANG types can be found in the RFCs above. The mandatory tag is a boolean and should be set to true if the field is required and false (or not added at all, as the default is false) if not.


The next two leaves (leaf) are age and commute

leaf age {
    type types:age_type;
}
leaf commute {
    type types:transportation_type;
}


Neither of these is mandatory, as can be seen by the lack of the mandatory tag (and therefore = mandatory false). As with name, age and commute both have a type. The value of type is not a standardized YANG type but instead uses the custom types defined at the beginning of this tutorial.


To reference these custom types in the leaves (leaf), we use the type element as before but the value consists of two variables joined together: the prefix defined earlier in this YANG module when we imported the custom types module and the name of the type within our engineer_types.yang file. These two elements are joined together using the colon (:) symbol.


type types:age_type;
type types:transportation_type;


Lists and Choices


Now we have a basic YANG model comprising custom types and a number of leaves (leaf). This module can be enhanced with some additional fields.  


Engineers work on projects and need to eat and drink. The first new information section will be a project. The project will be a separate branch in the tree which means that it will be a container inside the engineer container.


container engineer {
    container project {
        list work_item {
            key "work_item_name";
            ordered-by user;
            leaf work_item_name {
                type string;
            }
            leaf deadline {
                type string;
            }
        }
    }    
}


This section covers lists, and as an engineer can work on zero or more work items we will create a list called work_item. By now, defining elements should be straightforward and defining lists is no different. Lists however, require a specific field to be used as the key (the theory behind keys in lists and databases can range from simple to very complex and will not be covered here).  The key entered must match a leaf inside the list. In this tutorial, the key will be called work_item_name.


The addition of the leaves (leaf) inside the list is the same as earlier in the tutorial. We will create work_item_name and deadline.  


The last thing to mention here about lists is the ordered-by option. Lists can be ordered in two ways:


  • user - User ordered lists will appear in the tree based on the order in which they appear in the YANG module file. This is the order in which the list must be processed by any server (an element you can communicate with).
  • system - System ordered lists can appear in any order the server deems appropriate. This is often used where the processing of any items in a list is not dependent on any other item in that list. 



The next list called food will be more complicated. It will use the choice statement which will be explained in a moment.


container engineer {
    list food {
        key "item_name";
        ordered-by system;
        leaf item_name {
            type string;
        }
        choice food_type {
            case office {
                container coffee {
                    leaf size {
                        type enumeration {
                            enum tall;
                            enum grande;
                            enum venti;
                        }
                    }
                    leaf milk {
                        type boolean;
                    }
                }
                leaf donut {
                    type empty;
                }
            }
            case home {
                leaf salad {
                    type empty;
                }
                leaf smoothie {
                    type empty;
                }
            }
        }
    }
}


As you can see, the use of containers and leaves (leaf) remains the same no matter where in the tree they are located. The food list is a system ordered list with the item_name set as the key.


Within the food list there is a choice statement. This is a logical XOR statement and the options for this choice are created as case statements (which look very similar to containers). The food_type choice states that there are different food options available depending on whether the engineer is at home (case) or in the office (case).  As choice is an exclusive-OR (XOR) the user can select one of the two choices and as soon as one is selected the other (and all sub-elements) will be disabled.


The detailed list then says that if at home the engineer can have a salad or smoothie and if in the office they can have a coffee (which has a size and can be supplied with or without milk) or a donut. This should help them work on their project!


Putting it all together


Now the two YANG module files are complete and when compiled together will create the YANG model.


Both files in their entirety are here. Create these now if you haven't already done so above.


engineer_types.yang

module engineer_types {
    yang-version 1.1;
    namespace "urn:nokia.com:srexperts:types";
    prefix "types";
    revision 2019-05-06;
    typedef age_type {
        description "Engineers start work at 18 and should be retired by 110";
        type int8 {
            range "18 .. 110";
        }
        units years;
    }
    typedef transportation_type {
        description "Method of transportation";
        type enumeration {
            enum foot;
            enum bicycle;
            enum car;
            enum bus;
            enum train;
            enum boat;
            enum aeroplane;
        }
    }
}


engineer.yang

module engineer {
    yang-version 1.1;
    namespace "urn:nokia.com:srexperts:engineer";
    prefix "engineer";
    import engineer_types {
      prefix "types";
    }
    revision 2019-05-06;
    container engineer {
        description "Its me";
        leaf name {
            mandatory true;
            type string;
        }
        leaf age {
            type types:age_type;
        }
        leaf commute {
            type types:transportation_type;
        }
        list food {
            key "item_name";
            ordered-by system;
            leaf item_name {
                type string;
            }
            choice food_type {
                case office {
                    container coffee {
                        leaf size {
                            type enumeration {
                                enum tall;
                                enum grande;
                                enum venti;
                            }
                        }
                        leaf milk {
                            type boolean;
                        }
                    }
                    leaf donut {
                        type empty;
                    }
                }
                case home {
                    leaf salad {
                        type empty;
                    }
                    leaf smoothie {
                        type empty;
                    }
                }
            }
        }
        container project {
            list work_item {
                key "work_item_name";
                ordered-by user;
                leaf work_item_name {
                    type string;
                }
                leaf deadline {
                    type string;
                }
            }
        }    
    }
}


Using pyang


pyang is an open source tool available for download that allows users to compile YANG modules together (or independently) in order to validate and visualize the YANG model in various output formats.


The following is not a complete tutorial on pyang (many of these can be found on the Internet).  


Ensure that your system has pyang correctly installed then, from the same directory as the YANG files, enter the following command:

 

pyang -f tree engineer.yang


There is no need to add the engineer_types.yang file to the command as well as this is already imported from within the engineer.yang model.


The following is the output. It describes the YANG model we have made in a tree format. The -f flag above defines the output format. There are many options available and it is worth exploring them all for yourself.


module: engineer
  +--rw engineer
     +--rw name       string
     +--rw age?       types:age_type
     +--rw commute?    types:transportation_type
     +--rw food* [item_name]
     |  +--rw item_name         string
     |  +--rw (food_type)?
     |     +--:(office)
     |     |  +--rw coffee
     |     |  |  +--rw size?   enumeration
     |     |  |  +--rw milk?   boolean
     |     |  +--rw donut?      empty
     |     +--:(home)
     |        +--rw salad?      empty
     |        +--rw smoothie?   empty
     +--rw project
        +--rw work_item* [work_item_name]
           +--rw work_item_name    string
           +--rw deadline?         string


Conclusion


In this tutorial, we have learned the basics of the YANG modeling language including the following elements:


  • YANG module file formats
  • Mandatory YANG tags for modules
  • Importing modules
  • Custom type definitions
  • Leaf
  • Container
  • Choice
  • Case
  • User-ordered lists
  • System-ordered lists


We have written our own first YANG models and compiled them using the pyang open source tool.


Hopefully this has been a useful introduction to YANG and you feel ready to review in more detail the Nokia YANG models that define the SR OS router operating system and to get going with automation. Further tutorials are available on how to integrate with some common tools. Take a look as well at the Network Services Platform (NSP) sections to see Nokia's automation and management system.