Skip to content

Mappings

Lyle Bertz edited this page Jan 29, 2018 · 32 revisions

This page represents work for mapping the concepts used in YANG to JSON schema and Swagger. If there is a distinct difference in the end result of the mappings between YANG => JSON schema and YANG => Swagger it will be called out.

Commonly Mapped Information

Common Attributes

The following attributes are common to YANG, JSON Schema and Swagger. Their mapping is considered to be direct and not discussed further.

  • description
  • status - Maps to deprecated. The value is NOT included in the specific schema version when the status is obsolete. A prior version of the API MUST have marked 'deprecated = true'.
  • reference - When the entity contains an External Documentation Object the information is placed there. If not present and the entity supports Tags a Tag with name = "references", description = "Reference(s)." and the External Doc Reference is included (SEE NOTE 1).

NOTE 1 - Although the Tags Object has the attributed listed as 'externalDocs' it does not appear to a be a list but rather a single value.

if-feature mapping

If a YANG entity is not supported by the Server the information should not be present in the appropriately mapped entity. This is straight forward for operations but can be problematic for Schema Objects. The generator will assume all feature support. Further study is required on a more helpful mapping.

Container Statement

A Container is always mapped as a Schema Object

Leaf Statement

A Leaf is mapped as an Object IF the type is not a simple, direct mapping.

Leaf-list Statement

A leaf list is mapped as a JSON Schema list with the array item referencing the type.

List Statement

A leaf list is mapped as a JSON Schema list with the array item referencing a Schema Object.

Anydata Statement

There is no direct equivalent to this extension mechanism in Swagger. Rather a new version of the API / Schema is required.

Anyxml Statment

Anydata is recommended for use. Anyxml is not supported.

Node Paths

In YANG, data nodes are identified by absolute XPATH expressions or, for RestConf are identified using a URI-encoded path expression - see Section 3.5.3 of RFC 8040.

The following transformation is applied:

  1. The ':' in an api-identifer is replaced with an underscore.
  2. If a list-instance per Section 3.5.3.1 of RFC 8040 is present, a. The '/' separator replaces the '=' value. b. The list-item it is surrounded by the path templating {} delimiters.

NOTE - It is unclear in the Swagger definition that multi-valued lists are permitted in Path Templating.

Derived Types (typedef)

The type definition is intended to be reused. In JSON Schema, the "definitions" sub-schema provides support for reusable schema elements. As type definitions may be used by Choices and Grouping statements, they are placed as before them in the schema document.

Type definitions will be placed under the schema path "#/definitions/type-definitions/definitions".

When type definitions are used the label of the attribute using the type definition may be different than the name of the type definition.

The use of the type definition "foo" in attribute "a" would appear as follows:

YANG

typedef foo {
   type integer;
}

JSON Schema (using only accepted Swagger 3.0 keywords)

{
 "definitions" : {
      "type-definitions" : {
             "definitions" : {
                  "foo" : { "type" : "integer" }
             }
      }
  },
  "type" : "object",
   "properties" : {
       "datum" : { "$ref" : "#/definitions/type-definitions/definitions/foo" }
   }
}

Example

{
    "datum" : 91
}

Reusable Node Groups (grouping and uses statements)

Like type definitions, grouping statements will reside under a definitions. The path "#/definitions/groupings/definitions" is used.

NOTE - To be updated in next software release YANG

grouping fooSchema {
    leaf foo1 {
      mandatory true; 
      type integer; 
    }
    leaf foo2 { 
      mandatory true;
      type boolean; 
    }
}
grouping barSchema {
     leaf bar1 {
      mandatory true; 
      type integer; 
    }
    leaf bar2 { 
      mandatory true;
      type integer; 
    }
}

container Example {
    uses fooSchema;
    uses barSchema;
}

JSON Schema (using only accepted Swagger 3.0 keywords)

{
    "definitions": {
        "fooSchema": {
            "properties": {
                "foo1": {"type": "number"},
                "foo2": {"type": "boolean"}
            },
            "required": [ "foo1", "foo2" ]
        },
        "barSchema": {
            "properties": {
                "bar1": {"type": "integer"},
                "bar2": {"type": "integer"}
            },
            "required" : [ "bar1", "bar2" ]
        },
    "Example" : {
       "type" : "object",
       "allOf" : [
            { "$ref" : "#/definitions/fooSchema" },
            { "$ref" : "#/definitions/barSchema" }
       ]
    }
    },
    "properties" : {
       "Ex" : { "$ref" : "#/definitions/Example" }
     }
}

Example

{
   "Ex" : {
       "foo1" : 123,
       "bar1" : 456,
       "foo2" : true,
       "bar2" : 789
    }
}

Choices

