Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This page documents the existing Yang Parser used in ONAP and OpenDayLight and will investigate if they can be used for it fulfill the needs of the C&PS.

Resources

*Although this documentation link is to the latest ODL doc revision, it is very outdated and the code examples need significant updates, see findings in Mini-PoC below (bug reported: https://jira.opendaylight.org/browse/DOCS-126)

Overview

The Yang parser used in ONAP (CCSDK / SDNC) was developed (and still is) a OpenDayLight Library.

There are 2 different usage patterns within CCSDK, though.

Most of CCSDK/SDNC is using Yang primarily to define their northbound interfaces. In that context, there’s a maven plugin (org.opendaylight.yangtools:yang-maven-plugin) that is used to generate source code from the Yang model. Our application code in that case doesn’t really do anything directly with the Yang, since all of the that is handled for us by the generated code.

In ccsdk/sli/plugins, there is a plugin called restconf-client which was contributed by Huawei. That code uses the yangtool parser more directly so that it can interpret the results being returned when it calls a restconf interface. 

Mini-PoC

Overview

The Yang parser used in ONAP (CCSDK / SDNC) was developed (and still is) a OpenDayLight Library.

There are 2 different usage patterns within CCSDK, though.

Most of CCSDK/SDNC is using Yang primarily to define their northbound interfaces. In that context, there’s a maven plugin (org.opendaylight.yangtools:yang-maven-plugin) that is used to generate source code from the Yang model. Our application code in that case doesn’t really do anything directly with the Yang, since all of the that is handled for us by the generated code.

In ccsdk/sli/plugins, there is a plugin called restconf-client which was contributed by Huawei. That code uses the yangtool parser more directly so that it can interpret the results being returned when it calls a restconf interface. 

Resources

Additional Resources (still to be examined)

*Although this documentation link is to the latest ODL doc revision, it is very outdated and the code examples need significant updates, see findings in Mini-PoC below (bug reported: https://jira.opendaylight.org/browse/DOCS-126)

Mini-PoC

To help this evaluation To help this evaluation I will create a small sample project with the goal to pare a yang file using the ODL Yang Tools. I will report my findings here

...

The documentation mentioned above lists many modules but the code examples do not clarify which exactly are needed to parse a Yang model and Yang file data files in java code.

  • To be able to use yangtools teh project needs to use the mdsal 'binding-parent' pom
  • The module yang-parser-impl contains all code required to parse yang files
  • SLF4J has been added as the implementation requires a logger enabled


Code Block
language
Code Block
languagexml
themeMidnight
titlepom.xml
collapsetrue
    <properties>
		        <maven.compiler.version>3.8.1</maven.compiler.version>
		        <maven.compiler.release>11</maven.compiler.release>
		<org.opendaylight.        <odl.yangtools.version>5.0.3<5</org.opendaylightodl.yangtools.version>
	    </properties>
	
	    <build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
				                <version>${maven.compiler.version}</version>
				<configuration>
					                <configuration>
                    <release>${maven.compiler.release}</release>
				                </configuration>
			            </plugin>
		        </plugins>
	    </build>

	    <dependencies>
		<dependency>
			        <dependency>
            <groupId>org.opendaylight.yangtools</groupId>
			<artifactId>yang            <artifactId>yang-parser-api</artifactId>
			<version>${org.opendaylight            <version>${odl.yangtools.version}</version>
		        </dependency>

		<dependency>
			        <dependency>
            <groupId>org.opendaylight.yangtools</groupId>
			            <artifactId>yang-parser-impl</artifactId>
			            <version>${orgodl.opendaylight.yangtools.version}</version>
		        </dependency>

		<dependency>
			<groupId>org        <dependency>
            <groupId>org.opendaylight.yangtools</groupId>
			            <artifactId>yang-model-util</artifactId>
			<version>${org.opendaylight            <version>${odl.yangtools.version}</version>
		        </dependency>

		<dependency>
			        <dependency>
            <groupId>org.opendaylight.yangtools</groupId>
			            <artifactId>yang-data-codec-xml</artifactId>
			            <version>${orgodl.opendaylight.yangtools.version}</version>
		        </dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.6.1</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			        <dependency>
            <groupId>org.opendaylight.yangtools</groupId>
            <artifactId>yang-data-codec-gson</artifactId>
            <version>${odl.yangtools.version}</version>
        </dependency>
        <!-- SLF4J API -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.1</version>
		        </dependency>
	</dependencies>

Documentation Code Updates

The sample code provided in the documentation is faulty (using == for assigning?!) and is using some long deprecated and even removed classes and methods.

Bug reported: https://jira.opendaylight.org/browse/DOCS-126

Code Block
languagejava
themeMidnight
titleCorrected Code
linenumberstrue
collapsetrue
        <!-- LOG4J -->
    static YangParserFactory PARSER_FACTORY;

    static {<dependency>
        final Iterator<YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
<groupId>org.slf4j</groupId>
         if (!it.hasNext()) {   <artifactId>slf4j-log4j12</artifactId>
            throw new IllegalStateException("No YangParserFactory found");<version>1.6.1</version>
        }</dependency>
    </dependencies>

Note. SLF4J has also been added as the implementation requires an enabled logger

Documentation Code Updates

The sample code provided in the documentation is faulty (using == for assigning?!) and is using some long deprecated and even removed classes and methods.

Bug reported: https://jira.opendaylight.org/browse/DOCS-126

Code Block
languagejava
themeMidnight
titleCorrected Code
linenumberstrue
collapsetrue
    static YangParserFactory PARSER_FACTORY;

    static {    PARSER_FACTORY = it.next();
    }

    ...

        File file = new File(ClassLoader.getSystemClassLoader().getResource("example.yang").getFile());
        YangTextSchemaSource source = YangTextSchemaSource.forFile(file);
        
        final YangParserIterator<YangParserFactory> yangParserit = PARSER_FACTORYServiceLoader.createParser(StatementParserMode.DEFAULT_MODEload(YangParserFactory.class).iterator();
        yangParser.addSource(source);
if (!it.hasNext()) {
           SchemaContext schemaContextthrow =new yangParser.buildEffectiveModel(IllegalStateException("No YangParserFactory found");
        }
        PARSER_FACTORY = schemaContextit.getModules()

This is the kind of object (module) that gets created:

Image Removed

Generated Java Object Structures per Yang concept

Object Structure

The SchemaContext object generated by the Yang Parser in java has the following possible structures (java collections)

Gliffy Diagram
macroId8eafa348-6609-4eae-a733-beb5a2766488
nameYang Parser Java Object View
pagePin4
 

ChildNodes are implemented as a Map <QName, DataSchemaNode>

The Class DataSchemaNode can represent a Yang Leaf, List or Container

Main data types

The package org.opendaylight.yangtools.yang.model.util.type contains classes for all the possible data types including:

  • BaseBinaryType
  • BaseBitsType
  • BaseBooleanType
  • BaseDecimalType
  • BaseEnumerationType
  • BaseInt8Type
  • BaseInt16Type
  • BaseInt32Type
  • BaseInt64Type
  • BaseStringType
  • BaseUint8Type
  • BaseUint16Type
  • BaseUint32Type
  • BaseUint64Type

There are also some special data types such as:

  • BaseEmptyType
  • BaseIdentityrefType
  • BaseInstanceIdentifierType
  • BaseLeafrefType
  • BaseUnionType
next();
    }

    ...

        File file = new File(ClassLoader.getSystemClassLoader().getResource("example.yang").getFile());
        YangTextSchemaSource source = YangTextSchemaSource.forFile(file);
        
        final YangParser yangParser = PARSER_FACTORY.createParser(StatementParserMode.DEFAULT_MODE);
        yangParser.addSource(source);
        SchemaContext schemaContext = yangParser.buildEffectiveModel();

        schemaContext.getModules()

This is the kind of object (module) that gets created:

Image Added

Generated Java Object Structures per Yang concept

Object Structure

The SchemaContext object generated by the Yang Parser in java has the following possible structures (java collections)

Gliffy Diagram
macroId8eafa348-6609-4eae-a733-beb5a2766488
nameYang Parser Java Object View
pagePin5
 

SchemaTree is implemented as a ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> 

The SchemaTree can represent a tree of any Yang Leaf, List or Container. All leafs implement the  LeafStatement interface which provides methods for many Yang Language features including

  • getType()
  • getUnits()
  • getMandatory()
  • getWhenStatement()
  • getMustStatments()

Main data types

The package org.opendaylight.yangtools.yang.model.util.type contains classes for all the possible data types including:

  • BaseBinaryType
  • BaseBitsType
  • BaseBooleanType
  • BaseDecimalType
  • BaseEnumerationType
  • BaseInt8Type
  • BaseInt16Type
  • BaseInt32Type
  • BaseInt64Type
  • BaseStringType
  • BaseUint8Type
  • BaseUint16Type
  • BaseUint32Type
  • BaseUint64Type

There are also some special data types such as:

  • BaseEmptyType
  • BaseIdentityrefType
  • BaseInstanceIdentifierType
  • BaseLeafrefType
  • BaseUnionType

And also 'restricted' versions of the base types such as:

  • RestrictedStringType
  • RestrictedUint64Type


DescriptionYangJava Object ViewNotesXML Validation

JSON
Validation

Datatypes and basic constraints

Basic String

leaf response-code {
  type string;
}

Image Added

TypeStatement TypeAwareDeclaredStatement.getType()YesYes
Mandatory Basic String

leaf response-code {925px
  type string;
    mandatory "true";
}

Image Added

Optional<MandatoryStatement> MandatoryStatementAwareDeclaredStatement.getMandatory()

NoNo
Limited String leaf pnf-name {
  type string {
    length "0..256";
}

Image Added

Optional<LengthConstraint> AbstractLengthRestrictedType.getLengthConstraint()

YesYes
typedef (String) with pattern

typedef dotted-quad {
  type string {
pattern
  '(([0-9] ...';
  }
}

leaf address {
   type dotted-quad;
   mandatory "true";
}

Image Added

List<PatternConstraint> RestrictedStringType.getPatternConstraints()YesYes
Limited uint64 leaf cid {
  type uint64 {
     range "0..503";
  }
}

Image Added

org.opendaylight.yangtools.yang.model.util.type.RestrictedUint64TypeYesYes
boolean with default value

leaf blacklisted {
  type boolean;
  default 1;
}

Image Added

org.opendaylight.yangtools.yang.model.util.type.DerivedBooleanTypeN/AN/A

Unique

Unique list server {
  key "name";
  unique "ip port";
  leaf name {
    type string;
  }
  leaf ip {
    type dotted-quad;
  }
  leaf port {
    type uint32;
  }}

Image Added

org.opendaylight.yangtools.yang.model.api.stmt.UniqueStatementNoNo

Choice

Choicechoice transfer-method {
  leaf transfer-interval {
    type uint64 { range "15..2880"; }
    units minutes; }
  leaf transfer-on-commit {
  type empty;
  }}

Image Added

org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatementN/AN/A

Must

Must leaf ifType {
type enumeration {
enum ethernet;
enum atm;}}
leaf ifMTU {
type uint32;}
must "ifType != 'ethernet' or "
+ "(ifType = 'ethernet' and ifMTU = 1500)"
{
error-message 466px"An ethernet MTU must be 1500";}

Image Added

org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageStatementNoNo

When

When
leaf a {
    type boolean;
}
leaf b {
    type string;
    when "../a = 'true'";
}

Image Added

org.opendaylight.yangtools.yang.model.api.stmt.WhenStatementNoNo

Extension

Extension declaration

extension store-state-ext {
argument duration;
description "An extension to enable
state-storage for any attribute.
Use duration to specify how long: nnn<h|d|y>";

}

Image Added


N/A
Extension usage leaf attribute-with-temporal-storage {
  type string;
  cm-notify-api:store-state-ext "3d";
// store state 3 days

}

Image Added

org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement

which extends UnknownStatement:

public interface UnknownStatement<A> extends DeclaredStatement<A> {
default @Nullable A getArgument()
DescriptionYangJava Object ViewNotesXML Validation

Datatypes and basic constraints

Basic String

leaf response-code {
  type string;
}

Image Removed

YesMandatory Basic String

leaf response-code {
  type string;
    mandatory "true";
}

Image Removed

NoLimited String leaf pnf-name {
  type string {
    length "0..256";
}

Image Removed

Specialized class to hold length limitation

Yestypedef (String) with pattern

typedef dotted-quad {
  type string {
pattern
  '(([0-9] ...';
  }
}

leaf address {
   type dotted-quad;
   mandatory "true";
}

Image Removed

Checked by XmlParserYesLimited unint64 leaf cid {
  type string {
  length "0..52";
}

Image Removed

Probably, TBCboolean with default value

leaf blacklisted {
  type boolean;
  default 1;
}

Image Removed

N/A

Unique

Unique list server {
  key "name";
  unique "ip port";
  leaf name {
    type string;
  }
  leaf ip {
    type dotted-quad;
  }
  leaf port {
    type uint32;
  }}

Image Removed

NOT checked by XmlParserNo

Choice

Choicechoice transfer-method {
  leaf transfer-interval {
    type uint64 { range "15..2880"; }
    units minutes; }
  leaf transfer-on-commit {
  type empty;
  }}

Image Removed

TBC

Must

Must leaf ifType {
type enumeration {
enum ethernet;
enum atm;}}
leaf ifMTU {
type uint32;}
must "ifType != 'ethernet' or "
+ "(ifType = 'ethernet' and ifMTU = 1500)" {error-message 466px"An ethernet MTU must be 1500";}

Image Removed

TBC

When

When
leaf a {
    type boolean;
}
leaf b {
    type string;
    when "../a = 'true'";
}

Image Removed

TBC

Extension

Extension declaration extension store-state-ext {
argument duration;
description "An extension to enable state-storage for any attribute. Use duration to specify how long: nnn h|d|y";
}

Image Removed

N/AExtension usage leaf attribute-with-temporal-storage {
  type string;
  cm-notify-api:store-state-ext "3 d"; // store state 3 days
}

Image Removed

extension is stored as 'UnknownNode' and refers back to the extension declarationN/A

Augmentation

augment "server" {
  when "port = '8787'";
    leaf enable-debug {
      type boolean;
    }
}

Image Removed

N/A

RPC

rpc
rpc nbrlist-change-notification {
description
"RAN Neighbor List change notification to configure RuntimeDB";
input {
:
}
output {
:
}
}

Image Removed

N/Arpc input input {
leaf fap-service-number-of-entries-changed {
type uint64;
description
"Number of cells for which neighbor list has changed";
}
list fap-service {
key "alias";
leaf alias {
type string {
length "1..64";
}
}
leaf cid {
type string
 {
length "0..52"
return argument();
}
}
uses x-0005b9-lte-g;
leaf lte-cell-number-of-entries
}
}
N/A

Augmentation


augment "server" {
  when "port = '8787'";
    leaf enable-debug {
      type boolean;
    }
}

Image Added

The additional leaf just appears in the SchemaTree (without any reference that it is an augmentation)N/A

RPC

rpc
rpc nbrlist-change-notification {
description
"RAN Neighbor List change
type uint64
notification to configure RuntimeDB";
input {
description
    :
}
"Number
 
of
output 
cells
{
in
 
a
 
neighbor
 
list
 
that
 
was
: 
changed";

}
}
}

Image Added


N/A
rpc input
 input {
list
leaf 
lte
fap-
ran
service-
neighbor
number-
list
of-
in-use-lte-cell-changed
entries-changed {
type uint64;
key
 
"plmnid
 
cid";
description
"Number of cells for
uses
which 
lte-ran-
neighbor
-list-in-use-lte-cell-g
 list has changed";
}
description
 list fap-service {
key
"Changed/Modified List of cells in a neighbor list for this fap service";
"alias";
leaf alias {
}
  
}
}

Image Removed

N/Arpc outputoutput
type string {
uses cm-notification-response;
}

Image Removed

N/A

Data Parsing and validation

XML Parsing

...

languagejava
themeMidnight
titleXML Parsing Example
linenumberstrue
collapsetrue

...

    length "1..64";
}

...

 

...

 

...

}

...

 

...

     leaf cid {

...

 

...

 

...

 

...

     type string {

...

 

...

 

...

 

...

 

...

    

...

length "0..52";
}

...

 

...

 

...

 

...

}
uses x-0005b9-lte-g;

...

 

...

 

...

leaf lte-cell-number-of-entries {
type uint64;

...

 

...

 

...

 

...

 

...

 

...

 description

...

"Number 

...

of 

...

cells 

...

in a neighbor list that was changed";

...

}

...

 

...

 

...

    list lte-ran-neighbor-list-in-use-lte-cell-changed {
key

...

"plmnid cid";

...

uses lte-ran-neighbor-list-in-use-lte-cell-g;
description
"Changed/Modified List of cells

...

in a neighbor list for this fap service";
     }
}
}

Image Added


N/A
rpc output
output {
uses cm-notification-response;
}

Image Added


N/A

Yang Data Parsing and Validation

XML

*Note: the DataSchemaNode being used when creating the XmlParserStream HAS to be the root node of the xml data!

XML Validation Findings

  • The XML Parser is found to do basic data type checks including range checks and (regex) pattern validation. If the dat input doesn't conform those a clear exception detailing the problem is thrown
  • Features such as 'mandatory' and 'unique' are to be validated
  • More advanced features such as 'must', 'when', 'choice' etc have not yet been tested

The table in the sections above has  a column with the XML validation findings.

...

Parsing

Code Block
languagejava
themeMidnight
titleJSON XML Parsing Example
linenumberstrue
collapsetrue
		SchemaContext schemaContext = ... (see previous snippets)
        final JSONCodecFactoryModule jsonCodecFactorymodule = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContextschemaContext.findModules("ultraconfig-interfaces").iterator().next();
        QName qName = QName.create(module.getQNameModule(),"interfaces");
        final NormalizedNodeResultOptional<DataSchemaNode> resultnode = new NormalizedNodeResult();
        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, jsonCodecFactory);
        final InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("example2data.json");
        final JsonReader jsonReader = new JsonReader(new InputStreamReader(resourceAsStream));
        jsonParser.parse(jsonReader);
        final NormalizedNode<?, ?> transformedInput = result.getResult();

...

module.findDataChildByName(qName);
        if (node.isPresent()) {
            final InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("example2data.xml");
            final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
            final NormalizedNodeResult result = new NormalizedNodeResult();
            final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
            final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, node.get() );
            xmlParser.parse(reader);
            final NormalizedNode<?, ?> transformedInput = result.getResult();
        }

*Note: the DataSchemaNode being used when creating the XmlParserStream HAS to be the root node of the xml data!

XML Validation Findings

  • The XML Parser is found to do basic data type checks including range checks and (regex) pattern validation. If the dat input doesn't conform those a clear exception detailing the problem is thrown
  • Features such as 'mandatory' and 'unique' are to be validated
  • More advanced features such as 'must', 'when', 'choice' etc have not yet been tested

The table in the sections above has a column with the XML validation findings.

JSON Parsing

Code Block
languagejava
themeMidnight
titleJSON Parsing Example
linenumberstrue
collapsetrue
		SchemaContext schemaContext = ... (see previous snippets)
        JSONCodecFactory jsonCodecFactory = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext);
        final NormalizedNodeResult result = new NormalizedNodeResult();
        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, jsonCodecFactory);
        final InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("example2data.json");
        final JsonReader jsonReader = new JsonReader(new InputStreamReader(resourceAsStream));
        jsonParser.parse(jsonReader);
        final NormalizedNode<?, ?> transformedInput = result.getResult();

JSON Validation Findings

  • As expected the parsing of string, originating form XML or JSON is done by the same code and the results are identical to those for XML Data Parsing

Conclusion

Pros

  1. EXTENSIVE - The YangTools model parser is comprehensive and covers all possible Yang Language elements we might require
  2. AVAILABLE - Extensively used throughout ONAP and ODL projects (able to convert to Java objects, code & API). These parsers are already used in many ONAP platform projects.
  3. MATURE CODE - Mature code, dates back to 2013 with contributions from many companies including Cisco and Redhat and Pantheon Tech. (No need for licenses, these are also open source Yang parser). ODL is open source.
  4. VALIDATION - Testing with parsing of JSON and XML data with validated for a (parsed) model included. Files to objects and vice versa is possible. Model violation & compilation validation is available.
  5. OBJECTIVES - Meets our two high-level requirements & objectives. Parse models from SDC into Java objects relate to persistence of data. Parsing of documents compliant to those schemas.

Cons

  1. LEARNING CURVE - Due to its completeness it also is a complicated piece of software which will take some time to get familiar with.
  2. DOCUMENTATION - Documentation out of date, this page hopes to address that somewhat