The yang-parser API Docs are posted on GitHub Pages
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.
Suppose we wished to create a simple Calculator service. We might choose to specify the semantics of or service in a YANG file:
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.
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.
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.
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)
Copyright © 2017
Distributed under the Eclipse Public License, the same as Clojure.
Developed using IntelliJ IDEA with the Cursive Clojure plugin.