The Choice statement does permit properties with the same to appear in different Case statements because it represents alternatives (Choice statements), only one of which may be present. This is not a concern when all Case statements have a required attribute. However, when one of the Case statements does not require an attribute it is possible to validate against the Case when no properties in any Case statements are present. Case statements MAY allow additional properties to be present as well.

Swagger 3.0 does not support propetryNames nor patternProperties. Thus, JSON Schema translation MUST NOT include those keywords.

The strategy applied is to leverage the "not" keyword and a "required" property that includes in a Case statement the names of all properties in all other Case statements in the Choice.

The follow example is provided.

YANG

choice choice1 {
   mandatory true;
   case a {
     leaf a {
        type string;
     }
   }
   case b {
     leaf b {
        type string;
     }
   }        
}

JSON Schema (using only accepted Swagger 3.0 keywords)

{
    "definitions" : {
       "choice1" : {
         "oneOf" : [{
             "properties" : {
                 "a" : { "type" : "string" }
             },
             "not" : { "required" : [ "b" ] }
         }, { 
             "properties" : {
                  "b" : { "type" : "string" }
              },
             "not" : { "required" : [ "a" ] }
         }]
       },
       "y" : {
           "type" : "object",
           "$ref" : "#/definitions/choice1"
       }
     },
     "type" : "object",
     "properties" : {
         "ChoiceExample" : { "$ref" : "#/definitions/y"  }
     },
    "required" : [ "ChoiceExample" ]
}

Examples

{
  "ChoiceExample" : {
       "a" : "456"
   }
}

{
  "ChoiceExample" : {
       "b" : "123"
   }
}

The following fails because it is not the correct data type for "b"

{
  "ChoiceExample" : {
       "b" : 123
   }
}

The following fails because it contains both Case values

{
  "ChoiceExample" : {
       "b" : "123",
       "a" : "456"
   }
}

The following is invalid because it evaluates to both case a and case b.

{
  "ChoiceExample" : {
   }
}

If the Choice statement is not mandatory, it is defined as two nested "oneOf" statements.

JSON Schema (using only accepted Swagger 3.0 keywords)

...
     "oneOf" : [ {
             "properties" : { }
      } , {
         "oneOf" : [{
             "properties" : {
                 "a" : { "type" : "string" }
             },
             "not" : { "required" : [ "b" ] }
         }, { 
             "properties" : {
                  "b" : { "type" : "string" }
              },
             "not" : { "required" : [ "a" ] }
        } ] 
     } ]
...

in which case the empty object evaluates to true

{
  "ChoiceExample" : {
   }
}

If the default statement is used, a nested "oneOf" statement is used with the default Case being in the first oneOf schema and the second schema is comprised of an "oneOf" for all Case values that are not the default and an "anyOf". The "anyOf" contains the required attributes for each Case or, if none are present, it contains a list of required statements that contains each individual item in the Case statement.

minProperties is not used as Choice could be part of a larger object.

YANG

choice choice1 {
   default "c";
   case a {
     leaf a {
        type string;
     }
   }
   case b {
     leaf b {
        type string;
     };
     leaf d {
        mandatory true;
        type integer;
     }
   }  
   case c {
     leaf c {
        type string;
     }
   }      
}

JSON Schema (using only accepted Swagger 3.0 keywords)

{
    "definitions" : {
       "choice1" : {
         "oneOf" : [ {
             "properties" : {
                 "c" : { "type" : "string" }
             },
             "not" : { "required" : [ "a", "b" ] }
         } , {
         "anyOf" : [
             { "required" : [ "a" ] },
             { "required" : [ "d" ]}
         ],
         "oneOf" : [{
             "properties" : {
                 "a" : { "type" : "string" }
             },
             "not" : { "required" : [ "b", "c" ] }
         }, { 
             "properties" : {
                  "b" : { "type" : "string" },
                  "d" : { "type" : "integer" }
              },
              "required" : [ "d" ] 
        } ] } ]
       },
       "y" : {
           "type" : "object",
           "$ref" : "#/definitions/choice1"
       }
     },
     "type" : "object",
     "properties" : {
         "ChoiceExample" : { "$ref" : "#/definitions/y"  }
     },
    "required" : [ "ChoiceExample" ]
}

RPC entity information and substatements

RPC Entity / Substatement Swagger 3.0 Entity Path
rpc name Operation.operation
description Operation.description
grouping Resides in path "definitions/rpc_<rpc_name>"
if-feature See if-feature mapping
rpc output Operation.Response Object.Content[key = "application/json"].Media Object.Schema
rpc input Operation.Request Body Object.Content[key = "application/json"].Media Object.Schema
reference Operation.externalDocs
status Operation.deprecated
typedef Resides in path "definitions/rpc_<rpc_name>"

