Thursday, May 11, 2017

Parsing YANG Files

Overview

YANG is defined by the IETF in RFC 6020 and in RFC 7950 as an Interface Definition Language (IDL) to facilitate the interchange of data and to perform Remote Procedure Calls (RPC). YANG is a popular tool for the configuration and control of computer network routers/switches. Further information on the history & use of YANG can be found at Yang Central.

YANG Example

Suppose we wished to create a simple Calculator service. We might choose to specify the semantics of or service in a YANG file:

File: calc.yang

module calculator {
  namespace       "http://brocade.com/ns/calculator";
  contact         "Alan Thompson <athomps@brocade.com>";
  description     "YANG spec for a simple RPN calculator";
  revision 2017-04-01 {
    description "Prototype 1.0";
  }

  import calculator-types {
    prefix ct;
  }

  rpc add {
    description "Add 2 numbers";
    input {
      leaf x {
        type decimal64; }
      leaf y {
        type decimal64; } }
    output {
      leaf result {
        type decimal64; } } } }

This text file defines a Calculator server advertising a single function add. The add function accepts 2 arguments x and y of type decimal64, and returns a single value result also of type decimal64. A similar server defined in Java may have an interface that looks something like the following:

import CalculatorTypes;

public interface Calculator {
  public static final String namespace       "http://brocade.com/ns/calculator";
  public static final String contact         "Alan Thompson <athomps@brocade.com>";
  public static final String description     "YANG spec for a simple RPN calculator";
  public static final String revision        "2017-04-01";  // Prototype 1.0

  double add( double x, double y );
}

The goal of the yang-parser is to read the characters from a raw text file like calc.yang and construct an Abstract Syntax Tree (AST) suitable for further processing. The yang-parser unit tests show the results of running the parser:

(is= yang-ast
  [:module
   [:identifier "calculator"]
   [:namespace      [:string "http://brocade.com/ns/calculator"]]
   [:contact        [:string "Alan Thompson <athomps@brocade.com>"]]
   [:description    [:string "YANG spec for a simple RPN calculator"]]
   [:revision
    [:iso-date "2017-04-01"]
    [:description [:string "Prototype 1.0"]]]
   [:rpc
    [:identifier "add"]
    [:description [:string "Add 2 numbers"]]
    [:input
     [:leaf [:identifier "x"]
            [:type [:identifier "decimal64"]]]
     [:leaf [:identifier "y"]
            [:type [:identifier "decimal64"]]]]
    [:output
     [:leaf [:identifier "result"]
            [:type [:identifier "decimal64"]]]]]])

The above shows the AST produced by the first-stage of the yang-parser, expressed in the Hiccup format. Further transformations on this AST convert the :rpc portion of the AST into a form more readily usable for code generation and other tasks:

(tx-rpc rpc-hid)
(is= (tf/hid->hiccup rpc-hid)
  [:rpc {:name :add}
   [:input
    [:leaf {:type :decimal64, :name :x}]
    [:leaf {:type :decimal64, :name :y}]]
   [:output [:leaf {:type :decimal64, :name :result}]]] )

We can see that, after this transformation, the x and y leaf values have been collapsed into a simpler form, and the RPC function name add is attached directly to the :rpc element.

Code Generation

Once the YANG definition file has been parsed and tranformed in to a usable AST, we can read information about our Calculator RPC in order to generate an API definition as seen here:

(is= (rpc->api rpc-hid)
  '(fn fn-add [x y] (fn-add-impl x y)))

Status

Code for the YANG parser is available on Github.  It is written in Clojure and contains a collection of 387 unit tests. At present it can ingest a raw YANG syntax file and generate a structured AST as shown above. Future work includes the generation of API code and stub code so that clients can make RPC requests to servers using the NETCONF message format. Similar API & stub code will allow the server process to receive NETCONF RPC messages and generate well-formed NETCONF responses.