Skip to content

Parse, validate, manipulate, and use YANG modules.

License

Notifications You must be signed in to change notification settings

dgr/yang-parser

 
 

Repository files navigation

Parse, Validate, Manipulate, and Use YANG Modules

The yang-parser API Docs are posted on GitHub Pages

Yang-Parser 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 <[email protected]>";
  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 <[email protected]>";
  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 <[email protected]>"]]
   [: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)))

where the Clojure function rpc→api looks like this:

(s/defn rpc->api :- [s/Any]
  [rpc-hid :- tf/HID]
  (let [rpc-tree           (tf/hid->tree rpc-hid)
        rpc-name           (name (fetch-in rpc-tree [:attrs :name]))
        rpc-input-hid      (tf/find-hid rpc-hid [:rpc :input])
        rpc-input-arg-hids (grab :kids (tf/hid->node rpc-input-hid))
        rpc-arg-syms       (forv [hid rpc-input-arg-hids]
                             (it-> hid
                               (tf/hid->tree it)
                               (fetch-in it [:attrs :name])
                               (kw->sym it)))
        fn-name            (symbol (str "fn-" rpc-name))
        fn-name-impl       (symbol (str fn-name "-impl"))
        fn-def             (vec->list
                             (-> '(fn)
                               (append fn-name rpc-arg-syms)
                               (append (vec->list (prepend fn-name-impl rpc-arg-syms))))) ]
    fn-def ))

The implementation function fn-add-impl is the stub function that will marshall the args x and y into a netconf message for transmission to the Calculator server. It will also receive the rpc-reply message from the server, unmarshal the result of the calculation, and return it to the client.

YANG ABNF Specification

The YANG syntax is described via Augmented Backus-Naur Form (ABNF) syntax as specified in RFC 6020 and RFC 7950. The yang-parser uses a modified version of the ABNF syntax definition file designed to correct errors, remove ambiguities, and ease understanding and use of YANG files. The YANG ABNF syntax definition files are located in the ./resources directory.

Running Unit Tests

A full suite of unit tests is included to verify correct installation & dependencies. To run:

> lein test

-------------------------------------
   Clojure 1.8.0    Java 1.8.0_111
-------------------------------------

Testing tst.parse.calc0

Testing tst.parse.calc1

Testing tst.parse.calc2

Testing tst.parse.core

Testing tst.parse.demo

Testing tst.parse.orig.calc0

Testing tst.parse.orig.calc1

Testing tst.parse.orig.calc2

Testing tst.parse.orig.core

Testing tst.parse.orig.demo

Ran 60 tests containing 386 assertions.
0 failures, 0 errors.

Passed all tests
Finished at 21:28:37.086 (run time: 12.964s)

Requirements

  • Clojure 1.8.0

  • Java 1.8

Change Log

TBD

License

Copyright © 2017

Distributed under the Eclipse Public License, the same as Clojure.

Developed using IntelliJ IDEA with the Cursive Clojure plugin.

IntelliJ Cursive

ToDo List (#todo)

automated api generation
api stubs generation for client & server
netconf msg generation, parsing, validation

About

Parse, validate, manipulate, and use YANG modules.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Clojure 99.3%
  • Shell 0.7%