Extending Data Models (augment)

The body of each augment is mapped similar to a grouping. The target (argument) of the augment then has the properties added to it (similar to the grouping strategy). This creates a new version of the object. Versioning is handled by the Swagger 3.0 guidance on Inheritance and Polymorphism.

action Statement

action statements are mapped in a manner similar to RPC statements. However, they also appear in a distinct node path that MUST be mapped as part of the Swagger Operation Path (see Node Paths).

notification Statement

The substatements of a notification is mapped as a Schema object.

If the notification appears as in the top level of a tree it is an Operation. How subscription occurs is not supported by this mapping (TODO).

If the notification appears under node in the YANG tree, it is a Call back to an Operation on the node. If there is no operation defined a simple operation with no input or value is created and the Callback(s) are defined.

identity Statement

The base path of an identity is defined as the concatenation of separator and the base path of the identity it is derived from, i.e. the list of all ancestor identities, the '/' separator and identity's name.

For example,

module x { 
...
identity foo { } // Base path "/x:foo"
identity bar {
   base "foo";
}  // Base path "/x:foo/x:bar"
...

The JSON Schema for an identity is a string that contains a pattern which enforces an identity string to contain the same base path at the beginning of the string. The module name is always added (separated by a ':').

JSON Schema

"definitions" : {
  "foo" : {
      "type" : "string",
      "pattern" : "^/x:foo[/.*]"
   },
   "bar" : {
      "type" : "string",
      "pattern" : "^/x:foo/x:bar[/.*]"      
   }
}

Built-In Types

Integer Types

Type JSON Schema Type Definition
int8 integer { "type":"integer", "minimum":-128, "maximum":127 }
int16 integer { "type":"integer", "minimum":-32768, "maximum":32767 }
int32 integer { "type":"integer", "minimum":-2147483648, "maximum":2147483647 }
int64 integer { "type":"integer", "minimum":-9223372036854775808, "maximum":9223372036854775807 }
uint8 integer { "type":"integer", "minimum":0, "maximum":255 }
uint16 integer { "type":"integer", "minimum":0, "maximum":65535 }
uint32 integer { "type":"integer", "minimum":0, "maximum":4294967295 }
uint64 integer { "type":"integer", "minimum":0, "maximum":18446744073709551615 }

decimal64

The following type definition is used.

{ 
  "type" : "object",
  "properties" : [
    "value" : {  "type":"integer", "minimum"=-9223372036854775808, "maximum"=9223372036854775807 },
    "fraction" : { "type":"integer", "minimum"=0, "maximum"=18 }
  ]
}

string

This is a direct mapping to JSON Schema.

boolean

This is a direct mapping to JSON Schema.

enumeration

Three strategies exist for mapping label=>integer enumerations:

  • label mapping - the labels are sent over the wire. Integer mappings are provided in the descriptions.
  • integer mapping - integers are sent over the wire. Label mappings are provided in the descriptions.
  • hybrid mapping - a "oneOf" sub schema is used to provide both options. Mappings are provided in the descriptions.

Unfortunately, the mapping between types can only be provided in the descriptions. The label is provided first and integer is surrounded by (). It is printed in order of appearance. When an integer value has be automatically added it is delimited by []

YANG

 leaf myenum {
       type enumeration {
         enum zero;
         enum one;
         enum seven {
           value 7;
         }
       }
     }

is mapped as follows:

# Strategy 1
"myenum" : {
   "description" : "zero[0], one[1], seven(7)."
   "type" : "string",
   "enum" : [ "zero", "one", "seven" ]
}

# Strategy 2
"myenum" : {
   "description" : "zero[0], one[1], seven(7)."
   "type" : "integer",
   "enum" : [ 0, 1, 7 ]
}

# Strategy 3
"myenum" : {
   "description" : "zero[0], one[1], seven(7)."
   "oneOf" : [ {
      "type" : "string",
      "enum" : [ "zero", "one", "seven" ]
    }, {
      "type" : "integer",
      "enum" : [ 0, 1, 7 ]
   } ]
}

bits

This is a string with the value "pattern": "[01]{n}" where n is the max position.

The value / position information is added to the description.

binary

This is a string with the value "contentEncoding": "base64" added.

leafref

This is a string with the value "contentEncoding": "xpath" added.

NOTE - The "require-instance" substatement is not currently enforced.

identityref

The identityref is a string mapped with a pattern according to the identity mapping (see above).

empty

This is treated as an empty object in JSON Schema.

union

This is equivalent to a JSON Schema "oneOf" schema.

instance-identiifer

This is a string with the value "contentEncoding": "xpath" added.

extension Statement

extension statements are not supported in the mapping.