RFC 6020
YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)
YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF) ✨
okay bestie, welcome to RFC 6020 🎀 this is part 1 of 28, so buckle up 💅
- Stream: Internet Engineering Task Force (IETF) 🦋
- Request for Comments: 6020
- Editor: M. Bjorklund, Ed. (from Tail-f Systems) 💜
- Category: Standards Track 👑
- Date: October 2010
- ISSN: 2070-1721
- Obsoletes / Updates: the title block doesn't list anything this one obsoletes or updates, so it stands on its own 🌸
Abstract
YANG is a data modeling language 🧚 It's used to model the configuration and state data that the Network Configuration Protocol (NETCONF) manipulates, plus NETCONF remote procedure calls and NETCONF notifications. So basically, it's the blueprint language for how NETCONF data is shaped 💖
Status of This Memo
okay so the status part is giving very official energy 🥹 this is an Internet Standards Track document ✨
It's a product of the Internet Engineering Task Force (IETF), which means it reflects what the IETF community agreed on. It went through public review and got approved for publication by the Internet Engineering Steering Group (IESG). If you want more on Internet Standards, check out Section 2 of RFC 5741 📚
For the latest status, any errata, and how to send feedback, head to http://www.rfc-editor.org/info/rfc6020 🔗
Copyright Notice
bestie, this part is the legal stuff, but I'm keeping every bit of it 🫶
The copyright belongs to the IETF Trust and the document's authors, dated 2010, and all rights are reserved 💅 This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info), as they stood on the publication date. Please actually read those, because they spell out your rights and restrictions for this document 🙈
Any Code Components pulled out of this document have to include the Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. They come with no warranty, same as the Simplified BSD License says 🌈
Some of the material here may come from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The people who hold the copyright on some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process 😭 So unless you get a proper license from those copyright holders, this document can't be modified outside the IETF Standards Process, and derivative works can't be made outside it either. The only exceptions are formatting it for publication as an RFC or translating it into languages other than English 💜
Table of Contents
Here's the full map of the doc. It's verbatim so you can navigate it like a pro 🗺️
1. Introduction ....................................................8
2. Keywords ........................................................8
3. Terminology .....................................................8
3.1. Mandatory Nodes ...........................................10
4. YANG Overview ..................................................11
4.1. Functional Overview .......................................11
4.2. Language Overview .........................................13
4.2.1. Modules and Submodules .............................13
4.2.2. Data Modeling Basics ...............................13
4.2.3. State Data .........................................18
4.2.4. Built-In Types .....................................18
4.2.5. Derived Types (typedef) ............................19
4.2.6. Reusable Node Groups (grouping) ....................20
4.2.7. Choices ............................................21
4.2.8. Extending Data Models (augment) ....................22
4.2.9. RPC Definitions ....................................23
4.2.10. Notification Definitions ..........................24
5. Language Concepts ..............................................25
5.1. Modules and Submodules ....................................25
5.1.1. Import and Include by Revision .....................26
5.1.2. Module Hierarchies .................................27
5.2. File Layout ...............................................28
5.3. XML Namespaces ............................................29
5.3.1. YANG XML Namespace .................................29
5.4. Resolving Grouping, Type, and Identity Names ..............29
5.5. Nested Typedefs and Groupings .............................29
5.6. Conformance ...............................................30
5.6.1. Basic Behavior .....................................31
5.6.2. Optional Features ..................................31
5.6.3. Deviations .........................................31
5.6.4. Announcing Conformance Information in the
<hello> Message ....................................32
5.7. Data Store Modification ...................................34
6. YANG Syntax ....................................................34
That's the whole roadmap for part 1 🦋 Part 2 is coming up next, so stay tuned, bestie 💖✨
okay the table of contents is still going, bestie 📚✨ here's the rest of the map so you can find your way around the YANG statements 🗺️💖
6.1. Lexical Tokenization ......................................34
6.1.1. Comments ...........................................34
6.1.2. Tokens .............................................34
6.1.3. Quoting ............................................35
6.2. Identifiers ...............................................36
6.2.1. Identifiers and Their Namespaces ...................36
6.3. Statements ................................................37
6.3.1. Language Extensions ................................37
6.4. XPath Evaluations .........................................38
6.4.1. XPath Context ......................................38
6.5. Schema Node Identifier ....................................39
7. YANG Statements ................................................39
7.1. The module Statement ......................................39
7.1.1. The module's Substatements .........................41
7.1.2. The yang-version Statement .........................41
7.1.3. The namespace Statement ............................42
7.1.4. The prefix Statement ...............................42
7.1.5. The import Statement ...............................42
7.1.6. The include Statement ..............................43
7.1.7. The organization Statement .........................44
7.1.8. The contact Statement ..............................44
7.1.9. The revision Statement .............................44
7.1.10. Usage Example .....................................45
7.2. The submodule Statement ...................................46
7.2.1. The submodule's Substatements ......................48
7.2.2. The belongs-to Statement ...........................48
7.2.3. Usage Example ......................................49
7.3. The typedef Statement .....................................49
7.3.1. The typedef's Substatements ........................50
7.3.2. The typedef's type Statement .......................50
7.3.3. The units Statement ................................50
7.3.4. The typedef's default Statement ....................50
7.3.5. Usage Example ......................................51
7.4. The type Statement ........................................51
7.4.1. The type's Substatements ...........................51
7.5. The container Statement ...................................51
7.5.1. Containers with Presence ...........................52
7.5.2. The container's Substatements ......................53
7.5.3. The must Statement .................................53
7.5.4. The must's Substatements ...........................55
7.5.5. The presence Statement .............................56
7.5.6. The container's Child Node Statements ..............56
7.5.7. XML Mapping Rules ..................................56
7.5.8. NETCONF <edit-config> Operations ...................56
7.5.9. Usage Example ......................................57
7.6. The leaf Statement ........................................58
7.6.1. The leaf's default value ...........................58
7.6.2. The leaf's Substatements ...........................59
7.6.3. The leaf's type Statement ..........................59
7.6.4. The leaf's default Statement .......................59
7.6.5. The leaf's mandatory Statement .....................60
7.6.6. XML Mapping Rules ..................................60
7.6.7. NETCONF <edit-config> Operations ...................60
7.6.8. Usage Example ......................................61
7.7. The leaf-list Statement ...................................62
7.7.1. Ordering ...........................................62
7.7.2. The leaf-list's Substatements ......................63
7.7.3. The min-elements Statement .........................63
7.7.4. The max-elements Statement .........................63
7.7.5. The ordered-by Statement ...........................64
7.7.6. XML Mapping Rules ..................................64
7.7.7. NETCONF <edit-config> Operations ...................65
7.7.8. Usage Example ......................................66
7.8. The list Statement ........................................67
7.8.1. The list's Substatements ...........................68
7.8.2. The list's key Statement ...........................68
7.8.3. The list's unique Statement ........................69
7.8.4. The list's Child Node Statements ...................70
7.8.5. XML Mapping Rules ..................................70
7.8.6. NETCONF <edit-config> Operations ...................71
7.8.7. Usage Example ......................................72
7.9. The choice Statement ......................................75
7.9.1. The choice's Substatements .........................76
7.9.2. The choice's case Statement ........................76
7.9.3. The choice's default Statement .....................77
7.9.4. The choice's mandatory Statement ...................79
7.9.5. XML Mapping Rules ..................................79
7.9.6. NETCONF <edit-config> Operations ...................79
7.9.7. Usage Example ......................................79
7.10. The anyxml Statement .....................................80
7.10.1. The anyxml's Substatements ........................81
7.10.2. XML Mapping Rules .................................81
7.10.3. NETCONF <edit-config> Operations ..................81
7.10.4. Usage Example .....................................82
7.11. The grouping Statement ...................................82
7.11.1. The grouping's Substatements ......................83
7.11.2. Usage Example .....................................84
7.12. The uses Statement .......................................84
7.12.1. The uses's Substatements ..........................85
7.12.2. The refine Statement ..............................85
7.12.3. XML Mapping Rules .................................86
7.12.4. Usage Example .....................................86
7.13. The rpc Statement ........................................87
7.13.1. The rpc's Substatements ...........................88
7.13.2. The input Statement ...............................88
7.13.3. The output Statement ..............................89
7.13.4. XML Mapping Rules .................................90
7.13.5. Usage Example .....................................91
7.14. The notification Statement ...............................91
7.14.1. The notification's Substatements ..................92
7.14.2. XML Mapping Rules .................................92
7.14.3. Usage Example .....................................93
7.15. The augment Statement ....................................93
7.15.1. The augment's Substatements .......................94
7.15.2. XML Mapping Rules .................................94
7.15.3. Usage Example .....................................95
7.16. The identity Statement ...................................97
7.16.1. The identity's Substatements ......................97
7.16.2. The base Statement ................................97
7.16.3. Usage Example .....................................98
7.17. The extension Statement ..................................98
7.17.1. The extension's Substatements .....................99
7.17.2. The argument Statement ............................99
7.17.3. Usage Example ....................................100
7.18. Conformance-Related Statements ..........................100
7.18.1. The feature Statement ............................100
7.18.2. The if-feature Statement .........................102
7.18.3. The deviation Statement ..........................102
7.19. Common Statements .......................................105
7.19.1. The config Statement .............................105
7.19.2. The status Statement .............................105
7.19.3. The description Statement ........................106
7.19.4. The reference Statement ..........................106
7.19.5. The when Statement ...............................107
8. Constraints ...................................................108
8.1. Constraints on Data ......................................108
8.2. Hierarchy of Constraints .................................109
8.3. Constraint Enforcement Model .............................109
8.3.1. Payload Parsing ...................................109
8.3.2. NETCONF <edit-config> Processing ..................110
8.3.3. Validation ........................................111
9. Built-In Types ................................................111
9.1. Canonical Representation .................................112
9.2. The Integer Built-In Types ...............................112
9.2.1. Lexical Representation ............................113
9.2.2. Canonical Form ....................................114
9.2.3. Restrictions ......................................114
9.2.4. The range Statement ...............................114
9.2.5. Usage Example .....................................115
9.3. The decimal64 Built-In Type ..............................115
9.3.1. Lexical Representation ............................115
9.3.2. Canonical Form ....................................115
9.3.3. Restrictions ......................................116
9.3.4. The fraction-digits Statement .....................116
9.3.5. Usage Example .....................................117
9.4. The string Built-In Type .................................117
9.4.1. Lexical Representation ............................117
9.4.2. Canonical Form ....................................117
9.4.3. Restrictions ......................................117
9.4.4. The length Statement ..............................117
9.4.5. Usage Example .....................................118
9.4.6. The pattern Statement .............................119
9.4.7. Usage Example .....................................119
9.5. The boolean Built-In Type ................................120
9.5.1. Lexical Representation ............................120
9.5.2. Canonical Form ....................................120
9.5.3. Restrictions ......................................120
9.6. The enumeration Built-In Type ............................120
9.6.1. Lexical Representation ............................120
9.6.2. Canonical Form ....................................120
9.6.3. Restrictions ......................................120
9.6.4. The enum Statement ................................120
9.6.5. Usage Example .....................................121
9.7. The bits Built-In Type ...................................122
1. Introduction 💖
Okay so YANG is a data modeling language 🧚 used to model configuration and state data that gets manipulated by the Network Configuration Protocol (NETCONF), NETCONF remote procedure calls, and NETCONF notifications. It models the operations and content layers of NETCONF too (see the NETCONF Configuration Protocol [RFC4741], Section 1.1). ✨
This document explains the syntax and semantics of the YANG language 📖, how the data model in a YANG module gets represented in the Extensible Markup Language (XML) 🎀, and how NETCONF operations are used to manipulate the data. Iconic, honestly. 💅
2. Keywords 🔑
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14, [RFC2119]. 🌸
These are the special capital-letter words, so pay attention when you see them. They're the ones with real weight behind them 👑.
3. Terminology 📚
Bestie, here's the glossary. Every term below has a specific meaning in this document, so this is your cheat sheet. 🦋
- anyxml: A data node that can contain an unknown chunk of XML data.
- augment: Adds new schema nodes to a previously defined schema node.
- base type: The type from which a derived type was derived, which may be either a built-in type or another derived type.
- built-in type: A YANG data type defined in the YANG language, such as
uint32orstring. - choice: A schema node where only one of a number of identified alternatives is valid. 💜
- configuration data: The set of writable data that is required to transform a system from its initial default state into its current state [RFC4741].
- conformance: A measure of how accurately a device follows a data model.
- container: An interior data node that exists in at most one instance in the data tree. A container has no value, but rather a set of child nodes. 🎀
- data definition statement: A statement that defines new data nodes. One of container, leaf, leaf-list, list, choice, case, augment, uses, and anyxml.
- data model: A data model describes how data is represented and accessed.
- data node: A node in the schema tree that can be instantiated in a data tree. One of container, leaf, leaf-list, list, and anyxml.
- data tree: The instantiated tree of configuration and state data on a device. 🌈
- derived type: A type that is derived from a built-in type (such as uint32), or another derived type.
- device deviation: A failure of the device to implement the module faithfully. 🙈
- extension: An extension attaches non-YANG semantics to statements. The extension statement defines new statements to express these semantics.
- feature: A mechanism for marking a portion of the model as optional. Definitions can be tagged with a feature name and are only valid on devices that support that feature. ⭐
- grouping: A reusable set of schema nodes, which may be used locally in the module, in modules that include it, and by other modules that import from it. The grouping statement is not a data definition statement and, as such, does not define any nodes in the schema tree. 🧁
- identifier: Used to identify different kinds of YANG items by name.
- instance identifier: A mechanism for identifying a particular node in a data tree.
- interior node: Nodes within a hierarchy that are not leaf nodes.
- leaf: A data node that exists in at most one instance in the data tree. A leaf has a value but no child nodes. 🍓
- leaf-list: Like the leaf node but defines a set of uniquely identifiable nodes rather than a single node. Each node has a value but no child nodes.
- list: An interior data node that may exist in multiple instances in the data tree. A list has no value, but rather a set of child nodes.
- module: A YANG module defines a hierarchy of nodes that can be used for NETCONF-based operations. With its definitions and the definitions it imports or includes from elsewhere, a module is self-contained and "compilable". 🦋
- RPC: A Remote Procedure Call, as used within the NETCONF protocol.
- RPC operation: A specific Remote Procedure Call, as used within the NETCONF protocol. It is also called a protocol operation.
- schema node: A node in the schema tree. One of container, leaf, leaf-list, list, choice, case, rpc, input, output, notification, and anyxml.
- schema node identifier: A mechanism for identifying a particular node in the schema tree.
- schema tree: The definition hierarchy specified within a module.
- state data: The additional data on a system that is not configuration data, such as read-only status information and collected statistics [RFC4741]. 📊
- submodule: A partial module definition that contributes derived types, groupings, data nodes, RPCs, and notifications to a module. A YANG module can be constructed from a number of submodules. 🧩
- top-level data node: A data node where there is no other data node between it and a module or submodule statement.
- uses: The "uses" statement is used to instantiate the set of schema nodes defined in a grouping statement. The instantiated nodes may be refined and augmented to tailor them to any specific needs. 💅
3.1. Mandatory Nodes 🫶
Okay, so a mandatory node is one of the following. 👑
- A leaf, choice, or anyxml node with a
mandatorystatement with the valuetrue. - A list or leaf-list node with a
min-elementsstatement with a value greater than zero. - A container node without a
presencestatement, which has at least one mandatory node as a child.
4. YANG Overview ✨
4.1. Functional Overview ✨
okay so YANG is the language you use to model data for the NETCONF protocol 🎀 A YANG module defines a hierarchy of data that NETCONF operations can use, covering configuration, state data, Remote Procedure Calls (RPCs), and notifications. Basically it gives you a complete description of all the data sent between a NETCONF client and server. iconic 💅
YANG models its data as a tree, where every node has a name and either a value or a set of child nodes. It also gives you clear, concise descriptions of each node and how the nodes interact with each other 🌸
YANG organizes data models into modules and submodules 📦 A module can import data from other external modules and include data from submodules. The hierarchy can also be augmented, so one module can add data nodes to a hierarchy defined in a different module. And that augmentation can be conditional, meaning the new nodes only show up when certain conditions are met. the way that's so clever 🧚
YANG models can describe constraints on the data, limiting whether a node appears or what value it has, based on the presence or value of other nodes in the hierarchy. The client or the server can enforce these constraints, and valid content MUST abide by them. no cap, that's the rule 🔥
YANG comes with a set of built-in types, plus a type mechanism for defining your own. Derived types can narrow down the valid values of their base type using range or pattern restrictions, and clients or servers can enforce those. They can also spell out usage conventions, like a string-based type that holds a host name 🍓
YANG lets you define reusable groupings of nodes ✨ When you instantiate a grouping, you can refine or augment its nodes to fit your exact needs. Derived types and groupings can be defined in one module or submodule and then used right there or in any other module or submodule that imports or includes it. so reusable, I'm obsessed 😭
YANG's data hierarchy includes lists, where each entry is identified by keys that tell it apart from the others. Lists can be sorted by the user or sorted automatically by the system. For user-sorted lists, there are operations for changing the order of the entries 🦋
YANG modules can be translated into an equivalent XML syntax called YANG Independent Notation (YIN) (Section 11). That means apps using XML parsers and Extensible Stylesheet Language Transformations (XSLT) scripts can work with the models 🫶 The conversion from YANG to YIN is lossless, so you can round-trip YIN back into YANG 🌈
YANG finds a balance between high-level data modeling and low-level bits-on-the-wire encoding 💜 If you read a YANG module, you get the big-picture view of the data model, and you also understand how that data gets encoded in NETCONF operations. main character energy 👑
YANG is extensible too 🎀 Standards bodies, vendors, and individuals can all define extension statements. The statement syntax lets those extensions sit alongside standard YANG statements naturally, and extensions still stand out enough that the reader will notice them 👀
YANG also knows when to stop 🙈 It doesn't try to solve every possible problem. It keeps its problem space limited so it can express NETCONF data models, not arbitrary XML documents or arbitrary data models. The data models YANG describes are built to be easy for NETCONF operations to act on 💖
As far as possible, YANG stays compatible with the Simple Network Management Protocol's (SNMP's) SMIv2 (Structure of Management Information version 2 [RFC2578], [RFC2579]). SMIv2-based MIB modules can be automatically translated into YANG modules for read-only access. YANG isn't concerned with translating things back from YANG to SMIv2, though 🦋
Like NETCONF, YANG is built to integrate smoothly with the device's native management infrastructure ✨ That lets implementations use their existing access control mechanisms to protect or expose parts of the data model 🫶
4.2. Language Overview
this section introduces some key YANG constructs that'll make the later language details way easier to follow 🧚 It builds up step by step, since YANG concepts and statements are all interrelated. The detailed description of YANG statements and syntax starts in Section 7 📚
4.2.1. Modules and Submodules
A module has three kinds of statements: module-header statements, revision statements, and definition statements 💅 The module-header statements describe the module and tell you about the module itself. The revision statements tell you the module's history. The definition statements make up the body of the module, where the data model actually gets defined.
A NETCONF server can implement a bunch of modules, which gives you multiple views of the same data, or multiple views of separate sections of the device's data 🌸 Or the server can implement just one module that defines all the available data. Your call, bestie.
A module can be split into submodules, depending on what the module owner needs 🎀 From the outside, though, it still looks like one single module, no matter how many submodules it has or how big they are.
The "include" statement lets a module or submodule reference material in submodules, and the "import" statement lets you reference material defined in other modules 🔥
4.2.2. Data Modeling Basics
YANG defines four types of nodes for data modeling 💖 In each of the subsections below, you'll see the YANG syntax next to the matching NETCONF XML representation. ✨
4.2.2.1. Leaf Nodes
A leaf node holds simple data, like an integer or a string 🍓 It has exactly one value of a particular type, and it has no child nodes.
YANG Example:
leaf host-name {
type string;
description "Hostname for this system";
}
NETCONF XML Example:
<host-name>my.example.com</host-name>
The "leaf" statement is covered in Section 7.6 🧚
4.2.2.2. Leaf-List Nodes
A leaf-list is a sequence of leaf nodes, with exactly one value of a particular type per leaf 🌈
YANG Example:
leaf-list domain-search {
type string;
description "List of domain names to search";
}
NETCONF XML Example:
<domain-search>high.example.com</domain-search>
<domain-search>low.example.com</domain-search>
<domain-search>everywhere.example.com</domain-search>
The "leaf-list" statement is covered in Section 7.7 💜
4.2.2.3. Container Nodes
A container node groups related nodes together in a subtree 📦 A container only has child nodes and no value. It can hold any number of child nodes of any type, including leafs, lists, containers, and leaf-lists 🫶
YANG Example:
container system {
container login {
leaf message {
type string;
description
"Message given at start of login session";
}
}
}
NETCONF XML Example:
<system>
<login>
<message>Good morning</message>
</login>
</system>
The "container" statement is covered in Section 7.5 🎀
4.2.2.4. List Nodes
A list defines a sequence of list entries 👑 Each entry works like a structure or a record instance, and it's uniquely identified by the values of its key leafs. A list can define multiple key leafs and can contain any number of child nodes of any type, like leafs, lists, containers, and so on 🔥
YANG Example:
list user {
key "name";
leaf name {
type string;
}
leaf full-name {
type string;
}
leaf class {
type string;
}
}
NETCONF XML Example:
<user>
<name>glocks</name>
<full-name>Goldie Locks</full-name>
<class>intruder</class>
</user>
<user>
<name>snowey</name>
<full-name>Snow White</full-name>
<class>free-loader</class>
</user>
<user>
<name>rzell</name>
<full-name>Rapun Zell</full-name>
<class>tower</class>
</user>
The "list" statement is covered in Section 7.8 🌸
4.2.2.5. Example Module
okay, here's how all those statements come together to define the module 🎀
// Contents of "acme-system.yang"
module acme-system {
namespace "http://acme.example.com/system";
prefix "acme";
organization "ACME Inc.";
contact "joe@acme.example.com";
description
"The module for entities implementing the ACME system.";
revision 2007-06-09 {
description "Initial revision.";
}
container system {
leaf host-name {
type string;
description "Hostname for this system";
}
leaf-list domain-search {
type string;
description "List of domain names to search";
}
container login {
leaf message {
type string;
description
"Message given at start of login session";
}
list user {
key "name";
leaf name {
type string;
}
leaf full-name {
type string;
}
leaf class {
type string;
}
}
}
}
}
4.2.3. State Data
YANG can model state data as well as configuration data, using the config statement 🦋 When a node is tagged with config false, its subhierarchy gets flagged as state data. That data gets reported through NETCONF's <get> operation, not <get-config>. The parent containers, lists, and key leafs get reported too, so the state data has its context 🧚
In this example, each interface has two leafs: a configured speed and an observed speed. The observed speed isn't configuration, so it can come back with NETCONF <get> operations but not with <get-config> operations 🌈 Since it isn't configuration data, it can't be manipulated using <edit-config> either 🙈
list interface {
key "name";
leaf name {
type string;
}
leaf speed {
type enumeration {
enum 10m;
enum 100m;
enum auto;
}
}
leaf observed-speed {
type uint32;
config false;
}
}
4.2.4. Built-In Types
YANG comes with a squad of built-in types that feel a lot like the ones in many programming languages, but it has its own twists because the management world has special needs 💅. Here's the full lineup from Section 9:
+---------------------+-------------------------------------+
| Name | Description |
+---------------------+-------------------------------------+
| binary | Any binary data |
| bits | A set of bits or flags |
| boolean | "true" or "false" |
| decimal64 | 64-bit signed decimal number |
| empty | A leaf that does not have any value |
| enumeration | Enumerated strings |
| identityref | A reference to an abstract identity |
| instance-identifier | References a data tree node |
| int8 | 8-bit signed integer |
| int16 | 16-bit signed integer |
| int32 | 32-bit signed integer |
| int64 | 64-bit signed integer |
| leafref | A reference to a leaf instance |
| string | Human-readable string |
| uint8 | 8-bit unsigned integer |
| uint16 | 16-bit unsigned integer |
| uint32 | 32-bit unsigned integer |
| uint64 | 64-bit unsigned integer |
| union | Choice of member types |
+---------------------+-------------------------------------+
The "type" statement is covered in Section 7.4 🎀
4.2.5. Derived Types (typedef)
YANG lets you make your own types from base types using the "typedef" statement 🧚 A base type can be a built-in type or a derived type too, so you can stack them into a whole family tree of types.
A derived type can be used as the argument for the "type" statement.
YANG Example:
typedef percent {
type uint8 {
range "0 .. 100";
}
description "Percentage";
}
leaf completed {
type percent;
}
NETCONF XML Example:
<completed>20</completed>
The "typedef" statement is covered in Section 7.3 💖
4.2.6. Reusable Node Groups (grouping)
Sets of nodes can be bundled up into reusable packs with the "grouping" statement ✨ A grouping defines a bunch of nodes, and you stamp them out wherever you want using the "uses" statement:
grouping target {
leaf address {
type inet:ip-address;
description "Target IP address";
}
leaf port {
type inet:port-number;
description "Target port number";
}
}
container peer {
container destination {
uses target;
}
}
NETCONF XML Example:
<peer>
<destination>
<address>192.0.2.1</address>
<port>830</port>
</destination>
</peer>
Here's the best part 🙈 You can refine the grouping as you use it, so certain statements get overridden for that specific spot. In this example, only the description gets a glow-up:
container connection {
container source {
uses target {
refine "address" {
description "Source IP address";
}
refine "port" {
description "Source port number";
}
}
}
container destination {
uses target {
refine "address" {
description "Destination IP address";
}
refine "port" {
description "Destination port number";
}
}
}
}
The "grouping" statement is covered in Section 7.11 🦋
4.2.7. Choices
YANG lets your data model keep incompatible nodes apart using the "choice" and "case" statements 🫶 The "choice" holds a set of "case" statements, and each case is a group of schema nodes that can't show up together. A case can hold several nodes, but each node can only live in one case under a given "choice".
When you create an element from one case, every element from all the other cases gets implicitly deleted 😭 The device takes care of enforcing this rule, so incompatible stuff never sneaks into the configuration.
The choice and case nodes only show up in the schema tree. They don't appear in the data tree or in NETCONF messages, because the extra layers are only needed for the conceptual schema 🌸
YANG Example:
container food {
choice snack {
case sports-arena {
leaf pretzel {
type empty;
}
leaf beer {
type empty;
}
}
case late-night {
leaf chocolate {
type enumeration {
enum dark;
enum milk;
enum first-available;
}
}
}
}
}
NETCONF XML Example:
<food>
<pretzel/>
<beer/>
</food>
The "choice" statement is covered in Section 7.9 🍓
4.2.8. Extending Data Models (augment)
YANG lets a module slide extra nodes into data models, whether that's the current module (and its submodules) or an external module 🌈 This is handy for vendors who want to add their own vendor-specific parameters to standard data models in a way that still plays nicely with everyone else.
The "augment" statement picks the spot in the data model hierarchy where the new nodes get inserted, and the "when" statement sets the conditions for when those new nodes are valid 👑
YANG Example:
augment /system/login/user {
when "class != 'wheel'";
leaf uid {
type uint16 {
range "1000 .. 30000";
}
}
}
This example defines a "uid" node that is only valid when the user's "class" is not "wheel" 🙈
If a module augments another module, the XML representation of the data picks up the prefix of the augmenting module. For example, if the augmentation above lived in a module with prefix "other", the XML would look like this:
NETCONF XML Example:
<user>
<name>alicew</name>
<full-name>Alice N. Wonderland</full-name>
<class>drop-out</class>
<other:uid>1024</other:uid>
</user>
The "augment" statement is covered in Section 7.15 ⭐
4.2.9. RPC Definitions
YANG lets you define NETCONF RPCs 💅 The operation names, input parameters, and output parameters are all modeled with YANG data definition statements.
YANG Example:
rpc activate-software-image {
input {
leaf image-name {
type string;
}
}
output {
leaf status {
type string;
}
}
}
NETCONF XML Example:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<activate-software-image xmlns="http://acme.example.com/system">
<image-name>acmefw-2.3</image-name>
</activate-software-image>
</rpc>
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<status xmlns="http://acme.example.com/system">
The image acmefw-2.3 is being installed.
</status>
</rpc-reply>
The "rpc" statement is covered in Section 7.13 🦋
4.2.10. Notification Definitions
YANG lets you define notifications that work with NETCONF ✨ You use YANG data definition statements to model what's inside the notification.
YANG Example:
notification link-failure {
description "A link failure has been detected";
leaf if-name {
type leafref {
path "/interface/name";
}
}
leaf if-admin-status {
type admin-status;
}
leaf if-oper-status {
type oper-status;
}
}
NETCONF XML Example:
<notification
xmlns="urn:ietf:params:netconf:capability:notification:1.0">
<eventTime>2007-09-01T10:00:00Z</eventTime>
<link-failure xmlns="http://acme.example.com/system">
<if-name>so-1/2/3.0</if-name>
<if-admin-status>up</if-admin-status>
<if-oper-status>down</if-oper-status>
</link-failure>
</notification>
The "notification" statement is covered in Section 7.14 💜
5. Language Concepts 🎀
5.1. Modules and Submodules
Okay so, the module is the base unit of definition in YANG 🌸 A module defines a single data model. It can be a complete, cohesive model all on its own, or it can augment an existing data model with extra nodes.
Submodules are partial modules that chip in definitions to a module 🧚 A module can pull in any number of submodules, but each submodule can only belong to one module.
The names of all standard modules and submodules MUST be unique. Developers of enterprise modules are RECOMMENDED to pick names that are unlikely to collide with standard or other enterprise modules. For example, they can use the enterprise or organization name as a prefix for the module name 💖
A module uses the "include" statement to bring in its submodules, and the "import" statement to reference external modules. A submodule uses "import" to reference other modules, and "include" to reference other submodules within its own module. A module or submodule MUST NOT include submodules from other modules, and a submodule MUST NOT import its own module 🙈
The import and include statements are how definitions become available to other modules and submodules:
- For a module or submodule to reference definitions in an external module, the external module MUST be imported.
- For a module to reference definitions in one of its submodules, the module MUST include the submodule.
- For a submodule to reference definitions in a second submodule of the same module, the first submodule MUST include the second submodule.
There MUST NOT be any circular chains of imports or includes. For example, if submodule "a" includes submodule "b", then "b" cannot include "a" 😭
When a definition from an external module gets referenced, a locally defined prefix MUST be used, followed by ":", and then the external identifier. References to definitions in the local module MAY use the prefix notation. Since built-in data types don't belong to any module and have no prefix, references to built-in data types (e.g., int32) cannot use the prefix notation 💅
5.1.1. Import and Include by Revision ✨
okay so published modules are gonna keep evolving on their own timeline 🦋 which is totally fine, but it means you need to pin the exact revision you're importing. when a module gets written, it grabs whatever revisions of its imports exist at that moment. then when newer revisions drop, the importing module doesn't get affected at all and its contents stay exactly the same 🔒 the author only moves to the latest revision once they're actually ready, and that means republishing the module with an updated "import" statement. that republish is basically the author saying "yep, I accept whatever changed in there" 💅
submodules are the same vibe but a little simpler 🌸 a module or submodule that includes submodules has to name the revision of each included submodule. if a submodule changes, anything that includes it needs an update too.
here's an example, where module "b" imports module "a":
module a {
revision 2008-01-01 { ... }
grouping a {
leaf eh { .... }
}
}
module b {
import a {
prefix p;
revision-date 2008-01-01;
}
container bee {
uses p:a;
}
}
so if the author of "a" publishes a new revision, "b"'s author might not be into those changes 🙈 totally valid. if the new revision is acceptable, "b"'s author can republish with an updated revision in the "import" statement and we're all good 🎀
5.1.2. Module Hierarchies ✨
YANG lets you model data in multiple hierarchies, so your data can have more than one top-level node 🌈 models with several top-level nodes are sometimes really convenient, and YANG totally supports them ⭐
NETCONF can carry any XML content as the payload inside the <config> and <data> elements 🧚 the top-level nodes of YANG modules get encoded as child elements in those spots, in any order. the wrapping means the NETCONF messages are always well-formed XML documents, which is honestly iconic of it 💖
for example, this module:
module my-config {
namespace "http://example.com/schema/config";
prefix "co";
container system { ... }
container routing { ... }
}
could be encoded in NETCONF like this:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<!-- system data here -->
</system>
<routing xmlns="http://example.com/schema/config">
<!-- routing data here -->
</routing>
</config>
</edit-config>
</rpc>
5.2. File Layout 📁
YANG modules and submodules usually live in files, with one module or submodule per file 🍓 the file name SHOULD follow this form:
module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' )
this naming convention is how YANG compilers can find imported modules and included submodules 🔍 the language defines modules, but tools may compile submodules on their own for performance and manageability reasons. errors and warnings that can't be spotted while compiling a submodule might get delayed until the submodules are linked into one cohesive module 🫶
5.3. XML Namespaces 🎀
every YANG definition lives inside a module that's bound to a particular XML namespace [XML-NAMES], and that namespace is a globally unique URI [RFC3986] 🌍 a NETCONF client or server uses that namespace when it XML-encodes data, so it really matters 👑
namespaces for modules published in RFC streams [RFC4844] MUST be assigned by IANA, see Section 14 🦋
namespaces for private modules get assigned by whatever organization owns the module, and there's no central registry for those 🙌 the namespace URIs MUST be picked so they can't collide with standard or other enterprise namespaces. a super easy way to do that is to put the enterprise or organization name right in the namespace 💜
the "namespace" statement is covered in Section 7.1.3. go check it out, bestie 👀
5.3.1. YANG XML Namespace 💅
YANG defines its own XML namespace for NETCONF <edit-config> operations and <error-info> content 🧁 the name of that namespace is "urn:ietf:params:xml:ns:yang:1" ✨
5.4. Resolving Grouping, Type, and Identity Names 🔎
grouping, type, and identity names get resolved in the context where they're defined, not where they're used 🧚 people who use groupings, typedefs, and identities don't have to import modules or include submodules just to satisfy every reference the original definition makes. it works like static scoping in a regular programming language, and it's giving clean ✨
for example, say a module defines a grouping that references a type. when a second module uses that grouping, the type gets resolved in the context of the original module, not the second one 🌸 and there's no drama if both modules define the type too, since there's no ambiguity 💖
5.5. Nested Typedefs and Groupings 🌷
typedefs and groupings can show up nested under lots of YANG statements, so they're lexically scoped by the hierarchy they live in 🌳 that means you can define types and groupings right near where you use them instead of dumping everything at the top level. the closeness makes it way easier to read 📖
scoping also means types can be defined without worrying about name clashes with types in other submodules 🙌 you don't have to tack on weird prefixes just to avoid collisions in big modules. no cap, that's so freeing 💅
and finally, scoping lets the module author keep types and groupings private to their module or submodule so nobody else can reuse them 🔒 only top-level types and groupings (the ones that appear as substatements of a module or submodule statement) can be used outside that module or submodule. so the developer gets way more control over what the outside world sees and can hide the internal stuff 🫶
scoped definitions MUST NOT shadow definitions at a higher scope. a type or grouping can't be defined if a higher level in the schema hierarchy already has a definition with a matching identifier 🚫
a reference to an unprefixed type or grouping, or one that uses the prefix of the current module, gets resolved by finding the closest matching "typedef" or "grouping" statement among the immediate substatements of each ancestor statement 🔍
5.6. Conformance 💜
conformance is basically how accurately a device follows the model 🎯 generally speaking, devices are on the hook for implementing the model faithfully, so applications can treat devices that implement the model the same way. deviations from the model can make the model less useful and make applications that rely on it more fragile 💀
YANG modelers have three mechanisms for conformance:
- the basic behavior of the model
- optional features that are part of the model
- deviations from the model
we'll go through each one in order, bestie 🧚
5.6.1. Basic Behavior 🌈
the model is a contract between the NETCONF client and server 🤝 it lets both parties trust that the other one knows the syntax and semantics behind the modeled data. the strength of YANG comes down to the strength of that contract 💪
5.6.2. Optional Features 🦋
in lots of models, the modeler lets some sections be conditional ✨ the device decides whether those conditional parts are supported or valid for that particular device 🔧
like, a syslog data model might include the ability to save logs locally, but the modeler knows that only works if the device has local storage 📦 if there's no local storage, an application should not tell the device to save logs. yikes 🙈
YANG supports this conditional stuff with a construct called "feature" 💖 features let the modeler make parts of the module conditional in a way the device controls. the model can express things that aren't universally present across every device, and since those features are part of the model definition, everyone gets a consistent view 🌸 applications can learn which features a device supports and tailor their behavior to it. main character energy 👑
a module can declare any number of features, each identified by a simple string, and can make parts of itself optional based on those features 🎀 if the device supports a feature, the matching parts of the module are valid for that device. if the device doesn't support it, those parts aren't valid, and applications should behave accordingly 🫶
features are defined with the "feature" statement. definitions in the module that depend on a feature get marked with the "if-feature" statement, using the feature's name as its argument 🧁
further details are in Section 7.18.1 ✨
5.6.3. Deviations 🌷
in an ideal world, every device would have to implement the model exactly as defined, and deviations wouldn't be allowed at all 🥺 but in the real world, devices often can't or weren't designed to implement the model as written. so for YANG-based automation to handle those device deviations, there has to be a way for devices to tell applications the specifics of their deviations 🔍
for example, a BGP module might allow any number of BGP peers, but a particular device may only support 16 BGP peers 🌐 any application that tries to configure the 17th peer will get an error. an error can tell the app it can't add another peer, sure, but it's way better if the app already knows about the limit ahead of time, so it can stop the user before they start down a path that can't succeed 🙌
device deviations are declared with the "deviation" statement, which takes a string as its argument that identifies a node in the schema tree 🌳 the contents of the statement spell out how the device implementation deviates from the contract defined in the module 💅
further details are in Section 7.18.3 💖
5.6.4. Announcing Conformance Information in the <hello> Message
okay so the namespace URI MUST be advertised as a capability in the NETCONF <hello> message 💖 that's literally how a NETCONF server says "hey, I support this YANG module" 🦋 The capability URI advertised MUST be of the form:
capability-string = namespace-uri [ parameter-list ]
parameter-list = "?" parameter *( "&" parameter )
parameter = revision-parameter /
module-parameter /
feature-parameter /
deviation-parameter
revision-parameter = "revision=" revision-date
module-parameter = "module=" module-name
feature-parameter = "features=" feature *( "," feature )
deviation-parameter = "deviations=" deviation *( "," deviation )
so here's the breakdown 🎀 "revision-date" is the revision of the module (see Section 7.1.9) that the NETCONF server implements, "module-name" is the module's name as it shows up in the "module" statement (see Section 7.1), "namespace-uri" is the module's namespace URI as it shows up in the "namespace" statement (see Section 7.1.3), "feature" is the name of an optional feature the device implements (see Section 7.18.1), and "deviation" is the name of a module that defines device deviations (see Section 7.18.3).
in the parameter list, each named parameter MUST occur at most once 🙅♀️ no duplicate params, bestie.
5.6.4.1. Modules
servers tell you which modules they support through the <hello> message 🍓 the module namespace gets encoded as the base URI in the capability string, and the module name goes in the "module" parameter tacked onto that base URI.
a server MUST advertise all revisions of all modules it implements 💅 no skipping revisions, it's giving completeness.
for example, this <hello> message advertises one module "syslog" ✨
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capability>
http://example.com/syslog?module=syslog&revision=2008-04-01
</capability>
</hello>
5.6.4.2. Features
servers also list their supported features in the <hello> message 🧚 in <hello> messages, the features get encoded in the "features" parameter inside the URI. its value is a comma-separated list of feature names that the device supports for that specific module.
for example, this <hello> message advertises one module, telling the client that it supports the "local-storage" feature of module "syslog" 🌈
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capability>
http://example.com/syslog?module=syslog&features=local-storage
</capability>
</hello>
5.6.4.3. Deviations
device deviations get announced via the "deviations" parameter 👑 its value is a comma-separated list of modules containing deviations from the capability's module.
for example, this <hello> message advertises two modules, telling the client that it deviates from module "syslog" according to the deviations listed in the module "my-devs" 🫶
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capability>
http://example.com/syslog?module=syslog&deviations=my-devs
</capability>
<capability>
http://example.com/my-deviations?module=my-devs
</capability>
</hello>
5.7. Data Store Modification
ok, this one is a little chaotic 😭 data models may allow the server to alter the configuration data store in ways that aren't explicitly directed via NETCONF protocol messages. for example, a data model may define leafs that get system-generated values when the client doesn't provide one. a formal mechanism for specifying when these changes are allowed is out of scope for this specification 🙈
6. YANG Syntax ✨
the YANG syntax is similar to SMIng [RFC3780] and to programming languages like C and C++ 🎀 this C-like syntax was chosen specifically for its readability, since YANG values the time and effort of the readers of models above those of module writers and YANG tool-chain developers. this section introduces the YANG syntax.
YANG modules use the UTF-8 [RFC3629] character encoding 🌸
6.1. Lexical Tokenization
YANG modules are parsed as a series of tokens 🦋 this section details the rules for recognizing tokens from an input stream. YANG tokenization rules are both simple and powerful. the simplicity is driven by the need to keep parsers easy to implement, while the power comes from the fact that modelers need to express their models in readable formats 💜
6.1.1. Comments
comments are C++ style 💅 a single line comment starts with "//" and ends at the end of the line. a block comment is enclosed within "/" and "/".
6.1.2. Tokens
a token in YANG is either a keyword, a string, a semicolon (";"), or braces ("{" or "}") ✨ a string can be quoted or unquoted. a keyword is either one of the YANG keywords defined in this document, or a prefix identifier, followed by ":", followed by a language extension keyword. keywords are case sensitive 🙌 see Section 6.2 for a formal definition of identifiers.
6.1.3. Quoting
if a string contains any space or tab characters, a semicolon (";"), braces ("{" or "}"), or comment sequences ("//", "/", or "/"), then it MUST be enclosed within double or single quotes 🫶 no exceptions, no cap.
if the double-quoted string contains a line break followed by space or tab characters that are used to indent the text according to the layout in the YANG file, that leading whitespace gets stripped from the string, up to and including the column of the double quote character, or to the first non-whitespace character, whichever occurs first. in this process, a tab character is treated as 8 space characters 📏
if the double-quoted string contains space or tab characters before a line break, that trailing whitespace gets stripped from the string 🧹
a single-quoted string (enclosed within ' ') preserves each character within the quotes. a single quote character cannot occur in a single-quoted string, even when preceded by a backslash 🙅♀️
within a double-quoted string (enclosed within " "), a backslash character introduces a special character, which depends on the character that immediately follows the backslash 🌈
\n new line
\t a tab character
\" a double quote
\\ a single backslash
if a quoted string is followed by a plus character ("+"), followed by another quoted string, the two strings get concatenated into one string, so you can chain multiple concatenations to build one string 🧩 whitespace trimming and substitution of backslash-escaped characters in double-quoted strings is done before concatenation.
6.1.3.1. Quoting Examples
the following strings are equivalent 👯♀️
hello
"hello"
'hello'
"hel" + "lo"
'hel' + "lo"
the following examples show some special strings 🍓
"\"" - string containing a double quote
'"' - string containing a double quote
"\n" - string containing a new line character
'\n' - string containing a backslash followed
by the character n
the following examples show some illegal strings 💀
'''' - a single-quoted string cannot contain single quotes
""" - a double quote must be escaped in a double-quoted string
the following strings are equivalent too 🦋
"first line
second line"
"first line\n" + " second line"
6.2. Identifiers
identifiers are used to identify different kinds of YANG items by name 🏷️ each identifier starts with an uppercase or lowercase ASCII letter or an underscore character, followed by zero or more ASCII letters, digits, underscore characters, hyphens, and dots. implementations MUST support identifiers up to 64 characters in length. identifiers are case sensitive. the identifier syntax is formally defined by the rule "identifier" in Section 12. identifiers can be specified as quoted or unquoted strings 🫧
6.2.1. Identifiers and Their Namespaces
each identifier is valid in a namespace that depends on the type of the YANG item being defined. all identifiers defined in a namespace MUST be unique 🔑
- all module and submodule names share the same global module identifier namespace.
- all extension names defined in a module and its submodules share the same extension identifier namespace.
- all feature names defined in a module and its submodules share the same feature identifier namespace.
- all identity names defined in a module and its submodules share the same identity identifier namespace.
- all derived type names defined within a parent node or at the top level of the module or its submodules share the same type identifier namespace. this namespace is scoped to all descendant nodes of the parent node or module. this means that any descendant node may use that typedef, and it MUST NOT define a typedef with the same name 🚫
- all grouping names defined within a parent node or at the top level of the module or its submodules share the same grouping identifier namespace. this namespace is scoped to all descendant nodes of the parent node or module. this means that any descendant node may use that grouping, and it MUST NOT define a grouping with the same name 🚫
- all leafs, leaf-lists, lists, containers, choices, rpcs, notifications, and anyxmls defined (directly or through a uses statement) within a parent node or at the top level of the module or its submodules share the same identifier namespace. this namespace is scoped to the parent node or module, unless the parent node is a case node. in that case, the namespace is scoped to the closest ancestor node that is not a case or choice node 🌸
- all cases within a choice share the same case identifier namespace. this namespace is scoped to the parent choice node.
forward references are allowed in YANG ⏩
6.3. Statements
a YANG module contains a sequence of statements 📜 each statement starts with a keyword, followed by zero or one argument, followed either by a semicolon (";") or a block of substatements enclosed within braces ("{ }"):
statement = keyword [argument] (";" / "{" *statement "}")
the argument is a string, as defined in Section 6.1.2.
6.3.1. Language Extensions
a module can introduce YANG extensions by using the "extension" keyword (see Section 7.17) 🧚 the extensions can be imported by other modules with the "import" statement (see Section 7.1.5). when an imported extension is used, the extension's keyword MUST be qualified using the prefix with which the extension's module was imported. if an extension is used in the module where it is defined, the extension's keyword MUST be qualified with the module's prefix 🏷️
since submodules cannot include the parent module, any extensions in the module that need to be exposed to submodules MUST be defined in a submodule. submodules can then include this submodule to find the definition of the extension 💖
if a YANG compiler does not support a particular extension, which appears in a YANG module as an unknown-statement (see Section 12), the entire unknown-statement MAY be ignored by the compiler 🤷♀️
6.4. XPath Evaluations 🧚
okay bestie, so YANG leans on XML Path Language (XPath) 1.0 [XPATH] as its notation for describing a lot of the inter-node references and dependencies 💅. NETCONF clients and servers don't have to implement an XPath interpreter, but they MUST ensure that the requirements encoded in the data model are enforced. How they do that is an implementation decision 🫶. The XPath expressions MUST be syntactically correct, and all prefixes used MUST be present in the XPath context (see Section 6.4.1). An implementation may choose to implement them by hand, rather than using the XPath expression directly 🌸.
The data model used in the XPath expressions is the same one XPath 1.0 uses [XPATH], with the same extension for root node children that XSLT 1.0 uses [XSLT] (Section 3.1). So basically, the root node can have any number of element nodes as its children 🌈.
6.4.1. XPath Context 🎀
All YANG XPath expressions share this XPath context definition:
- The set of namespace declarations is the set of all "import" statements' prefix and namespace pairs in the module where the XPath expression is specified, and the "prefix" statement's prefix for the "namespace" statement's URI.
- Names without a namespace prefix belong to the same namespace as the identifier of the current node. Inside a grouping, that namespace is affected by where the grouping is used (see Section 7.12).
- The function library is the core function library defined in [XPATH], and a function "current()" that returns a node set with the initial context node.
- The set of variable bindings is empty.
The mechanism for handling unprefixed names is adopted from XPath 2.0 [XPATH2.0], and it helps simplify XPath expressions in YANG ✨. No ambiguity may ever arise because YANG node identifiers are always qualified names with a non-null namespace URI 🙈.
The context node varies with the YANG XPath expression, and is specified where the YANG statement with the XPath expression is defined.
6.5. Schema Node Identifier 🗺️
A schema node identifier is a string that identifies a node in the schema tree. It comes in two forms, "absolute" and "descendant", defined by the rules "absolute-schema-nodeid" and "descendant-schema-nodeid" in Section 12, respectively. It's a path of identifiers separated by slashes ("/"). In an absolute schema node identifier, the first identifier after the leading slash is any top-level schema node in the local module or in all imported modules.
References to identifiers defined in external modules MUST be qualified with appropriate prefixes, and references to identifiers defined in the current module and its submodules MAY use a prefix 🍓.
For example, to identify the child node "b" of top-level node "a", the string "/a/b" can be used.
7. YANG Statements 📜
The following sections describe all of the YANG statements.
Note that even a statement that doesn't have any substatements defined in YANG can have vendor-specific extensions as substatements 👑. For example, the "description" statement doesn't have any substatements defined in YANG, but this is legal:
description "some text" {
acme:documentation-flag 5;
}
7.1. The module Statement 📦
The "module" statement defines the module's name, and it groups all statements that belong to the module together 🫶. Its argument is the name of the module, followed by a block of substatements that hold detailed module information. The module name follows the rules for identifiers in Section 6.2.
Names of modules published in RFC streams [RFC4844] MUST be assigned by IANA, see Section 14 ✨.
Private module names are assigned by the organization that owns the module, without a central registry. It is RECOMMENDED to choose module names that have a low probability of colliding with standard or other enterprise modules and submodules, e.g., by using the enterprise or organization name as a prefix for the module name 🌸.
A module typically has the following layout:
module <module-name> {
// header information
<yang-version statement>
<namespace statement>
<prefix statement>
// linkage statements
<import statements>
<include statements>
// meta information
<organization statement>
<contact statement>
<description statement>
<reference statement>
// revision history
<revision statements>
// module definitions
<other statements>
}
7.1.1. The module's Substatements 🧾
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| augment | 7.15 | 0..n |
| choice | 7.9 | 0..n |
| contact | 7.1.8 | 0..1 |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| deviation | 7.18.3 | 0..n |
| extension | 7.17 | 0..n |
| feature | 7.18.1 | 0..n |
| grouping | 7.11 | 0..n |
| identity | 7.16 | 0..n |
| import | 7.1.5 | 0..n |
| include | 7.1.6 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| namespace | 7.1.3 | 1 |
| notification | 7.14 | 0..n |
| organization | 7.1.7 | 0..1 |
| prefix | 7.1.4 | 1 |
| reference | 7.19.4 | 0..1 |
| revision | 7.1.9 | 0..n |
| rpc | 7.13 | 0..n |
| typedef | 7.3 | 0..n |
| uses | 7.12 | 0..n |
| yang-version | 7.1.2 | 0..1 |
+--------------+---------+-------------+
7.1.2. The yang-version Statement 🦋
The optional "yang-version" statement specifies which version of the YANG language was used when developing the module. The statement's argument is a string. If present, it MUST contain the value "1", which is the current YANG version and the default value 💖.
Handling of the "yang-version" statement for versions other than "1" (the version defined here) is out of scope for this specification. Any document that defines a higher version will need to define the backward compatibility of that higher version.
7.1.3. The namespace Statement 🌈
The "namespace" statement defines the XML namespace that all identifiers defined by the module are qualified by, with the exception of data node identifiers defined inside a grouping (see Section 7.12 for details). The argument to the "namespace" statement is the URI of the namespace.
See also Section 5.3.
7.1.4. The prefix Statement 🎀
The "prefix" statement is used to define the prefix associated with the module and its namespace. Its argument is the prefix string that is used as a prefix to access a module. The prefix string MAY be used to refer to definitions contained in the module, e.g., "if:ifName". A prefix follows the same rules as an identifier (see Section 6.2).
When used inside the "module" statement, the "prefix" statement defines the prefix to be used when this module is imported. To improve readability of the NETCONF XML, a NETCONF client or server that generates XML or XPath that uses prefixes SHOULD use the prefix defined by the module, unless there is a conflict 🧚.
When used inside the "import" statement, the "prefix" statement defines the prefix to be used when accessing definitions inside the imported module. When a reference to an identifier from the imported module is used, the prefix string for the imported module is used in combination with a colon (":") and the identifier, e.g., "if: ifIndex". To improve readability of YANG modules, the prefix defined by a module SHOULD be used when the module is imported, unless there is a conflict. If there is a conflict, i.e., two different modules that both have defined the same prefix are imported, at least one of them MUST be imported with a different prefix 😭.
All prefixes, including the prefix for the module itself, MUST be unique within the module or submodule ⚠️
7.1.5. The import Statement 🔗
The "import" statement makes definitions from one module available inside another module or submodule. The argument is the name of the module to import, and the statement is followed by a block of substatements that hold detailed import information. When a module is imported, the importing module may:
- use any grouping and typedef defined at the top level in the imported module or its submodules.
- use any extension, feature, and identity defined in the imported module or its submodules.
- use any node in the imported module's schema tree in "must", "path", and "when" statements, or as the target node in "augment" and "deviation" statements.
The mandatory "prefix" substatement assigns a prefix for the imported module that is scoped to the importing module or submodule. Multiple "import" statements may be specified to import from different modules 💜.
When the optional "revision-date" substatement is present, any typedef, grouping, extension, feature, and identity referenced by definitions in the local module are taken from the specified revision of the imported module. It is an error if the specified revision of the imported module does not exist. If no "revision-date" substatement is present, it is undefined which revision of the module they are taken from 🙈.
Multiple revisions of the same module MUST NOT be imported. 💀
The import's Substatements
+---------------+---------+-------------+
| substatement | section | cardinality |
+---------------+---------+-------------+
| prefix | 7.1.4 | 1 |
| revision-date | 7.1.5.1 | 0..1 |
+---------------+---------+-------------+
7.1.5.1. The import's revision-date Statement 🍓
The import's "revision-date" statement is used to specify the exact version of the module to import. The "revision-date" statement MUST match the most recent "revision" statement in the imported module 🫶.
7.1.6. The include Statement 🧩
okay so include is how you pull a submodule's stuff into its parent module, or into another submodule of that same parent. 🦋 The argument is an identifier, which is just the name of the submodule you're including. You can only include submodules that actually belong to your module (the ones that say so with belongs-to, see Section 7.2.2). And submodules can only include other submodules from the same module. no cap, the family tree has to make sense 💅
When a module includes a submodule, the submodule's contents get folded into the module's node hierarchy. 🌸 When a submodule includes another submodule, the target's definitions just become available to the submodule doing the including. ✨
The optional revision-date substatement lets you say exactly which revision of the submodule you want. If that revision doesn't exist, that's an error 💀. If you leave it out, which revision gets included is undefined, so be specific if you care 🙈
Including multiple revisions of the same submodule is a MUST NOT 🚫. Literally don't do it.
The includes's Substatements
+---------------+---------+-------------+
| substatement | section | cardinality |
+---------------+---------+-------------+
| revision-date | 7.1.5.1 | 0..1 |
+---------------+---------+-------------+
7.1.7. The organization Statement 🏢
The organization statement names the party responsible for the module. 💖 Its argument is a string describing the organization or organizations whose auspices the module was developed under.
7.1.8. The contact Statement 📬
The contact statement is where you leave contact info for the module ✉️ Its argument is a string with the details of the person or people technical questions should go to, like their name, postal address, phone number, and email address.
7.1.9. The revision Statement 📅
The revision statement keeps the editorial history of the module, starting with the initial revision. 🕰️ A string of revision statements tells the story of how the module's definition has changed over time. The argument is a date string in the format "YYYY-MM-DD", followed by a block of substatements with the detailed revision info. A module SHOULD have at least one initial revision statement. And for every published editorial change, a new one SHOULD be added at the front of the revision sequence, so everything stays in reverse chronological order. main character timeline energy 🎀
7.1.9.1. The revision's Substatement
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| reference | 7.19.4 | 0..1 |
+--------------+---------+-------------+
7.1.10. Usage Example 🌈
module acme-system {
namespace "http://acme.example.com/system";
prefix "acme";
import ietf-yang-types {
prefix "yang";
}
include acme-types;
organization "ACME Inc.";
contact
"Joe L. User
ACME, Inc.
42 Anywhere Drive
Nowhere, CA 95134
USA
Phone: +1 800 555 0100
EMail: joe@acme.example.com";
description
"The module for entities implementing the ACME protocol.";
revision "2007-06-09" {
description "Initial revision.";
}
// definitions follow...
}
7.2. The submodule Statement 🧁
YANG's main unit is the module, but a module can be built out of several submodules too. 🍓 That lets a designer split a big, messy model into pieces, while every submodule still feeds into one single namespace, the one defined by the module that includes them. iconic teamwork 🫶
The submodule statement names the submodule and bundles all the statements that belong to it. Its argument is the submodule's name, followed by a block of substatements with the detailed info. The submodule name follows the identifier rules in Section 6.2.
Names of submodules published in RFC streams [RFC4844] MUST be assigned by IANA, see Section 14. 👑
Private submodule names are handled by whichever organization owns the submodule, with no central registry 🔐. It is RECOMMENDED to pick names that are unlikely to collide with standard or other enterprise modules and submodules. One way to do that is to start the name with the enterprise or organization name. 🦄
A submodule usually looks like this:
submodule <module-name> {
<yang-version statement>
// module identification
<belongs-to statement>
// linkage statements
<import statements>
<include statements>
// meta information
<organization statement>
<contact statement>
<description statement>
<reference statement>
// revision history
<revision statements>
// module definitions
<other statements>
}
7.2.1. The submodule's Substatements 📋
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| augment | 7.15 | 0..n |
| belongs-to | 7.2.2 | 1 |
| choice | 7.9 | 0..n |
| contact | 7.1.8 | 0..1 |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| deviation | 7.18.3 | 0..n |
| extension | 7.17 | 0..n |
| feature | 7.18.1 | 0..n |
| grouping | 7.11 | 0..n |
| identity | 7.16 | 0..n |
| import | 7.1.5 | 0..n |
| include | 7.1.6 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| notification | 7.14 | 0..n |
| organization | 7.1.7 | 0..1 |
| reference | 7.19.4 | 0..1 |
| revision | 7.1.9 | 0..n |
| rpc | 7.13 | 0..n |
| typedef | 7.3 | 0..n |
| uses | 7.12 | 0..n |
| yang-version | 7.1.2 | 0..1 |
+--------------+---------+-------------+
7.2.2. The belongs-to Statement 🔗
The belongs-to statement says which module the submodule belongs to. 💜 Its argument is an identifier, the name of that module.
A submodule MUST only be included by the module it belongs to, or by another submodule that belongs to that same module. 🚦
The mandatory prefix substatement gives the module a prefix. Once that's set, every definition in the local submodule and in any included submodules can be reached through that prefix 🧚
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| prefix | 7.1.4 | 1 |
+--------------+---------+-------------+
7.2.3. Usage Example 🎀
submodule acme-types {
belongs-to "acme-system" {
prefix "acme";
}
import ietf-yang-types {
prefix "yang";
}
organization "ACME Inc.";
contact
"Joe L. User
ACME, Inc.
42 Anywhere Drive
Nowhere, CA 95134
USA
Phone: +1 800 555 0100
EMail: joe@acme.example.com";
description
"This submodule defines common ACME types.";
revision "2007-06-09" {
description "Initial revision.";
}
// definitions follows...
}
7.3. The typedef Statement 🪄
The typedef statement defines a new type you can use locally in the module, in any modules or submodules that include it, and in other modules that import from it, following the rules in Section 5.5. ✨ The new type is called the "derived type", and the type it came from is the "base type". Every derived type can be traced all the way back to a YANG built-in type, which is very iconic of it 👑
The typedef statement's argument is an identifier, the name of the type being defined. It MUST be followed by a block of substatements with the detailed typedef info.
The name of the type MUST NOT be one of the YANG built-in types 🙅♀️. If the typedef sits at the top level of a YANG module or submodule, its name MUST be unique within that module.
7.3.1. The typedef's Substatements 📝
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| default | 7.3.4 | 0..1 |
| description | 7.19.3 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| type | 7.3.2 | 1 |
| units | 7.3.3 | 0..1 |
+--------------+---------+-------------+
7.3.2. The typedef's type Statement 🧱
The type statement MUST be present. It defines the base type this type is derived from. See Section 7.4 for the details.
7.3.3. The units Statement 📏
The units statement is optional. Its argument is a string holding a textual definition of the units associated with the type.
7.3.4. The typedef's default Statement 🌷
The default statement takes a string argument with a default value for the new type.
The value of the default statement MUST be valid according to the type given in the type statement. 💅
If the base type has a default value, and the new derived type doesn't specify its own default, then the base type's default value is also the default value of the new derived type. 🫶
If the type's default value isn't valid under the new restrictions in a derived type or leaf definition, then that derived type or leaf definition MUST specify a new default value that works with those restrictions. 🔥
7.3.5. Usage Example 🦋
typedef listen-ipv4-address {
type inet:ipv4-address;
default "0.0.0.0";
}
7.4. The type Statement ✨
okay so the type statement takes one argument: a string naming either a YANG built-in type (see Section 9) or a derived type (see Section 7.3) 🎀. It can also come with an optional block of substatements, which is where you add extra restrictions on the type 💅.
The restrictions you can apply depend on which type you're restricting 🌸. Every built-in type's restriction statements are explained in the subsections of Section 9 🦋.
7.4.1. The type's Substatements
+------------------+---------+-------------+
| substatement | section | cardinality |
+------------------+---------+-------------+
| bit | 9.7.4 | 0..n |
| enum | 9.6.4 | 0..n |
| length | 9.4.4 | 0..1 |
| path | 9.9.2 | 0..1 |
| pattern | 9.4.6 | 0..n |
| range | 9.2.4 | 0..1 |
| require-instance | 9.13.2 | 0..1 |
| type | 7.4 | 0..n |
+------------------+---------+-------------+
7.5. The container Statement 🧚
the container statement defines an interior data node in the schema tree 🌈. It takes one argument, an identifier, and then a block of substatements that holds all the detailed container info 💜.
a container node has no value of its own, but it does have a list of child nodes in the data tree 🍓. Those child nodes get defined inside the container's substatements ✨.
7.5.1. Containers with Presence
YANG has two styles of containers, and this is the part I'm obsessed with 😭. The first style exists purely to organize the hierarchy of data nodes. The second style is one where the container's presence in the configuration actually means something 🔥.
in the first style the container has no meaning of its own. it just exists to hold child nodes 💅. This is the default style 🎀.
for example, the scrambling options for Synchronous Optical Network (SONET) interfaces can go inside a "scrambling" container. That keeps the configuration hierarchy organized and keeps those nodes together 🫶. The "scrambling" node itself means nothing, so when it becomes empty, removing it takes a task off the user's plate 🍓.
in the second style, the container's presence is configuration data all by itself, basically a single bit of config 🦋. The container is both a config knob and a way to organize related config, so these containers get explicitly created and deleted 👑.
YANG calls this style a "presence container" 💖. You flag it with the "presence" statement, which takes a text string saying what the node's presence means 🌸.
for example, an "ssh" container could turn on the ability to log into the device with ssh 🔐. It can also hold any ssh-related knobs, like connection rates or retry limits ⭐.
The "presence" statement (see Section 7.5.5) is what gives meaning to the container existing in the data tree 🦋.
7.5.2. The container's Substatements
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| choice | 7.9 | 0..n |
| config | 7.19.1 | 0..1 |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| grouping | 7.11 | 0..n |
| if-feature | 7.18.2 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| must | 7.5.3 | 0..n |
| presence | 7.5.5 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| typedef | 7.3 | 0..n |
| uses | 7.12 | 0..n |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.5.3. The must Statement 🔥
the "must" statement is optional 🌷. It takes a string containing an XPath expression (see Section 6.4) and it's how you formally declare a constraint on valid data 💅. The constraint gets enforced according to the rules in Section 8 ✨.
when a datastore gets validated, all the "must" constraints are conceptually evaluated once for each data node in the data tree, and for all leafs with default values in use (see Section 7.6.1) 🧚. If a data node doesn't exist in the data tree and doesn't have a default value, its "must" statements don't get evaluated 🙈.
All such constraints MUST evaluate to true for the data to be valid 🚨. no exceptions, bestie 💀.
The XPath expression is conceptually evaluated in the following context, on top of the definition in Section 6.4.1 🌈:
- The context node is the node in the data tree for which the
"must"statement is defined. - The accessible tree is made up of all nodes in the data tree, and all leafs with default values in use (see Section 7.6.1).
the accessible tree depends on the context node 🦋:
- If the context node represents configuration, the tree is the data in the NETCONF datastore where the context node exists. The XPath root node has all top-level configuration data nodes in all modules as children.
- If the context node represents state data, the tree is all state data on the device, and the
<running/>datastore. The XPath root node has all top-level data nodes in all modules as children. - If the context node represents notification content, the tree is the notification XML instance document. The XPath root node has the element representing the notification being defined as the only child.
- If the context node represents RPC input parameters, the tree is the RPC XML instance document. The XPath root node has the element representing the RPC operation being defined as the only child.
- If the context node represents RPC output parameters, the tree is the RPC reply instance document. The XPath root node has the elements representing the RPC output parameters as children.
the result of the XPath expression gets turned into a boolean value using the standard XPath rules 💜.
Note that since all leaf values in the data tree are conceptually stored in their canonical form (see Sections 7.6 and 7.7), any XPath comparisons happen on the canonical value 🍓.
Also note that the XPath expression is only conceptually evaluated 🙈. That means an implementation doesn't have to run an XPath evaluator on the device. How the evaluation actually gets done is an implementation decision 🫶.
7.5.4. The must's Substatements
+---------------+---------+-------------+
| substatement | section | cardinality |
+---------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| error-app-tag | 7.5.4.2 | 0..1 |
| error-message | 7.5.4.1 | 0..1 |
| reference | 7.19.4 | 0..1 |
+---------------+---------+-------------+
7.5.4.1. The error-message Statement 💌
the "error-message" statement is optional and takes a string as its argument 🎀. If the constraint evaluates to false, that string gets passed back as <error-message> in the <rpc-error> 😭.
7.5.4.2. The error-app-tag Statement 🏷️
the "error-app-tag" statement is also optional and takes a string as its argument 🌸. If the constraint evaluates to false, that string gets passed back as <error-app-tag> in the <rpc-error> 🦋.
7.5.4.3. Usage Example of must and error-message 🧁
container interface {
leaf ifType {
type enumeration {
enum ethernet;
enum atm;
}
}
leaf ifMTU {
type uint32;
}
must "ifType != 'ethernet' or " +
"(ifType = 'ethernet' and ifMTU = 1500)" {
error-message "An ethernet MTU must be 1500";
}
must "ifType != 'atm' or " +
"(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
error-message "An atm MTU must be 64 .. 17966";
}
}
7.5.5. The presence Statement 🎀
the "presence" statement assigns a meaning to a container being present in the data tree 💖. It takes a string as its argument, and that string is a textual description of what the node's presence means 🌷.
If a container has the "presence" statement, the container's existence in the data tree carries some meaning ✨. If it doesn't, the container is just there to give the data some structure and carries no meaning by itself 🧚.
See Section 7.5.1 for more info 🫶.
7.5.6. The container's Child Node Statements
Inside a container, the "container", "leaf", "list", "leaf-list", "uses", "choice", and "anyxml" statements can all be used to define child nodes of the container 🌈.
7.5.7. XML Mapping Rules 🍓
a container node gets encoded as an XML element 💜. The element's local name is the container's identifier, and its namespace is the module's XML namespace (see Section 7.1.3) 🦋.
The container's child nodes get encoded as subelements of the container element. If the container defines RPC input or output parameters, those subelements are encoded in the same order they're defined in the "container" statement 🔥. Otherwise, the subelements can be encoded in any order 🙈.
A NETCONF server that replies to a <get> or <get-config> request MAY choose not to send a container element if the container node doesn't have the "presence" statement and no child nodes exist 🫶. So a client that receives an <rpc-reply> for a <get> or <get-config> request must be prepared to handle the case where a container node without a "presence" statement isn't in the XML at all 💅.
7.5.8. NETCONF <edit-config> Operations ✨
Containers can be created, deleted, replaced, and modified through <edit-config>, using the "operation" attribute (see [RFC4741], Section 7.2) on the container's XML element 🎀.
If a container doesn't have a "presence" statement and its last child node gets deleted, the NETCONF server MAY delete the container 🧹.
When a NETCONF server processes an <edit-config> request, the elements of procedure for the container node are 📋:
- If the operation is
"merge"or"replace", the node is created if it does not exist. - If the operation is
"create", the node is created if it does not exist. If the node already exists, a"data-exists"error is returned. - If the operation is
"delete", the node is deleted if it exists. If the node does not exist, a"data-missing"error is returned.
7.5.9. Usage Example 🎀
Okay, let's say we have this container definition 💅:
container system {
description "Contains various system parameters";
container services {
description "Configure externally available services";
container "ssh" {
presence "Enables SSH";
description "SSH service specific configuration";
// more leafs, containers and stuff here...
}
}
}
And here's a corresponding XML instance example 🧚:
<system>
<services>
<ssh/>
</services>
</system>
Since the <ssh> element is present, ssh is enabled ✨.
To delete a container with an <edit-config> 🗑️:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<services>
<ssh nc:operation="delete"/>
</services>
</system>
</config>
</edit-config>
</rpc>
7.6. The leaf Statement 💖
The "leaf" statement is how you define a leaf node in the schema tree 🌸. It takes one argument, which is an identifier, and then a block of substatements that holds all the detailed leaf info.
A leaf node has a value but no child nodes in the data tree. Conceptually, the value in the data tree is always in the canonical form (see Section 9.1) ✨.
A leaf node can exist in zero or one instances in the data tree 🦋.
The "leaf" statement is used to define a scalar variable of a particular built-in or derived type 💜.
7.6.1. The leaf's default value 🌈
The default value of a leaf is the value the server uses if the leaf doesn't exist in the data tree. How the default value gets used depends on the leaf's closest ancestor node in the schema tree that is not a non-presence container 👀:
- If no such ancestor exists in the schema tree, the default value MUST be used.
- Otherwise, if this ancestor is a case node, the default value MUST be used if any node from the case exists in the data tree, or if the case node is the choice's default case, and no nodes from any other case exist in the data tree.
- Otherwise, the default value MUST be used if the ancestor node exists in the data tree.
In these cases, the default value is said to be in use 🫶.
When the default value is in use, the server MUST operationally behave as if the leaf was present in the data tree with the default value as its value 🔥.
If a leaf has a "default" statement, the leaf's default value is the value of that "default" statement. Otherwise, if the leaf's type has a default value, and the leaf is not mandatory, then the leaf's default value is the type's default value. In all other cases, the leaf does not have a default value 💅.
7.6.2. The leaf's Substatements 📋
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| config | 7.19.1 | 0..1 |
| default | 7.6.4 | 0..1 |
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| mandatory | 7.6.5 | 0..1 |
| must | 7.5.3 | 0..n |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| type | 7.6.3 | 1 |
| units | 7.3.3 | 0..1 |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.6.3. The leaf's type Statement 🧩
The "type" statement MUST be present 💯. It takes as an argument the name of an existing built-in or derived type. The optional substatements specify restrictions on that type. See Section 7.4 for all the details 🍓.
7.6.4. The leaf's default Statement 🌷
The "default" statement is optional. It takes as an argument a string that contains a default value for the leaf.
The value of the "default" statement MUST be valid according to the type specified in the leaf's "type" statement 🙌.
The "default" statement MUST NOT be present on nodes where "mandatory" is true 🚫.
7.6.5. The leaf's mandatory Statement 🔒
The "mandatory" statement is optional. It takes as an argument the string "true" or "false", and it puts a constraint on valid data. If it's not specified, the default is "false" 🤍.
If "mandatory" is "true", the behavior of the constraint depends on the type of the leaf's closest ancestor node in the schema tree that is not a non-presence container (see Section 7.5.1) 🧐:
- If no such ancestor exists in the schema tree, the leaf MUST exist.
- Otherwise, if this ancestor is a case node, the leaf MUST exist if any node from the case exists in the data tree.
- Otherwise, the leaf MUST exist if the ancestor node exists in the data tree.
This constraint is enforced according to the rules in Section 8 ⭐.
7.6.6. XML Mapping Rules 🍰
A leaf node is encoded as an XML element. The element's local name is the leaf's identifier, and its namespace is the module's XML namespace (see Section 7.1.3) 🎀.
The value of the leaf node is encoded to XML according to the type, and it's sent as character data in the element 💌.
A NETCONF server that replies to a <get> or <get-config> request MAY choose not to send the leaf element if its value is the default value. So a client that receives an <rpc-reply> for a <get> or <get-config> request MUST be prepared to handle the case where a leaf node with a default value isn't present in the XML 🙈. In that case, the value the server is using is known to be the default value 💡.
See Section 7.6.8 for an example 🦋.
7.6.7. NETCONF <edit-config> Operations ⚙️
When a NETCONF server processes an <edit-config> request, these are the steps for a leaf node 📝:
- If the operation is "merge" or "replace", the node is created if it doesn't exist, and its value is set to the value found in the XML RPC data 🌱.
- If the operation is "create", the node is created if it doesn't exist. If the node already exists, a "data-exists" error is returned 😬.
- If the operation is "delete", the node is deleted if it exists. If the node doesn't exist, a "data-missing" error is returned 💀.
7.6.8. Usage Example 🌟
Given the following "leaf" statement, placed in the previously defined "ssh" container (see Section 7.5.9) 💜:
leaf port {
type inet:port-number;
default 22;
description "The port to which the SSH server listens"
}
And a corresponding XML instance example:
<port>2022</port>
To set the value of a leaf with an <edit-config> 🛠️:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<services>
<ssh>
<port>2022</port>
</ssh>
</services>
</system>
</config>
</edit-config>
</rpc>
7.7. The leaf-list Statement 🦄
Where the "leaf" statement defines a simple scalar variable of a particular type, the "leaf-list" statement defines an array of a particular type 🌈. The "leaf-list" statement takes one argument, which is an identifier, followed by a block of substatements that holds the detailed leaf-list info.
The values in a leaf-list MUST be unique 🔑.
Conceptually, the values in the data tree are always in the canonical form (see Section 9.1) ✨.
If the type referenced by the leaf-list has a default value, it has no effect in the leaf-list 🫧.
7.7.1. Ordering 🔢
YANG supports two styles for ordering the entries within lists and leaf-lists 👑. In lots of lists, the order of the entries doesn't affect how the list's configuration is implemented, so the device is free to sort the entries in any reasonable order. The list's "description" string may hint at an order to the device implementor 💭. YANG calls this style "system ordered," and these lists are indicated with the statement ordered-by system.
For example, a list of valid users would typically be sorted alphabetically, since the order the users appeared in the configuration wouldn't change how their accounts get created 🧑🤝🧑.
In the other style, the order of the entries matters for how the list's configuration is implemented, so the user is responsible for ordering the entries, and the device keeps that order 🎯. YANG calls this style "user ordered," and these lists are indicated with the statement ordered-by user.
For example, the order that firewall filter entries get applied to incoming traffic can change how that traffic gets filtered 🛡️. The user would need to decide whether the filter entry that discards all TCP traffic should come before or after the filter entry that allows all traffic from trusted interfaces. That choice of order would be crucial 😱.
YANG provides a rich set of facilities within NETCONF's <edit-config> operation that lets you control the order of entries in user-ordered lists 🎀. List entries can be inserted or rearranged, positioned as the first or last entry in the list, or positioned before or after another specific entry 🪄.
The "ordered-by" statement is covered in Section 7.7.5 💕.
7.7.2. The leaf-list's Substatements 📋
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| config | 7.19.1 | 0..1 |
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| max-elements | 7.7.4 | 0..1 |
| min-elements | 7.7.3 | 0..1 |
| must | 7.5.3 | 0..n |
| ordered-by | 7.7.5 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| type | 7.4 | 1 |
| units | 7.3.3 | 0..1 |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.7.3. The min-elements Statement 🔽
The "min-elements" statement is optional. It takes as an argument a non-negative integer that puts a constraint on valid list entries. A valid leaf-list or list MUST have at least min-elements entries 🧮.
If there's no "min-elements" statement, it defaults to zero 0️⃣.
The behavior of the constraint depends on the type of the leaf-list's or list's closest ancestor node in the schema tree that is not a non-presence container (see Section 7.5.1) 🌿:
- If this ancestor is a case node, the constraint is enforced if any other node from the case exists.
- Otherwise, it is enforced if the ancestor node exists.
The constraint is further enforced according to the rules in Section 8 💖.
7.7.4. The max-elements Statement 🧱
okay so the "max-elements" statement is optional 💅 and it takes either a positive integer or the string "unbounded" as its argument. it puts a limit on which list entries are valid. a valid leaf-list or list always has at most max-elements entries, no more 🙅♀️
if there's no "max-elements" statement, it defaults to "unbounded" ✨ (aka no limit, iykyk)
the "max-elements" constraint gets enforced by the rules in Section 8 🎀
7.7.5. The ordered-by Statement 🦋
the "ordered-by" statement decides whether the order of entries in a list is set by the user or by the system 🧚 the argument is either "system" or "user". if it's not there, the order defaults to "system" 💖
this statement gets ignored if the list represents state data, RPC output parameters, or notification content 🙈
see Section 7.7.1 for more deets 💜
7.7.5.1. ordered-by system
the entries are sorted in an unspecified order, so the implementation is free to sort them in whatever order is most appropriate 🌸 an implementation SHOULD use the same order for the same data, no matter how that data was created. a deterministic order makes it possible to compare things with simple tools like "diff", which is so iconic 🙌
this is the default order 👑
7.7.5.2. ordered-by user
the entries are sorted in an order defined by the user 🫶 this order is controlled by using special XML attributes in the <edit-config> request. see Section 7.7.7 for the details ✨
7.7.6. XML Mapping Rules 🧩
a leaf-list node is encoded as a series of XML elements. each element's local name is the leaf-list's identifier, and its namespace is the module's XML namespace (see Section 7.1.3) 💅
the value of each leaf-list entry gets encoded to XML according to its type, and it's sent as character data in the element 🍓
the XML elements representing leaf-list entries MUST appear in the order specified by the user if the leaf-list is "ordered-by user". otherwise, the order is implementation-dependent 🌈 the XML elements representing leaf-list entries MAY be interleaved with other sibling elements, unless the leaf-list defines RPC input or output parameters 🦋
see Section 7.7.8 for an example 🎀
7.7.7. NETCONF <edit-config> Operations 🛠️
leaf-list entries can be created and deleted, but not modified, through <edit-config>. you do this by using the "operation" attribute on the leaf-list entry's XML element 🫧
in an "ordered-by user" leaf-list, the attributes "insert" and "value" in the YANG XML namespace (Section 5.3.1) can be used to control where in the leaf-list the entry goes 📍 they work during "create" operations to insert a new entry, and during "merge" or "replace" operations to insert a new entry or move an existing one ✨
the "insert" attribute can take the values "first", "last", "before", and "after". if the value is "before" or "after", the "value" attribute MUST also be used to specify an existing entry in the leaf-list 💖
if there's no "insert" attribute in a "create" operation, it defaults to "last" 🌸
if several entries in an "ordered-by user" leaf-list are modified in the same <edit-config> request, the entries are modified one at a time, in the order of the XML elements in the request 🧚
in a <copy-config>, or an <edit-config> with a "replace" operation that covers the entire leaf-list, the leaf-list order is the same as the order of the XML elements in the request 👑
when a NETCONF server processes an <edit-config> request, the steps for a leaf-list node are:
- If the operation is "merge" or "replace", the leaf-list entry is created if it does not exist.
- If the operation is "create", the leaf-list entry is created if it does not exist. If the leaf-list entry already exists, a "data-exists" error is returned 💀
- If the operation is "delete", the entry is deleted from the leaf-list if it exists. If the leaf-list entry does not exist, a "data-missing" error is returned 😭
7.7.8. Usage Example 🎀
here's the leaf-list definition, verbatim, because details matter bestie 🫶
leaf-list allow-user {
type string;
description "A list of user name patterns to allow";
}
a corresponding XML instance example looks like this 💅
<allow-user>alice</allow-user>
<allow-user>bob</allow-user>
to create a new element in this list, using the default <edit-config> operation "merge" 🌷
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<services>
<ssh>
<allow-user>eric</allow-user>
</ssh>
</services>
</system>
</config>
</edit-config>
</rpc>
given the following ordered-by user leaf-list 🦋
leaf-list cipher {
type string;
ordered-by user;
description "A list of ciphers";
}
the following would be used to insert a new cipher "blowfish-cbc" after "3des-cbc" 🔥
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:yang="urn:ietf:params:xml:ns:yang:1">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<services>
<ssh>
<cipher nc:operation="create"
yang:insert="after"
yang:value="3des-cbc">blowfish-cbc</cipher>
</ssh>
</services>
</system>
</config>
</edit-config>
</rpc>
7.8. The list Statement 🌈
the "list" statement is used to define an interior data node in the schema tree 🧩 a list node can exist in multiple instances in the data tree, and each instance is called a list entry 🫧 the "list" statement takes one argument, which is an identifier, followed by a block of substatements that holds the detailed list info 📋
a list entry is uniquely identified by the values of the list's keys, if they're defined 🔑
7.8.1. The list's Substatements 📚
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| choice | 7.9 | 0..n |
| config | 7.19.1 | 0..1 |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| grouping | 7.11 | 0..n |
| if-feature | 7.18.2 | 0..n |
| key | 7.8.2 | 0..1 |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| max-elements | 7.7.4 | 0..1 |
| min-elements | 7.7.3 | 0..1 |
| must | 7.5.3 | 0..n |
| ordered-by | 7.7.5 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| typedef | 7.3 | 0..n |
| unique | 7.8.3 | 0..n |
| uses | 7.12 | 0..n |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.8.2. The list's key Statement 🗝️
the "key" statement MUST be present if the list represents configuration, and it MAY be present otherwise 🌸 it takes one argument: a string with a space-separated list of leaf identifiers for this list. a leaf identifier MUST NOT appear more than once in the key. each leaf identifier MUST refer to a child leaf of the list. the leafs can be defined directly in substatements to the list, or in groupings used in the list 🧚
the combined values of all the leafs specified in the key are used to uniquely identify a list entry 💖 all key leafs MUST be given values when a list entry is created. so any default values in the key leafs or their types get ignored 🙅♀️ it also means any mandatory statement in the key leafs gets ignored too 🫠
a leaf that's part of the key can be of any built-in or derived type, except it MUST NOT be the built-in type "empty" 🚫
all key leafs in a list MUST have the same value for their "config" as the list itself 🎀
the key string syntax is formally defined by the rule "key-arg" in Section 12 ✨
7.8.3. The list's unique Statement 🔐
the "unique" statement is used to put constraints on valid list entries 💅 it takes one argument: a string containing a space-separated list of schema node identifiers, which MUST be given in the descendant form (see the rule "descendant-schema-nodeid" in Section 12). each such schema node identifier MUST refer to a leaf 🍓
if one of the referenced leafs represents configuration data, then all of the referenced leafs MUST represent configuration data 🌷
the "unique" constraint says the combined values of all the leaf instances in the argument string, including leafs with default values, MUST be unique within all list entry instances in which all referenced leafs exist 🦋 the constraint is enforced according to the rules in Section 8 👑
the unique string syntax is formally defined by the rule "unique-arg" in Section 12 🧩
7.8.3.1. Usage Example 🌈
with the following list 📋
list server {
key "name";
unique "ip port";
leaf name {
type string;
}
leaf ip {
type inet:ip-address;
}
leaf port {
type inet:port-number;
}
}
the following configuration is not valid 🙈
<server>
<name>smtp</name>
<ip>192.0.2.1</ip>
<port>25</port>
</server>
<server>
<name>http</name>
<ip>192.0.2.1</ip>
<port>25</port>
</server>
the following configuration is valid 💖 since the "http" and "ftp" list entries don't have a value for all referenced leafs, they aren't taken into account when the "unique" constraint is enforced 🫶
<server>
<name>smtp</name>
<ip>192.0.2.1</ip>
<port>25</port>
</server>
<server>
<name>http</name>
<ip>192.0.2.1</ip>
</server>
<server>
<name>ftp</name>
<ip>192.0.2.1</ip>
</server>
7.8.4. The list's Child Node Statements 🧸
within a list, the "container", "leaf", "list", "leaf-list", "uses", "choice", and "anyxml" statements can be used to define child nodes to the list 🌼
7.8.5. XML Mapping Rules 🎀
Okay so a list gets encoded as a stack of XML elements, one for every entry in the list 🧚 Each element's local name is the list's identifier, and its namespace is the module's XML namespace (see Section 7.1.3). Cute, right?
The list's key nodes go in as subelements of the list's identifier element, in the same order they're defined in the "key" statement 🔑
The rest of the list's child nodes come after the keys, as subelements of the list element 💅 If the list defines RPC input or output parameters, those subelements follow the same order they're defined in within the "list" statement. Otherwise, they can come in any order, bestie, no stress 🌸
Now the ordering drama. The XML elements for list entries MUST appear in the order the user specified if the list is "ordered-by user". Otherwise the order is implementation-dependent 🦋 The list entry elements MAY be interleaved with other sibling elements, unless the list defines RPC input or output parameters. Main character energy for the implementation, honestly 👑
7.8.6. NETCONF <edit-config> Operations 💜
List entries can be created, deleted, replaced, and modified through <edit-config>, using the "operation" attribute on the list's XML element ✨ In every case, the values of all the keys get used to uniquely identify a list entry. If not all the keys are specified for a list entry, you get a "missing-element" error 😭
In an "ordered-by user" list, the attributes "insert" and "key" in the YANG XML namespace (Section 5.3.1) can be used to control where in the list the entry goes. You can use them during "create" operations to insert a new list entry, or during "merge" or "replace" operations to insert a new entry or move an existing one 🍓
The "insert" attribute can be "first", "last", "before", or "after". If the value is "before" or "after", the "key" attribute MUST also be used, so it can point at an existing element in the list. The value of the "key" attribute is the key predicates of the full instance identifier (see Section 9.13) for the list entry 🫶
If there's no "insert" attribute on a "create" operation, it defaults to "last" 🌈
If several entries in an "ordered-by user" list get modified in the same <edit-config> request, they're handled one at a time, in the order of the XML elements in the request 💖
In a <copy-config>, or in an <edit-config> with a "replace" operation that covers the entire list, the list entry order is the same as the order of the XML elements in the request ✨
When a NETCONF server processes an <edit-config> request, the procedure for a list node goes like this 🧠
Let's be clear about this part, it's the core logic 👀
- If the operation is "merge" or "replace", the list entry gets created if it doesn't exist. If it already exists and the "insert" and "key" attributes are present, the entry gets moved according to their values. If the entry exists and the "insert" and "key" attributes are not present, the entry doesn't move 🦋
- If the operation is "create", the list entry gets created if it doesn't exist. If it already exists, a "data-exists" error is returned 💀
- If the operation is "delete", the entry gets deleted from the list if it exists. If the list entry doesn't exist, a "data-missing" error is returned 🙈
7.8.7. Usage Example 🌸
Given the following list:
list user {
key "name";
config true;
description "This is a list of users in the system.";
leaf name {
type string;
}
leaf type {
type string;
}
leaf full-name {
type string;
}
}
A corresponding XML instance example:
<user>
<name>fred</name>
<type>admin</type>
<full-name>Fred Flintstone</full-name>
</user>
To create a new user "barney":
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<user nc:operation="create">
<name>barney</name>
<type>admin</type>
<full-name>Barney Rubble</full-name>
</user>
</system>
</config>
</edit-config>
</rpc>
To change the type of "fred" to "superuser":
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<user>
<name>fred</name>
<type>superuser</type>
</user>
</system>
</config>
</edit-config>
</rpc>
Given the following ordered-by user list:
list user {
description "This is a list of users in the system.";
ordered-by user;
config true;
key "name";
leaf name {
type string;
}
leaf type {
type string;
}
leaf full-name {
type string;
}
}
The following would be used to insert a new user "barney" after the user "fred":
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:yang="urn:ietf:params:xml:ns:yang:1">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config"
xmlns:ex="http://example.com/schema/config">
<user nc:operation="create"
yang:insert="after"
yang:key="[ex:name='fred']">
<name>barney</name>
<type>admin</type>
<full-name>Barney Rubble</full-name>
</user>
</system>
</config>
</edit-config>
</rpc>
The following would be used to move the user "barney" before the user "fred":
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:yang="urn:ietf:params:xml:ns:yang:1">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config"
xmlns:ex="http://example.com/schema/config">
<user nc:operation="merge"
yang:insert="before"
yang:key="[ex:name='fred']">
<name>barney</name>
</user>
</system>
</config>
</edit-config>
</rpc>
7.9. The choice Statement 🦋
The "choice" statement defines a set of alternatives, and only one of them may exist at any one time ✨ It takes an identifier as its argument, followed by a block of substatements that hold the detailed choice information. The identifier is used to identify the choice node in the schema tree. A choice node does not exist in the data tree 🌙
A choice is made up of a number of branches, defined with the "case" substatement. Each branch contains a number of child nodes. Nodes from at most one of the choice's branches exist at the same time 💅
See Section 8.3.2 for additional information 💜
7.9.1. The choice's Substatements 🍓
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| case | 7.9.2 | 0..n |
| config | 7.19.1 | 0..1 |
| container | 7.5 | 0..n |
| default | 7.9.3 | 0..1 |
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| mandatory | 7.9.4 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.9.2. The choice's case Statement 🌈
The "case" statement is used to define branches of the choice. It takes an identifier as its argument, followed by a block of substatements that hold the detailed case information 🎀
The identifier is used to identify the case node in the schema tree. A case node does not exist in the data tree 🧚
Inside a "case" statement, the "anyxml", "choice", "container", "leaf", "list", "leaf-list", and "uses" statements can be used to define child nodes of the case node. The identifiers of all these child nodes MUST be unique within all cases in a choice. For example, the following is illegal:
choice interface-type { // This example is illegal YANG
case a {
leaf ethernet { ... }
}
case b {
container ethernet { ...}
}
}
As a shorthand, the "case" statement can be left out if the branch contains a single "anyxml", "container", "leaf", "list", or "leaf-list" statement. In that case, the identifier of the case node is the same as the identifier in the branch statement. The following example:
choice interface-type {
container ethernet { ... }
}
is equivalent to:
choice interface-type {
case ethernet {
container ethernet { ... }
}
}
The case identifier MUST be unique within a choice 🫶
7.9.2.1. The case's Substatements 💖
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| choice | 7.9 | 0..n |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| uses | 7.12 | 0..n |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.9.3. The choice's default Statement ✨
okay so the "default" statement tells everyone which case gets to be the default if no child nodes from any of the choice's cases show up 💅 Its argument is the identifier of the "case" statement. If there's no "default" statement at all, there is no default case 🙈
The "default" statement MUST NOT be present on choices where "mandatory" is true 🚫
The default case only matters when we're thinking about the default values of nodes under the cases 🧚 The default values for nodes under the default case get used if none of the nodes under any of the cases are present.
There MUST NOT be any mandatory nodes (Section 3.1) directly under the default case 🙅♀️
Default values for child nodes under a case only get used if one of the nodes under that case is present, or if that case is the default case. If none of the nodes under a case are present and that case isn't the default case, the default values of that case's child nodes get ignored 💀
In this example, the choice defaults to "interval", so the default value kicks in if none of "daily", "time-of-day", or "manual" are present. If "daily" is present, the default value for "time-of-day" gets used 🌸
container transfer {
choice how {
default interval;
case interval {
leaf interval {
type uint16;
default 30;
units minutes;
}
}
case daily {
leaf daily {
type empty;
}
leaf time-of-day {
type string;
units 24-hour-clock;
default 1am;
}
}
case manual {
leaf manual {
type empty;
}
}
}
}
7.9.4. The choice's mandatory Statement 🎀
The "mandatory" statement is optional. It takes the string "true" or "false" as its argument, and it puts a constraint on what counts as valid data 👑 If "mandatory" is "true", then at least one node from exactly one of the choice's case branches MUST exist.
If it isn't specified, the default is "false" 🦋
How strict the constraint is depends on the type of the choice's closest ancestor node in the schema tree that isn't a non-presence container (see Section 7.5.1) 🍓
- If this ancestor is a case node, the constraint is enforced if any other node from the case exists.
- Otherwise, it is enforced if the ancestor node exists.
The constraint is further enforced according to the rules in Section 8 🫶
7.9.5. XML Mapping Rules 🌈
The choice and case nodes are not visible in XML, so they just kind of vanish 🫥
The child nodes of the selected "case" statement MUST be encoded in the same order they're defined in the "case" statement if they're part of an RPC input or output parameter definition. Otherwise, the subelements can be encoded in any order 🔥
7.9.6. NETCONF <edit-config> Operations 💜
Since only one of the choice's cases can be valid at any given time, creating a node from one case implicitly deletes all nodes from all the other cases. Iconic behavior, honestly 😭 If an <edit-config> operation creates a node from a case, the NETCONF server will delete any existing nodes that are defined in other cases inside the choice 🧹
7.9.7. Usage Example ⭐
Say we have this choice:
container protocol {
choice name {
case a {
leaf udp {
type empty;
}
}
case b {
leaf tcp {
type empty;
}
}
}
}
A matching XML instance example looks like this:
<protocol>
<tcp/>
</protocol>
To change the protocol from tcp to udp:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<system xmlns="http://example.com/schema/config">
<protocol>
<udp nc:operation="create"/>
</protocol>
</system>
</config>
</edit-config>
</rpc>
7.10. The anyxml Statement 🧚
The "anyxml" statement defines an interior node in the schema tree 🌸 It takes one argument, which is an identifier, followed by a block of substatements that holds detailed anyxml information.
The "anyxml" statement is used to represent an unknown chunk of XML. No restrictions are placed on the XML, which can be super useful in things like RPC replies. An example is the <filter> parameter in the <get-config> operation 🍓
An anyxml node cannot be augmented (see Section 7.15) 🙅♀️
Since using anyxml limits how much you can manipulate the content, it is RECOMMENDED that the "anyxml" statement not be used to represent configuration data 🙈
An anyxml node exists in zero or one instances in the data tree 🦋
7.10.1. The anyxml's Substatements 🎀
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| config | 7.19.1 | 0..1 |
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| mandatory | 7.6.5 | 0..1 |
| must | 7.5.3 | 0..n |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.10.2. XML Mapping Rules 🌷
An anyxml node is encoded as an XML element. The element's local name is the anyxml's identifier, and its namespace is the module's XML namespace (see Section 7.1.3). The value of the anyxml node is encoded as XML content of this element 💖
Note that any prefixes used in the encoding are local to each instance encoding. That means the same XML may be encoded differently by different implementations 🌈
7.10.3. NETCONF <edit-config> Operations 🔥
An anyxml node is treated as an opaque chunk of data. This data can only be modified in its entirety 🫧
Any "operation" attributes present on subelements of an anyxml node are ignored by the NETCONF server 🙈
When a NETCONF server processes an <edit-config> request, the elements of procedure for the anyxml node are:
- If the operation is "merge" or "replace", the node is created if it does not exist, and its value is set to the XML content of the anyxml node found in the XML RPC data.
- If the operation is "create", the node is created if it does not exist, and its value is set to the XML content of the anyxml node found in the XML RPC data. If the node already exists, a "data-exists" error is returned.
- If the operation is "delete", the node is deleted if it exists. If the node does not exist, a "data-missing" error is returned.
7.10.4. Usage Example 💅
Given the following "anyxml" statement:
anyxml data;
These are two valid encodings of the same anyxml value. Iykyk 🫶
<data xmlns:if="http://example.com/ns/interface">
<if:interface>
<if:ifIndex>1</if:ifIndex>
</if:interface>
</data>
<data>
<interface xmlns="http://example.com/ns/interface">
<ifIndex>1</ifIndex>
</interface>
</data>
7.11. The grouping Statement 🦋
The "grouping" statement is used to define a reusable block of nodes. It can be used locally in the module, in modules that include it, and by other modules that import from it, according to the rules in Section 5.5. It takes one argument, which is an identifier, followed by a block of substatements that holds detailed grouping information 🎀
The "grouping" statement is not a data definition statement, so it doesn't define any nodes in the schema tree. Main character energy for something that just sits there looking cute 🌸
A grouping is like a "structure" or a "record" in conventional programming languages 💜
Once a grouping is defined, it can be referenced in a "uses" statement (see Section 7.12). A grouping MUST NOT reference itself, neither directly nor indirectly through a chain of other groupings 🚫
If the grouping is defined at the top level of a YANG module or submodule, the grouping's identifier MUST be unique within the module ✨
A grouping is more than just textual substitution. It defines a collection of nodes. Identifiers appearing inside the grouping are resolved relative to the scope where the grouping is defined, not where it's used. Prefix mappings, type names, grouping names, and extension usage are all evaluated in the hierarchy where the "grouping" statement appears. For extensions, this means they're applied to the grouping node, not the uses node 👑
7.11.1. The grouping's Substatements 🍓
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| choice | 7.9 | 0..n |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| grouping | 7.11 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| typedef | 7.3 | 0..n |
| uses | 7.12 | 0..n |
+--------------+---------+-------------+
7.11.2. Usage Example 🧚
import ietf-inet-types {
prefix "inet";
}
grouping endpoint {
description "A reusable endpoint group.";
leaf ip {
type inet:ip-address;
}
leaf port {
type inet:port-number;
}
}
7.12. The uses Statement 💖
The "uses" statement is used to reference a "grouping" definition. It takes one argument, which is the name of the grouping 🫶
When a "uses" reference points at a grouping, the nodes defined by that grouping are copied into the current schema tree, and then updated according to the "refine" and "augment" statements. It's basically a copy-and-glow-up situation ✨
The identifiers defined in the grouping aren't bound to a namespace until the contents of the grouping get added to the schema tree via a "uses" statement that does not appear inside a "grouping" statement. At that point they get bound to the namespace of the current module 🌈
7.12.1. The uses's Substatements 🔥
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| augment | 7.15 | 0..1 |
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| refine | 7.12.2 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.12.2. The refine Statement ✨
okay so the "refine" statement lets you tweak some properties of each node in a grouping 🎀. Its argument is a string that points at a node in the grouping, and that node is called the refine's target node. If a node in the grouping isn't the target of any "refine" statement, it doesn't get refined at all, so it's used exactly the way it was defined in the grouping 🧚.
The argument string is a descendant schema node identifier (see Section 6.5) 💜.
Here's what you're allowed to refine:
- A leaf or choice node may get a default value, or a new default value if it already had one.
- Any node may get a specialized "description" string.
- Any node may get a specialized "reference" string.
- Any node may get a different "config" statement.
- A leaf, anyxml, or choice node may get a different "mandatory" statement.
- A container node may get a "presence" statement.
- A leaf, leaf-list, list, container, or anyxml node may get additional "must" expressions.
- A leaf-list or list node may get a different "min-elements" or "max-elements" statement.
7.12.3. XML Mapping Rules 🦋
Every node in the grouping gets encoded as if it was defined inline, even if it was imported from another module with a different XML namespace 🌈.
7.12.4. Usage Example 💖
So say you want to use the "endpoint" grouping from Section 7.11.2 inside an HTTP server definition in some other module 🍓. You'd do something like this:
import acme-system {
prefix "acme";
}
container http-server {
leaf name {
type string;
}
uses acme:endpoint;
}
And a matching XML instance looks like this:
<http-server>
<name>extern-web</name>
<ip>192.0.2.1</ip>
<port>80</port>
</http-server>
If port 80 should be the default for the HTTP server, you can add a default with a refine 👑:
container http-server {
leaf name {
type string;
}
uses acme:endpoint {
refine port {
default 80;
}
}
}
What if you want a list of servers where each server uses ip and port as its keys? Also super doable 🔥:
list server {
key "ip port";
leaf name {
type string;
}
uses acme:endpoint;
}
Now this one is a total no-go 🙈. Defining a leaf named "ip" when the grouping already brings in a node with that same identifier is an error:
container http-server {
uses acme:endpoint;
leaf ip { // illegal - same identifier "ip" used twice
type string;
}
}
7.13. The rpc Statement 🌸
The "rpc" statement is how you define a NETCONF RPC operation 💅. It takes one argument, an identifier, followed by a block of substatements that hold the detailed rpc info. That argument is the name of the RPC, and it gets used as the element name directly under the <rpc> element, as designated by the substitution group rpcOperation in [RFC4741] ✨.
The "rpc" statement defines an rpc node in the schema tree. Under that rpc node, there's also a schema node named "input" and a schema node named "output". Both "input" and "output" are defined in the module's namespace 🦋.
7.13.1. The rpc's Substatements 🧚
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| grouping | 7.11 | 0..n |
| if-feature | 7.18.2 | 0..n |
| input | 7.13.2 | 0..1 |
| output | 7.13.3 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| typedef | 7.3 | 0..n |
+--------------+---------+-------------+
7.13.2. The input Statement 🎀
The "input" statement is optional, and it's used to define input parameters for the RPC operation. It doesn't take an argument. The substatements under "input" define nodes under the RPC's input node 💜.
If a leaf in the input tree has a "mandatory" statement with the value "true", the leaf MUST be present in a NETCONF RPC invocation. Otherwise, the server MUST return a missing-element error 😭.
If a leaf in the input tree has a default value, the NETCONF server MUST use that value in the same cases described in Section 7.6.1. In those cases, the server MUST operationally behave as if the leaf was present in the NETCONF RPC invocation, with the default value as its value 🫶.
If a "config" statement is present for any node in the input tree, the "config" statement gets ignored 🙈.
If any node has a "when" statement that would evaluate to false, then that node MUST NOT be present in the input tree 💀.
7.13.2.1. The input's Substatements 🌈
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| choice | 7.9 | 0..n |
| container | 7.5 | 0..n |
| grouping | 7.11 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| typedef | 7.3 | 0..n |
| uses | 7.12 | 0..n |
+--------------+---------+-------------+
7.13.3. The output Statement 🍓
The "output" statement is optional too, and it's used to define output parameters for the RPC operation. It doesn't take an argument. The substatements under "output" define nodes under the RPC's output node 🧚.
If a leaf in the output tree has a "mandatory" statement with the value "true", the leaf MUST be present in a NETCONF RPC reply 💖.
If a leaf in the output tree has a default value, the NETCONF client MUST use that value in the same cases described in Section 7.6.1. In those cases, the client MUST operationally behave as if the leaf was present in the NETCONF RPC reply, with the default value as its value ✨.
If a "config" statement is present for any node in the output tree, the "config" statement gets ignored 🙈.
If any node has a "when" statement that would evaluate to false, then that node MUST NOT be present in the output tree 💀.
7.13.3.1. The output's Substatements 🌸
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| choice | 7.9 | 0..n |
| container | 7.5 | 0..n |
| grouping | 7.11 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| typedef | 7.3 | 0..n |
| uses | 7.12 | 0..n |
+--------------+---------+-------------+
7.13.4. XML Mapping Rules 🦋
An rpc node gets encoded as a child XML element of the <rpc> element defined in [RFC4741]. The element's local name is the rpc's identifier, and its namespace is the module's XML namespace (see Section 7.1.3) 💜.
Input parameters get encoded as child XML elements of the rpc node's XML element, in the same order they're defined within the "input" statement 🎀.
If the RPC operation invocation succeeded and no output parameters came back, the <rpc-reply> contains a single <ok/> element, which is defined in [RFC4741] 🌈. If output parameters do come back, they're encoded as child elements of the <rpc-reply> element defined in [RFC4741], in the same order they're defined within the "output" statement 🔥.
7.13.5. Usage Example 💅
The following example defines an RPC operation 🍓:
module rock {
namespace "http://example.net/rock";
prefix "rock";
rpc rock-the-house {
input {
leaf zip-code {
type string;
}
}
}
}
And here's a corresponding XML instance example of the complete rpc and rpc-reply 🧚:
<rpc message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<rock-the-house xmlns="http://example.net/rock">
<zip-code>27606-0100</zip-code>
</rock-the-house>
</rpc>
<rpc-reply message-id="101"
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<ok/>
</rpc-reply>
7.14. The notification Statement 🌈
The "notification" statement is used to define a NETCONF notification 🔔. It takes one argument, an identifier, followed by a block of substatements that hold the detailed notification info. The "notification" statement defines a notification node in the schema tree ✨.
If a leaf in the notification tree has a "mandatory" statement with the value "true", the leaf MUST be present in a NETCONF notification 😭.
If a leaf in the notification tree has a default value, the NETCONF client MUST use that value in the same cases described in Section 7.6.1. In those cases, the client MUST operationally behave as if the leaf was present in the NETCONF notification, with the default value as its value 🫶.
If a "config" statement is present for any node in the notification tree, the "config" statement gets ignored 🙈.
7.14.1. The notification's Substatements 💜
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| choice | 7.9 | 0..n |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| grouping | 7.11 | 0..n |
| if-feature | 7.18.2 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| typedef | 7.3 | 0..n |
| uses | 7.12 | 0..n |
+--------------+---------+-------------+
7.14.2. XML Mapping Rules 🎀
A notification node gets encoded as a child XML element of the <notification> element defined in NETCONF Event Notifications [RFC5277]. The element's local name is the notification's identifier, and its namespace is the module's XML namespace (see Section 7.1.3) 🌸.
7.14.3. Usage Example
okay so here's a notification example bestie 🎀 it's giving a whole module called event:
module event {
namespace "http://example.com/event";
prefix "ev";
notification event {
leaf event-class {
type string;
}
anyxml reporting-entity;
leaf severity {
type string;
}
}
}
and here's the XML instance of the complete notification, which is literally so cute 🦋:
<notification
xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
<eventTime>2008-07-08T00:01:00Z</eventTime>
<event xmlns="http://example.com/event">
<event-class>fault</event-class>
<reporting-entity>
<card>Ethernet0</card>
</reporting-entity>
<severity>major</severity>
</event>
</notification>
7.15. The augment Statement
okay the "augment" statement is my favourite glow-up tool ✨ it lets a module or submodule add stuff to the schema tree of an external module, or to the current module and its submodules, and it can also add nodes from a grouping in a "uses" statement 💅. The argument is a string that points at a node in the schema tree, and that node is called the augment's target node 🎯. The target node MUST be either a container, list, choice, case, input, output, or notification node 👑. Then it gets augmented with whatever nodes are defined in the substatements that come after the "augment" statement 🌸.
the argument string is a schema node identifier (check Section 6.5 for that one 💜). if the "augment" statement sits on the top level of a module or submodule, you MUST use the absolute form (the rule "absolute-schema-nodeid" in Section 12 🫶). but if the "augment" is a substatement of the "uses" statement, you MUST use the descendant form instead (the rule "descendant-schema-nodeid" in Section 12, obsessed 😭).
if the target node is a container, list, case, input, output, or notification node, you can use the "container", "leaf", "list", "leaf-list", "uses", and "choice" statements inside the "augment" 🧚.
if the target node is a choice node, you can use the "case" statement, or a case shorthand statement (see Section 7.9.2), inside the "augment" 🍓.
if the target node is in another module, then nodes added by the augmentation MUST NOT be mandatory nodes (see Section 3.1). this one is a big deal, don't skip it 🙈
and the "augment" statement MUST NOT add multiple nodes with the same name from the same module to the target node. no duplicate names, periodt 💀
7.15.1. The augment's Substatements
here's the substatement table for the augment, in a code block so it stays exact 🌈:
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| anyxml | 7.10 | 0..n |
| case | 7.9.2 | 0..n |
| choice | 7.9 | 0..n |
| container | 7.5 | 0..n |
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| leaf | 7.6 | 0..n |
| leaf-list | 7.7 | 0..n |
| list | 7.8 | 0..n |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| uses | 7.12 | 0..n |
| when | 7.19.5 | 0..1 |
+--------------+---------+-------------+
7.15.2. XML Mapping Rules
okay the XML side is simple and I love it 🦋 every data node defined in the "augment" statement becomes an XML element in the XML namespace of the module where the "augment" is specified 💖.
when a node gets augmented, the augmenting child nodes get encoded as subelements of the augmented node, in any order 🫧.
7.15.3. Usage Example
in namespace http://example.com/schema/interfaces, we have this base list 💜:
container interfaces {
list ifEntry {
key "ifIndex";
leaf ifIndex {
type uint32;
}
leaf ifDescr {
type string;
}
leaf ifType {
type iana:IfType;
}
leaf ifMtu {
type int32;
}
}
}
then, in namespace http://example.com/schema/ds0, we add to it 🍓:
import interface-module {
prefix "if";
}
augment "/if:interfaces/if:ifEntry" {
when "if:ifType='ds0'";
leaf ds0ChannelNumber {
type ChannelNumber;
}
}
and here's a corresponding XML instance example 🎀:
<interfaces xmlns="http://example.com/schema/interfaces"
xmlns:ds0="http://example.com/schema/ds0">
<ifEntry>
<ifIndex>1</ifIndex>
<ifDescr>Flintstone Inc Ethernet A562</ifDescr>
<ifType>ethernetCsmacd</ifType>
<ifMtu>1500</ifMtu>
</ifEntry>
<ifEntry>
<ifIndex>2</ifIndex>
<ifDescr>Flintstone Inc DS0</ifDescr>
<ifType>ds0</ifType>
<ds0:ds0ChannelNumber>1</ds0:ds0ChannelNumber>
</ifEntry>
</interfaces>
as another example 👑, suppose we have the choice from Section 7.9.7. this construct can be used to extend the protocol definition, which is so iconic:
augment /ex:system/ex:protocol/ex:name {
case c {
leaf smtp {
type empty;
}
}
}
and a corresponding XML instance example, two ways 🧚:
<ex:system>
<ex:protocol>
<ex:tcp/>
</ex:protocol>
</ex:system>
or
<ex:system>
<ex:protocol>
<other:smtp/>
</ex:protocol>
</ex:system>
7.16. The identity Statement
the "identity" statement is how you define a new globally unique, abstract, and untyped identity 🦋. its only purpose is to denote its name, its semantics, and that it exists, no more no less 💅. an identity can either be defined from scratch or derived from a base identity. the identity's argument is an identifier that is the name of the identity, and it's followed by a block of substatements that holds the detailed identity info 🌸.
the built-in datatype "identityref" (see Section 9.10) can be used to reference identities within a data model 💜.
7.16.1. The identity's Substatements
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| base | 7.16.2 | 0..1 |
| description | 7.19.3 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
+--------------+---------+-------------+
7.16.2. The base Statement
the "base" statement is optional 🫶. it takes a string argument that is the name of an existing identity, which the new identity is derived from. if there's no "base" statement, the identity is defined from scratch 🌈.
if a prefix is on the base name, it refers to an identity defined in the module that was imported with that prefix, or the local module if the prefix matches the local module's prefix 🎀. otherwise, an identity with the matching name MUST be defined in the current module or an included submodule 😭.
since submodules cannot include the parent module, any identities in the module that need to be exposed to submodules MUST be defined in a submodule. submodules can then include this submodule to find the definition of the identity 🧚.
an identity MUST NOT reference itself, neither directly nor indirectly through a chain of other identities. no identity loops allowed, bestie 💀
7.16.3. Usage Example
module crypto-base {
namespace "http://example.com/crypto-base";
prefix "crypto";
identity crypto-alg {
description
"Base identity from which all crypto algorithms
are derived.";
}
}
module des {
namespace "http://example.com/des";
prefix "des";
import "crypto-base" {
prefix "crypto";
}
identity des {
base "crypto:crypto-alg";
description "DES crypto algorithm";
}
identity des3 {
base "crypto:crypto-alg";
description "Triple DES crypto algorithm";
}
}
7.17. The extension Statement
the "extension" statement lets you define new statements within the YANG language 🔥. this new statement definition can be imported and used by other modules, so it's basically a shareable keyword 💖.
the statement's argument is an identifier that is the new keyword for the extension, and it must be followed by a block of substatements that holds the detailed extension info 🌸. the whole purpose of the "extension" statement is to define a keyword so it can be imported and used by other modules 👑.
the extension can be used like a normal YANG statement: the statement name, followed by an argument if the extension defines one, and an optional block of substatements 🦋. the statement's name is made by combining the prefix of the module where the extension was defined, a colon (":"), and the extension's keyword, with no whitespace in between 🫧. the substatements of an extension are defined by the extension itself, using some mechanism outside the scope of this specification 🎀. syntactically, the substatements MUST be YANG statements, or also defined using "extension" statements. YANG statements in extensions MUST follow the syntactical rules in Section 12 💅.
7.17.1. The extension's Substatements
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| argument | 7.17.2 | 0..1 |
| description | 7.19.3 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
+--------------+---------+-------------+
7.17.2. The argument Statement
the "argument" statement is optional 🌈. it takes a string that is the name of the argument to the keyword. if there's no argument statement, the keyword expects no argument when it's used 🧚.
the argument's name gets used in the YIN mapping, where it's an XML attribute or element name, depending on the argument's "yin-element" statement 🍓.
7.17.2.1. The argument's Substatements
+--------------+----------+-------------+
| substatement | section | cardinality |
+--------------+----------+-------------+
| yin-element | 7.17.2.2 | 0..1 |
+--------------+----------+-------------+
7.17.2.2. The yin-element Statement
the "yin-element" statement is optional 💜. it takes the string "true" or "false" as its argument, and it indicates whether the argument is mapped to an XML element in YIN or to an XML attribute (see Section 11) 🦋.
if there's no "yin-element" statement, it defaults to "false" ✨.
7.17.3. Usage Example
to define an extension 🎀:
module my-extensions {
...
extension c-define {
description
"Takes as argument a name string.
Makes the code generator use the given name in the
#define.";
argument "name";
}
}
to use the extension 💅:
module my-interfaces {
...
import my-extensions {
prefix "myext";
}
...
container interfaces {
...
myext:c-define "MY_INTERFACES";
}
}
7.18. Conformance-Related Statements 🎀
Okay so this section is all about conformance statements, the bits that say whether something is actually following the rules 💅 (check Section 5.6 for the full tea).
7.18.1. The feature Statement
Bestie, feature is how we mark chunks of the schema as conditional 🦋. You give it a name, and later an if-feature can point right at that name. Any schema node tagged with a feature gets ignored by the device unless the device supports that feature. So the module can change depending on what the device can actually do, which is giving richer model, different devices and roles all welcome ✨. The model literally describes the device's abilities from the inside 😭.
The argument is the name of the new feature, and it follows the identifier rules in Section 6.2. The if-feature statement uses this name to tie schema nodes to the feature 🔗.
Here's an example. A feature called "local-storage" means the device can store syslog messages on some kind of local storage. The local-storage-limit leaf is conditional on that feature, so if the device doesn't report support for it, that node just isn't supported 🙈.
module syslog {
...
feature local-storage {
description
"This feature means the device supports local
storage (memory, flash or disk) that can be used to
store syslog messages.";
}
container syslog {
leaf local-storage-limit {
if-feature local-storage;
type uint64;
units "kilobyte";
config false;
description
"The amount of local storage that can be
used to hold syslog messages.";
}
}
}
The if-feature statement can show up in a bunch of places across YANG syntax. Anything tagged with if-feature gets ignored when the device doesn't support that feature 💖.
Here's a big rule 💀: a feature MUST NOT reference itself, neither directly nor indirectly through a chain of other features. No circles allowed, periodt.
Another one: if a device wants to implement a feature that depends on other features (meaning the feature has one or more if-feature sub-statements), the device MUST also implement all the dependant features. You can't pick the fancy one and skip its prerequisites 🫶.
7.18.1.1. The feature's Substatements
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| if-feature | 7.18.2 | 0..n |
| status | 7.19.2 | 0..1 |
| reference | 7.19.4 | 0..1 |
+--------------+---------+-------------+
7.18.2. The if-feature Statement
The if-feature statement makes its parent statement conditional, the main character move 🎀. Its argument is the name of a feature, as defined by a feature statement. The parent statement is implemented by servers that support that feature.
If a prefix is on the feature name, it refers to a feature defined in the module imported with that prefix, or to the local module if the prefix matches the local module's prefix. Otherwise, a feature with the matching name MUST be defined in the current module or an included submodule 👑.
Submodules can't include the parent module, which is a bit of a plot twist 😭. So any features that need to be visible to submodules MUST be defined in a submodule. Then other submodules can include that submodule to find the feature's definition ✨.
7.18.3. The deviation Statement
The deviation statement describes a hierarchy in a module that the device doesn't implement faithfully 🙈. The argument is a string that identifies the node in the schema tree where the deviation happens. This node is called the deviation's target node. The contents of the deviation statement give the details.
The argument string is an absolute schema node identifier (see Section 6.5).
Deviations are how a device or class of devices shows the ways it varies from a standard. Which means deviations MUST never be part of a published standard, because they're the mechanism for learning how implementations differ from the standards 🧚.
Device deviations are strongly discouraged, and they MUST only be used as a last resort. Telling the app how a device fails to follow a standard is NOT a substitute for implementing the standard correctly. A device that deviates from a module is not fully compliant with that module 💅.
That said, sometimes a device just doesn't have the hardware or software to support parts of a standard module. When that happens, the device has two options: it either treats attempts to configure the unsupported parts as an error reported back to the poor unsuspecting application, or it quietly ignores those incoming requests. Neither choice is acceptable 💀.
So instead, YANG lets devices document the parts of a base module that aren't supported, or that are supported but with different syntax, using the deviation statement 🦋.
7.18.3.1. The deviation's Substatements
+--------------+----------+-------------+
| substatement | section | cardinality |
+--------------+----------+-------------+
| description | 7.19.3 | 0..1 |
| deviate | 7.18.3.2 | 1..n |
| reference | 7.19.4 | 0..1 |
+--------------+----------+-------------+
7.18.3.2. The deviate Statement
The deviate statement defines how the device's implementation of the target node deviates from its original definition. The argument is one of the strings "not-supported", "add", "replace", or "delete" 🌸.
The argument "not-supported" means the target node isn't implemented by this device. Done and dusted.
The argument "add" adds properties to the target node. The properties to add are identified by substatements of the deviate statement. If a property can only appear once, the property MUST NOT exist in the target node already 🙅♀️.
The argument "replace" replaces properties of the target node. The properties to replace are identified by substatements of the deviate statement. The properties to replace MUST exist in the target node.
The argument "delete" deletes properties from the target node. The properties to delete are identified by substatements of the delete statement. The substatement's keyword MUST match a corresponding keyword in the target node, and the argument's string MUST be equal to the corresponding keyword's argument string in the target node 🔍.
The deviates's Substatements
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| config | 7.19.1 | 0..1 |
| default | 7.6.4 | 0..1 |
| mandatory | 7.6.5 | 0..1 |
| max-elements | 7.7.4 | 0..1 |
| min-elements | 7.7.3 | 0..1 |
| must | 7.5.3 | 0..n |
| type | 7.4 | 0..1 |
| unique | 7.8.3 | 0..n |
| units | 7.3.3 | 0..1 |
+--------------+---------+-------------+
7.18.3.3. Usage Example
In this example, the device is telling client apps that it doesn't support the "daytime" service, in the style of RFC 867 🕰️.
deviation /base:system/base:daytime {
deviate not-supported;
}
The next example gives a leaf that has no default value a device-specific default:
deviation /base:system/base:user/base:type {
deviate add {
default "admin"; // new users are 'admin' by default
}
}
In this one, the device limits the number of name servers to 3:
deviation /base:system/base:name-server {
deviate replace {
max-elements 3;
}
}
If the original definition is:
container system {
must "daytime or time";
...
}
then a device might remove this must constraint by doing:
deviation "/base:system" {
deviate delete {
must "daytime or time";
}
}
7.19. Common Statements
This section defines substatements that are shared by several other statements 🫧.
7.19.1. The config Statement
The config statement takes as an argument the string "true" or "false". If config is "true", the definition represents configuration. Data nodes representing configuration will be part of the reply to a <get-config> request, and can be sent in a <copy-config> or <edit-config> request ⚙️.
If config is "false", the definition represents state data. Data nodes representing state data will be part of the reply to a <get>, but not to a <get-config> request, and they cannot be sent in a <copy-config> or <edit-config> request 🙈.
If config isn't specified, the default is the same as the parent schema node's config value. If the parent node is a case node, the value is the same as the case node's parent choice node 🧩.
If the top node doesn't have a config statement, the default is "true".
If a node has config set to "false", then no node underneath it can have config set to "true". No sneaky overrides 💅.
7.19.2. The status Statement
The status statement takes as an argument one of the strings "current", "deprecated", or "obsolete" 🚦.
- "current" means the definition is current and valid. ✅
- "deprecated" indicates an obsolete definition, but it permits new or continued implementation so that it fosters interoperability with older or existing implementations 🕰️.
- "obsolete" means the definition is obsolete and SHOULD NOT be implemented, and/or it can be removed from implementations 🗑️.
If no status is specified, the default is "current".
If a definition is "current", it MUST NOT reference a "deprecated" or "obsolete" definition within the same module. Keep it fresh, bestie ✨.
If a definition is "deprecated", it MUST NOT reference an "obsolete" definition within the same module.
For example, the following is illegal 🙅♀️:
typedef my-type {
status deprecated;
type int32;
}
leaf my-leaf {
status current;
type my-type; // illegal, since my-type is deprecated
}
7.19.3. The description Statement
The description statement takes as an argument a string that contains a human-readable textual description of this definition 📝. The text is written in a language (or languages) the module developer picks. For the sake of interoperability, it is RECOMMENDED to choose a language that's widely understood by the community of network admins who will use the module 🌍.
7.19.4. The reference Statement 📚
okay so the reference statement takes one string argument 🔗 and it's basically a textual cross-reference pointing at an external document. That document could be another module with related management info, or just something with extra details about this definition. it's giving "go read this for more" 💅
For example, a typedef for a "uri" data type could look like:
typedef uri {
type string;
reference
"RFC 3986: Uniform Resource Identifier (URI): Generic Syntax";
...
}
7.19.5. The when Statement ⏰
okay bestie, the when statement makes its parent data definition statement conditional 🧚. The node that parent defines is only valid when the condition in when is satisfied. The argument is an XPath expression (see Section 6.4) that formally spells out that condition. If the XPath expression conceptually evaluates to "true" for a particular instance, the node is valid. If not, it isn't. no cap, that's the whole vibe 💖
See Section 8.3.2 for additional information.
The XPath expression is conceptually evaluated in the following context, in addition to the definition in Section 6.4.1:
- If the
whenstatement is a child of anaugmentstatement, then the context node is the augment's target node in the data tree, if the target node is a data node. Otherwise, the context node is the closest ancestor node to the target node that is also a data node. - If the
whenstatement is a child of auses,choice, orcasestatement, then the context node is the closest ancestor node to theuses,choice, orcasenode that is also a data node. - If the
whenstatement is a child of any other data definition statement, the context node is the data definition's node in the data tree. - The accessible tree is made up of all nodes in the data tree, and all leafs with default values in use (see Section 7.6.1).
The accessible tree depends on the context node:
- If the context node represents configuration, the tree is the data in the NETCONF datastore where the context node exists. The XPath root node has all top-level configuration data nodes in all modules as children.
- If the context node represents state data, the tree is all state data on the device, and the
<running/>datastore. The XPath root node has all top-level data nodes in all modules as children. - If the context node represents notification content, the tree is the notification XML instance document. The XPath root node has the element representing the notification being defined as the only child.
- If the context node represents RPC input parameters, the tree is the RPC XML instance document. The XPath root node has the element representing the RPC operation being defined as the only child.
- If the context node represents RPC output parameters, the tree is the RPC reply instance document. The XPath root node has the elements representing the RPC output parameters as children.
The result of the XPath expression is converted to a boolean value using the standard XPath rules.
Note that the XPath expression is conceptually evaluated. This means that an implementation does not have to use an XPath evaluator on the device. The when statement can very well be implemented with specially written code. 🫶
8. Constraints 🔒
8.1. Constraints on Data
okay so several YANG statements define constraints on valid data 📋. These constraints get enforced in different ways depending on what kind of data the statement defines.
- If the constraint is defined on configuration data, it MUST be true in a valid configuration data tree.
- If the constraint is defined on state data, it MUST be true in a reply to a
<get>operation without a filter. - If the constraint is defined on notification content, it MUST be true in any notification instance.
- If the constraint is defined on RPC input parameters, it MUST be true in an invocation of the RPC operation.
- If the constraint is defined on RPC output parameters, it MUST be true in the RPC reply.
8.2. Hierarchy of Constraints
this one's a vibe 🌳 conditions on parent nodes affect constraints on child nodes as a natural consequence of the hierarchy of nodes. "must", "mandatory", "min-elements", and "max-elements" constraints are not enforced if the parent node has a "when" or "if-feature" property that is not satisfied on the current device. so if the parent is off the table, the kids get a pass 🙈
In this example, the "mandatory" constraint on the "longitude" leaf are not enforced on devices that lack the "has-gps" feature:
container location {
if-feature has-gps;
leaf longitude {
mandatory true;
...
}
}
8.3. Constraint Enforcement Model
bestie, for configuration data there are three windows when constraints MUST be enforced 🪟:
- during parsing of RPC payloads
- during processing of NETCONF operations
- during validation
Each of these scenarios is considered in the following sections. 💅
8.3.1. Payload Parsing
when content shows up in RPC payloads, it MUST be well-formed XML, following the hierarchy and content rules defined by the set of models the device implements. no skipping the rules, periodt 🎀
- If a leaf data value does not match the type constraints for the leaf, including those defined in the type's "range", "length", and "pattern" properties, the server MUST reply with an "invalid-value" error-tag in the rpc-error, and with the error-app-tag and error-message associated with the constraint, if any exist.
- If all keys of a list entry are not present, the server MUST reply with a "missing-element" error-tag in the rpc-error.
- If data for more than one case branch of a choice is present, the server MUST reply with a "bad-element" in the rpc-error.
- If data for a node tagged with
if-featureis present, and the feature is not supported by the device, the server MUST reply with an "unknown-element" error-tag in the rpc-error. - If data for a node tagged with
whenis present, and the "when" condition evaluates to "false", the server MUST reply with an "unknown-element" error-tag in the rpc-error. - For insert handling, if the value for the attributes "before" and "after" are not valid for the type of the appropriate key leafs, the server MUST reply with a "bad-attribute" error-tag in the rpc-error.
- If the attributes "before" and "after" appears in any element that is not a list whose "ordered-by" property is "user", the server MUST reply with an "unknown-attribute" error-tag in the rpc-error.
8.3.2. NETCONF <edit-config> Processing
after the incoming data is parsed, the NETCONF server performs the <edit-config> operation by applying the data to the configuration datastore 🛠️. During this processing, the following errors MUST be detected:
- Delete requests for non-existent data.
- Create requests for existent data.
- Insert requests with "before" or "after" parameters that do not exist.
During <edit-config> processing:
- If the NETCONF operation creates data nodes under a "choice", any existing nodes from other "case" branches are deleted by the server.
- If the NETCONF operation modifies a data node such that any node's "when" expression becomes false, then the node with the "when" expression is deleted by the server.
8.3.3. Validation
when datastore processing is complete, the final contents MUST obey all validation constraints ✅. This validation processing happens at different times depending on the datastore. If the datastore is <running/> or <startup/>, these constraints MUST be enforced at the end of the <edit-config> or <copy-config> operation. If the datastore is <candidate/>, the constraint enforcement is delayed until a <commit> or <validate> operation. 🥹
- Any "must" constraints MUST evaluate to "true".
- Any referential integrity constraints defined via the "path" statement MUST be satisfied.
- Any "unique" constraints on lists MUST be satisfied.
- The "min-elements" and "max-elements" constraints are enforced for lists and leaf-lists.
9. Built-In Types 🔢
YANG has a set of built-in types, kinda like the ones in lots of programming languages, but with some differences because of the special needs of the management information model ✨.
Additional types may be defined, derived from those built-in types or from other derived types. Derived types may use subtyping to formally restrict the set of possible values.
The different built-in types and their derived types allow different kinds of subtyping, namely length and regular expression restrictions of strings (Sections 9.4.4 and 9.4.6) and range restrictions of numeric types (Section 9.2.4).
The lexical representation of a value of a certain type is used in the NETCONF messages and when specifying default values and numerical ranges in YANG modules.
9.1. Canonical Representation
for most types there's a single canonical representation of the type's values 💎. Some types allow multiple lexical representations of the same value. For example, the positive integer "17" can be represented as "+17" or "17". Implementations MUST support all lexical representations specified in this document.
When a NETCONF server sends data, it MUST be in the canonical form.
Some types have a lexical representation that depends on the XML context in which they occur. These types do not have a canonical form. 🙃
9.2. The Integer Built-In Types
the integer built-in types are int8, int16, int32, int64, uint8, uint16, uint32, and uint64. They represent signed and unsigned integers of different sizes 🌈:
int8represents integer values between-128and127, inclusively.int16represents integer values between-32768and32767, inclusively.int32represents integer values between-2147483648and2147483647, inclusively.int64represents integer values between-9223372036854775808and9223372036854775807, inclusively.uint8represents integer values between0and255, inclusively.uint16represents integer values between0and65535, inclusively.uint32represents integer values between0and4294967295, inclusively.uint64represents integer values between0and18446744073709551615, inclusively.
9.2.1. Lexical Representation ✨
okay so an integer gets written as an optional sign ("+" or "-") followed by a run of decimal digits 💖 no sign at all? then it's "+" by default 🫶
for convenience, when you're setting a default value for an integer in a YANG module, you can also write it in hexadecimal or octal 🔥 hex is an optional sign, then "0x", then hex digits (uppercase or lowercase, both fine 💅). octal is an optional sign, then a "0", then octal digits 🦋
ngl there's a trap here 💀 if a default value in a YANG module starts with a leading zero ("0"), it gets read as octal. but in XML instance documents, an integer is always decimal, and leading zeros are allowed there 🙈
Examples:
// legal values
+4711 // legal positive value
4711 // legal positive value
-123 // legal negative value
0xf00f // legal positive hexadecimal value
-0xf // legal negative hexadecimal value
052 // legal positive octal value
// illegal values
- 1 // illegal intermediate space
9.2.2. Canonical Form 🌸
the canonical form of a positive integer leaves off the "+" sign 🫶 leading zeros are NOT allowed here (yes I'm yelling, it's a rule 😭). zero is written as "0", full stop ✨
9.2.3. Restrictions 💜
every integer type can be restricted with the "range" statement (Section 9.2.4). iconic behaviour, we love to see it 👑
9.2.4. The range Statement 🎀
the "range" statement is an optional substatement of the "type" statement. it takes a range expression string as its argument, and it's used to restrict integer and decimal built-in types, or types derived from those 🦋
a range is either one explicit value, or a lower-inclusive bound, two dots "..", and an upper-inclusive bound 🌈 you can give several values or ranges separated by "|". when you do, they all MUST be disjoint and they MUST be in ascending order 🔥
if you restrict a type that's already range-restricted, the new restriction MUST be equal or MORE limiting 💅 that means raising the lower bounds, lowering the upper bounds, removing explicit values or ranges, or splitting ranges into several ranges with gaps in between. ate that, no notes 💖
each explicit value and each range boundary value in the expression MUST match the type being restricted, or be one of the special values "min" or "max" ✨ "min" means the minimum value accepted for that type, and "max" means the maximum 🧚
the range expression syntax is formally defined by the rule "range-arg" in Section 12 🫶
9.2.4.1. The range's Substatements 🍓
+---------------+---------+-------------+
| substatement | section | cardinality |
+---------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| error-app-tag | 7.5.4.2 | 0..1 |
| error-message | 7.5.4.1 | 0..1 |
| reference | 7.19.4 | 0..1 |
+---------------+---------+-------------+
9.2.5. Usage Example 🌈
typedef my-base-int32-type {
type int32 {
range "1..4 | 10..20";
}
}
typedef my-type1 {
type my-base-int32-type {
// legal range restriction
range "11..max"; // 11..20
}
}
typedef my-type2 {
type my-base-int32-type {
// illegal range restriction
range "11..100";
}
}
9.3. The decimal64 Built-In Type 🦋
the decimal64 type represents a subset of the real numbers that can be written as decimal numerals 💖 its value space is the set of numbers you get by multiplying a 64-bit signed integer by a negative power of ten, so "i x 10^-n", where i is an integer64 and n is an integer from 1 to 18, inclusive 🔥
9.3.1. Lexical Representation ✨
a decimal64 value is written as an optional sign ("+" or "-"), then a run of decimal digits, optionally followed by a period ('.') as the decimal indicator and another run of decimal digits 🫶 no sign means "+" 🌸
9.3.2. Canonical Form 💅
the canonical form of a positive decimal64 leaves off the "+" sign 💜 the decimal point is REQUIRED, which is the same as saying it's mandatory here 😭 hmm, careful: the spec says the decimal point is required, so it's just required, and that's the whole vibe 🙈
leading and trailing zeros are NOT allowed, but there MUST be at least one digit before and at least one digit after the decimal point 👑 zero is written as "0.0" ✨
9.3.3. Restrictions 🎀
a decimal64 type can be restricted with the "range" statement (Section 9.2.4) 🧚
9.3.4. The fraction-digits Statement 🌈
the "fraction-digits" statement is a substatement of the "type" statement, and it MUST be present if the type is "decimal64" 🔥 it takes an integer from 1 to 18, inclusive 💖 it controls the size of the smallest possible difference between decimal64 values, by limiting the value space to numbers that can be written as "i x 10^-n", where n is the fraction-digits argument 🦋
the table below lists the minimum and maximum value for each fraction-digits value 🍓
+----------------+-----------------------+----------------------+
| fraction-digit | min | max |
+----------------+-----------------------+----------------------+
| 1 | -922337203685477580.8 | 922337203685477580.7 |
| 2 | -92233720368547758.08 | 92233720368547758.07 |
| 3 | -9223372036854775.808 | 9223372036854775.807 |
| 4 | -922337203685477.5808 | 922337203685477.5807 |
| 5 | -92233720368547.75808 | 92233720368547.75807 |
| 6 | -9223372036854.775808 | 9223372036854.775807 |
| 7 | -922337203685.4775808 | 922337203685.4775807 |
| 8 | -92233720368.54775808 | 92233720368.54775807 |
| 9 | -9223372036.854775808 | 9223372036.854775807 |
| 10 | -922337203.6854775808 | 922337203.6854775807 |
| 11 | -92233720.36854775808 | 92233720.36854775807 |
| 12 | -9223372.036854775808 | 9223372.036854775807 |
| 13 | -922337.2036854775808 | 922337.2036854775807 |
| 14 | -92233.72036854775808 | 92233.72036854775807 |
| 15 | -9223.372036854775808 | 9223.372036854775807 |
| 16 | -922.3372036854775808 | 922.3372036854775807 |
| 17 | -92.23372036854775808 | 92.23372036854775807 |
| 18 | -9.223372036854775808 | 9.223372036854775807 |
+----------------+-----------------------+----------------------+
9.3.5. Usage Example 💜
typedef my-decimal {
type decimal64 {
fraction-digits 2;
range "1 .. 3.14 | 10 | 20..max";
}
}
9.4. The string Built-In Type 🌸
the string built-in type represents human-readable strings in YANG 💖 the legal characters are tab, carriage return, line feed, and the legal characters of Unicode and ISO/IEC 10646 [ISO.10646] 🫶
;; any Unicode character, excluding the surrogate blocks,
;; FFFE, and FFFF.
string = *char
char = %x9 / %xA / %xD / %x20-D7FF / %xE000-FFFD /
%x10000-10FFFF
9.4.1. Lexical Representation 🦋
a string value is lexically represented as character data in XML instance documents 🎀
9.4.2. Canonical Form ✨
the canonical form is the same as the lexical representation 💅 no Unicode normalization is performed on string values, so what you write is what you get 💖
9.4.3. Restrictions 🔥
a string can be restricted with the "length" (Section 9.4.4) and "pattern" (Section 9.4.6) statements 🧚
9.4.4. The length Statement 👑
the "length" statement is an optional substatement of the "type" statement. it takes a length expression string as its argument, and it's used to restrict the built-in type "string", or types derived from "string" 🌈
a "length" statement restricts the number of Unicode characters in the string 🍓
a length range is either one explicit value, or a lower bound, two dots "..", and an upper bound 💜 you can give several values or ranges separated by "|". length-restricting values MUST NOT be negative 🙈 if you give multiple values or ranges, they all MUST be disjoint and they MUST be in ascending order 🔥 if you restrict a type that's already length-restricted, the new restriction MUST be equal or MORE limiting, meaning raising the lower bounds, lowering the upper bounds, removing explicit length values or ranges, or splitting ranges into several ranges with gaps in between 💅 a length value is a non-negative integer, or one of the special values "min" or "max" ✨ "min" and "max" mean the minimum and maximum length accepted for the type being restricted, respectively 🫶 an implementation is not required to support a length value larger than 18446744073709551615 🌸
the length expression syntax is formally defined by the rule "length-arg" in Section 12 🦋
9.4.4.1. The length's Substatements 🎀
+---------------+---------+-------------+
| substatement | section | cardinality |
+---------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| error-app-tag | 7.5.4.2 | 0..1 |
| error-message | 7.5.4.1 | 0..1 |
| reference | 7.19.4 | 0..1 |
+---------------+---------+-------------+
9.4.5. Usage Example 🧚
typedef my-base-str-type {
type string {
length "1..255";
}
}
type my-base-str-type {
// legal length refinement
length "11 | 42..max"; // 11 | 42..255
}
type my-base-str-type {
// illegal length refinement
length "1..999";
}
9.4.6. The pattern Statement 🌈
the "pattern" statement is an optional substatement of the "type" statement. it takes a regular expression string as its argument, as defined in [XSD-TYPES] 🍓 it's used to restrict the built-in type "string", or types derived from "string", to values that match the pattern 💖
if the type has multiple "pattern" statements, the expressions are ANDed together, so all of them have to match 🔥
if a pattern restriction is applied to a type that's already pattern-restricted, values must match all patterns in the base type, in addition to the new patterns 💜
9.4.6.1. The pattern's Substatements 🦋
+---------------+---------+-------------+
| substatement | section | cardinality |
+---------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| error-app-tag | 7.5.4.2 | 0..1 |
| error-message | 7.5.4.1 | 0..1 |
| reference | 7.19.4 | 0..1 |
+---------------+---------+-------------+
9.4.7. Usage Example 🫶
with the following type:
type string {
length "0..4";
pattern "[0-9a-fA-F]*";
}
the following strings match:
AB // legal
9A00 // legal
and the following strings do NOT match:
00ABAB // illegal, too long
xx00 // illegal, bad characters
9.5. The boolean Built-In Type 💅
the boolean built-in type represents a boolean value ✨
9.5.1. Lexical Representation 💖
Okay so the lexical representation of a boolean is just a string, and it's either "true" or "false" 🫶. These values MUST be in lowercase, bestie, no exceptions 💅.
9.5.2. Canonical Form ✨
The canonical form is literally the same as the lexical representation. Easy peasy 🌸.
9.5.3. Restrictions 🙈
A boolean cannot be restricted. It's already as simple as it gets 💜.
9.6. The enumeration Built-In Type 🌈
The enumeration built-in type is giving "a set of assigned names" energy. It represents values from that set 🧚.
9.6.1. Lexical Representation 🍓
The lexical representation of an enumeration value is the assigned name string ✨.
9.6.2. Canonical Form 🎀
The canonical form is the assigned name string, same as before 💅.
9.6.3. Restrictions 💀
An enumeration cannot be restricted. Iconic, no notes 👑.
9.6.4. The enum Statement 🦋
The "enum" statement is a substatement to the "type" statement. It MUST be present if the type is "enumeration" 🔥. It gets used over and over to specify each assigned name of an enumeration type. It takes one argument, a string, which is the assigned name. The string MUST NOT be empty and MUST NOT have any leading or trailing whitespace characters 🫧. The use of Unicode control codes SHOULD be avoided 🙈.
The statement can optionally be followed by a block of substatements that holds detailed enum info 🌷.
All assigned names in an enumeration MUST be unique, periodt 💖.
9.6.4.1. The enum's Substatements 🌸
Here's the table of substatements, no cap:
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| value | 9.6.4.2 | 0..1 |
+--------------+---------+-------------+
9.6.4.2. The value Statement 🧚
The "value" statement is optional, and it's used to attach an integer value to the assigned name for the enum 🎀. This integer value MUST be in the range -2147483648 to 2147483647, and it MUST be unique within the enumeration type 🔢. YANG and the XML encoding don't use the value at all, but it's carried along as a little convenience for implementors 🫶.
If a value isn't specified, one gets assigned automatically. If the "enum" substatement is the first one defined, the assigned value is zero (0). Otherwise, the assigned value is one greater than the current highest enum value ✨.
If the current highest value is equal to 2147483647, then an enum value MUST be specified for "enum" substatements that come after the one with the current highest value. Main character energy, but make it explicit 👑.
9.6.5. Usage Example 🍓
Check out this leaf:
leaf myenum {
type enumeration {
enum zero;
enum one;
enum seven {
value 7;
}
}
}
The lexical representation of the leaf "myenum" with value "seven" is:
<myenum>seven</myenum>
9.7. The bits Built-In Type 🦋
The bits built-in type represents a bit set 💜. A bits value is a set of flags, identified by small integer position numbers starting at 0. Each bit number has an assigned name 🌈.
9.7.1. Restrictions 🙈
A bits type cannot be restricted 💅.
9.7.2. Lexical Representation ✨
The lexical representation of the bits type is a space-separated list of the individual bit values that are set. An empty string therefore represents a value where no bits are set 🫧.
9.7.3. Canonical Form 🌸
In the canonical form, the bit values are separated by a single space character, and they appear ordered by their position (see Section 9.7.4.2) 🎀.
9.7.4. The bit Statement 🔥
The "bit" statement is a substatement to the "type" statement. It MUST be present if the type is "bits" 🙌. It gets used repeatedly to specify each assigned named bit of a bits type. It takes one argument, a string that is the assigned name of the bit. It's followed by a block of substatements that holds detailed bit info. The assigned name follows the same syntax rules as an identifier (see Section 6.2) 🧚.
All assigned names in a bits type MUST be unique 💖.
9.7.4.1. The bit's Substatements 🌷
Here's the table, bestie:
+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| description | 7.19.3 | 0..1 |
| reference | 7.19.4 | 0..1 |
| status | 7.19.2 | 0..1 |
| position | 9.7.4.2 | 0..1 |
+--------------+---------+-------------+
9.7.4.2. The position Statement 👑
The "position" statement is optional. It takes one argument, a non-negative integer value that specifies the bit's position within a hypothetical bit field 🔢. The position value MUST be in the range 0 to 4294967295, and it MUST be unique within the bits type. YANG and the NETCONF messages don't use it, but it's carried as a convenience to implementors 🫶.
If a bit position isn't specified, one gets assigned automatically. If the "bit" substatement is the first one defined, the assigned value is zero (0). Otherwise, the assigned value is one greater than the current highest bit position ✨.
If the current highest bit position value is equal to 4294967295, then a position value MUST be specified for "bit" substatements that come after the one with the current highest position value. Not me having to count that high 💀.
9.7.5. Usage Example 🎀
Given the following leaf:
leaf mybits {
type bits {
bit disable-nagle {
position 0;
}
bit auto-sense-speed {
position 1;
}
bit 10-Mb-only {
position 2;
}
}
default "auto-sense-speed";
}
The lexical representation of this leaf, with bit values disable-nagle and 10-Mb-only set, would be:
<mybits>disable-nagle 10-Mb-only</mybits>
9.8. The binary Built-In Type 🧚
The binary built-in type represents any binary data, which is a sequence of octets 🍓.
9.8.1. Restrictions 🙈
A binary can be restricted with the "length" (Section 9.4.4) statement. The length of a binary value is the number of octets it contains 💜.
9.8.2. Lexical Representation 🌈
Binary values are encoded with the base64 encoding scheme (see [RFC4648], Section 4) 🫧.
9.8.3. Canonical Form ✨
The canonical form of a binary value follows the rules in [RFC4648] 💅.
9.9. The leafref Built-In Type 🔗
The leafref type is used to reference a particular leaf instance in the data tree 🌸. The "path" substatement (Section 9.9.2) selects a set of leaf instances, and the leafref value space is the set of values of those leaf instances 🎀.
If the leaf with the leafref type represents configuration data, the leaf it refers to MUST also represent configuration. This puts a constraint on valid data 🔒. All leafref nodes MUST reference existing leaf instances, or leafs with default values in use (see Section 7.6.1), for the data to be valid. This constraint is enforced according to the rules in Section 8 🫶.
There MUST NOT be any circular chains of leafrefs. Absolutely no loops, bestie 🔁.
If the leaf that the leafref refers to is conditional based on one or more features (see Section 7.18.2), then the leaf with the leafref type MUST also be conditional based on at least the same set of features 🦋.
9.9.1. Restrictions 🙈
A leafref cannot be restricted 💅.
9.9.2. The path Statement 🧭
The "path" statement is a substatement to the "type" statement. It MUST be present if the type is "leafref" 🔥. It takes one argument, a string that MUST refer to a leaf or leaf-list node.
The syntax for a path argument is a subset of the XPath abbreviated syntax. Predicates are only used for constraining the values for the key nodes for list entries. Each predicate consists of exactly one equality test per key, and multiple adjacent predicates MAY be present if a list has multiple keys. The syntax is formally defined by the rule "path-arg" in Section 12 ✨.
The predicates are only used when more than one key reference is needed to uniquely identify a leaf instance. This happens if a list has multiple keys, or if a reference to a leaf other than the key in a list is needed. In these cases, multiple leafrefs are typically specified, and predicates are used to tie them together 🪢.
The "path" expression evaluates to a node set consisting of zero, one, or more nodes. If the leaf with the leafref type represents configuration data, this node set MUST be non-empty 🫧.
The "path" XPath expression is conceptually evaluated in the following context, in addition to the definition in Section 6.4.1:
- The context node is the node in the data tree for which the "path" statement is defined.
The accessible tree depends on the context node:
- If the context node represents configuration data, the tree is the data in the NETCONF datastore where the context node exists. The XPath root node has all top-level configuration data nodes in all modules as children.
- Otherwise, the tree is all state data on the device, and the
<running/>datastore. The XPath root node has all top-level data nodes in all modules as children.
9.9.3. Lexical Representation 🌷
A leafref value is encoded the same way as the leaf it references 💖.
9.9.4. Canonical Form 💅
The canonical form of a leafref is the same as the canonical form of the leaf it references ✨.
9.9.5. Usage Example 💅
okay so let's see leafref in action 🦋 first up, we've got this list:
list interface {
key "name";
leaf name {
type string;
}
leaf admin-status {
type admin-status;
}
list address {
key "ip";
leaf ip {
type yang:ip-address;
}
}
}
this leafref points at an interface that already exists 🎀
leaf mgmt-interface {
type leafref {
path "../interface/name";
}
}
and here's what the XML looks like 🧚
<interface>
<name>eth0</name>
</interface>
<interface>
<name>lo</name>
</interface>
<mgmt-interface>eth0</mgmt-interface>
next, these leafrefs point at an existing address on an interface, and this is giving main character energy 👑
container default-address {
leaf ifname {
type leafref {
path "../../interface/name";
}
}
leaf address {
type leafref {
path "../../interface[name = current()/../ifname]"
+ "/address/ip";
}
}
}
the XML for that one:
<interface>
<name>eth0</name>
<admin-status>up</admin-status>
<address>
<ip>192.0.2.1</ip>
</address>
<address>
<ip>192.0.2.2</ip>
</address>
</interface>
<interface>
<name>lo</name>
<admin-status>up</admin-status>
<address>
<ip>127.0.0.1</ip>
</address>
</interface>
<default-address>
<ifname>eth0</ifname>
<address>192.0.2.2</address>
</default-address>
now this list uses a leafref as one of its keys 🔑 it's kinda like a foreign key in a relational database, iykyk
list packet-filter {
key "if-name filter-id";
leaf if-name {
type leafref {
path "/interface/name";
}
}
leaf filter-id {
type uint32;
}
...
}
the XML:
<interface>
<name>eth0</name>
<admin-status>up</admin-status>
<address>
<ip>192.0.2.1</ip>
</address>
<address>
<ip>192.0.2.2</ip>
</address>
</interface>
<packet-filter>
<if-name>eth0</if-name>
<filter-id>1</filter-id>
...
</packet-filter>
<packet-filter>
<if-name>eth0</if-name>
<filter-id>2</filter-id>
...
</packet-filter>
last one for this bit: this notification has two leafrefs that both point at one existing admin-status 🔥
notification link-failure {
leaf if-name {
type leafref {
path "/interface/name";
}
}
leaf admin-status {
type leafref {
path
"/interface[name = current()/../if-name]"
+ "/admin-status";
}
}
}
and the XML notification:
<notification
xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
<eventTime>2008-04-01T00:01:00Z</eventTime>
<link-failure xmlns="http://acme.example.com/system">
<if-name>eth0</if-name>
<admin-status>up</admin-status>
</link-failure>
</notification>
9.10. The identityref Built-In Type 🦋
the identityref type is how you point at an existing identity (see Section 7.16) 🫶
9.10.1. Restrictions
identityrefs can't be restricted 🙅
9.10.2. The identityref's base Statement 🎀
the "base" statement is a substatement of "type", and it MUST be present if the type is "identityref". its argument is the name of an identity, as defined by an "identity" statement. if there's a prefix on the identity name, it points at an identity from the module that got imported with that prefix. otherwise, an identity with the matching name MUST be defined in the current module or an included submodule.
valid values for an identityref are any identities derived from the identityref's base identity. on a particular server, the valid values get narrowed down even more, to just the identities defined in the modules that server supports 💅
9.10.3. Lexical Representation
an identityref gets encoded as the referred identity's qualified name, as defined in [XML-NAMES]. if the prefix isn't there, the namespace of the identityref is the default namespace in effect on the element that holds the identityref value.
when an identityref gets a default value using the "default" statement, the identity name in that default value MAY have a prefix. if a prefix is present on the identity name, it points at an identity from the module imported with that prefix. otherwise, an identity with the matching name MUST be defined in the current module or an included submodule.
9.10.4. Canonical Form
the lexical form depends on the XML context the value shows up in, so this type doesn't have a canonical form 🌈
9.10.5. Usage Example ✨
with the identity definitions in Section 7.16.3 and this module:
module my-crypto {
namespace "http://example.com/my-crypto";
prefix mc;
import "crypto-base" {
prefix "crypto";
}
identity aes {
base "crypto:crypto-alg";
}
leaf crypto {
type identityref {
base "crypto:crypto-alg";
}
}
}
the leaf "crypto" gets encoded like this when the value is the "des3" identity defined in the "des" module:
<crypto xmlns:des="http://example.com/des">des:des3</crypto>
prefixes used in the encoding are local to each instance encoding, so the same identityref can be encoded differently by different implementations 🧚 for example, this encodes the same leaf as above:
<crypto xmlns:x="http://example.com/des">x:des3</crypto>
if the "crypto" leaf's value is "aes" from the "my-crypto" module instead, it can be encoded as:
<crypto xmlns:mc="http://example.com/my-crypto">mc:aes</crypto>
or, using the default namespace:
<crypto>aes</crypto>
9.11. The empty Built-In Type 🌸
the empty built-in type represents a leaf that doesn't have any value. it conveys information by its presence or absence, bestie 🙈
an empty type cannot have a default value.
9.11.1. Restrictions
an empty type cannot be restricted.
9.11.2. Lexical Representation
Not applicable.
9.11.3. Canonical Form
Not applicable.
9.11.4. Usage Example
this leaf:
leaf enable-qos {
type empty;
}
will be encoded as:
<enable-qos/>
if it exists 🍓
9.12. The union Built-In Type 🧚
the union built-in type represents a value that corresponds to one of its member types. ✨
when the type is "union", the "type" statement (Section 7.4) MUST be present. it gets used to repeatedly specify each member type of the union, and it takes as an argument a string that's the name of a member type.
a member type can be any built-in or derived type, except it MUST NOT be one of the built-in types "empty" or "leafref". no cap, that's a hard no 💀
when a string representing a union data type gets validated, the string is validated against each member type, in the order they're specified in the "type" statement, until a match is found.
any default value or "units" property defined in the member types is not inherited by the union type. so don't expect it to carry over 🥺
example:
type union {
type int32;
type enumeration {
enum "unbounded";
}
}
9.12.1. Restrictions
a union cannot be restricted. however, each member type can be restricted, based on the rules defined in Section 9. 🎀
9.12.2. Lexical Representation
the lexical representation of a union is a value that corresponds to the representation of any one of the member types.
9.12.3. Canonical Form
the canonical form of a union value is the same as the canonical form of the value's member type 💖
9.13. The instance-identifier Built-In Type 🔍
the instance-identifier built-in type is used to uniquely identify a particular instance node in the data tree. it's giving precision 💅
the syntax for an instance-identifier is a subset of the XPath abbreviated syntax, formally defined by the rule "instance-identifier" in Section 12. it's used to uniquely identify a node in the data tree. predicates only get used for specifying the values for the key nodes of list entries, a value of a leaf-list entry, or a positional index for a list without keys. for identifying list entries with keys, each predicate has one equality test per key, and each key MUST have a corresponding predicate.
if the leaf with the instance-identifier type represents configuration data, and the "require-instance" property (Section 9.13.2) is "true", the node it refers to MUST also represent configuration. this puts a constraint on valid data. all such leaf nodes MUST reference existing nodes or leaf nodes with their default value in use (see Section 7.6.1) for the data to be valid. this constraint is enforced according to the rules in Section 8. 🫶
the "instance-identifier" XPath expression is conceptually evaluated in the following context, in addition to the definition in Section 6.4.1:
- the context node is the root node in the accessible tree.
the accessible tree depends on the leaf with the instance-identifier type:
- if this leaf represents configuration data, the tree is the data in the NETCONF datastore where the leaf exists. the XPath root node has all top-level configuration data nodes in all modules as children.
- otherwise, the tree is all state data on the device, and the
<running/>datastore. the XPath root node has all top-level data nodes in all modules as children. 🧚
9.13.1. Restrictions
an instance-identifier can be restricted with the "require-instance" statement (Section 9.13.2). ✨
9.13.2. The require-instance Statement
the "require-instance" statement is a substatement of the "type" statement, and it MAY be present if the type is "instance-identifier". it takes as an argument the string "true" or "false". if this statement isn't present, it defaults to "true".
if "require-instance" is "true", it means the instance being referred to MUST exist for the data to be valid. this constraint is enforced according to the rules in Section 8. 👑
if "require-instance" is "false", it means the instance being referred to MAY exist in valid data. 🌸
9.13.3. Lexical Representation
an instance-identifier value is lexically represented as a string. all node names in an instance-identifier value MUST be qualified with explicit namespace prefixes, and those prefixes MUST be declared in the XML namespace scope in the instance-identifier's XML element. 🎀
prefixes used in the encoding are local to each instance encoding. so the same instance-identifier can be encoded differently by different implementations 🦋
9.13.4. Canonical Form
since the lexical form depends on the XML context the value shows up in, this type doesn't have a canonical form 🌈
9.13.5. Usage Example 💖
okay so here are some instance-identifiers for you to stare at 👀 each one points at a different spot in the data tree 🧚
/* instance-identifier for a container */
/ex:system/ex:services/ex:ssh
/* instance-identifier for a leaf */
/ex:system/ex:services/ex:ssh/ex:port
/* instance-identifier for a list entry */
/ex:system/ex:user[ex:name='fred']
/* instance-identifier for a leaf in a list entry */
/ex:system/ex:user[ex:name='fred']/ex:type
/* instance-identifier for a list entry with two keys */
/ex:system/ex:server[ex:ip='192.0.2.1'][ex:port='80']
/* instance-identifier for a leaf-list entry */
/ex:system/ex:services/ex:ssh/ex:cipher[.='blowfish-cbc']
/* instance-identifier for a list entry without keys */
/ex:stats/ex:port[3]
10. Updating a Module ✨
ngl models get updated all the time 🔄 once people have been using one for a while, you might want to tweak it 💅 BUT you can't just change stuff if it could break things between a client on the old spec and a server on the new one 😭 that would be chaos bestie 💀
For any published change, a new revision statement (Section 7.1.9) MUST be included in front of the existing revision statements. If there are no existing revision statements, then one MUST be added to identify the new revision. Furthermore, any necessary changes MUST be applied to any meta-data statements, including the organization and contact statements (Sections 7.1.7, 7.1.8). 🎀
Note that definitions inside a module can be imported by any other module, and they get referenced in import statements by the module name 🦋 so a module name MUST NOT be changed. Also the namespace statement MUST NOT be changed, because every XML element is qualified by the namespace 🧚
Obsolete definitions MUST NOT be removed from modules, since other modules might still be referencing their identifiers 🙈
Here's what you're allowed to do to a definition 🌈
- An
enumerationtype may have new enums added, provided the old enums's values do not change. - A
bitstype may have new bits added, provided the old bit positions do not change. - A
range,length, orpatternstatement may expand the allowed value space. - A
defaultstatement may be added to a leaf that does not have a default value (either directly or indirectly through its type). - A
unitsstatement may be added. - A
referencestatement may be added or updated. - A
muststatement may be removed or its constraint relaxed. - A
mandatorystatement may be removed or changed fromtruetofalse. - A
min-elementsstatement may be removed, or changed to require fewer elements. - A
max-elementsstatement may be removed, or changed to allow more elements. - A
descriptionstatement may be added or clarified without changing the semantics of the definition. - New typedefs, groupings, rpcs, notifications, extensions, features, and identities may be added.
- New data definition statements may be added if they do not add mandatory nodes (Section 3.1) to existing nodes or at the top level in a module or submodule, or if they are conditionally dependent on a new feature (i.e., have an
if-featurestatement that refers to a new feature). - A new
casestatement may be added. - A node that represented state data may be changed to represent configuration, provided it is not mandatory (Section 3.1).
- An
if-featurestatement may be removed, provided its node is not mandatory (Section 3.1). - A
statusstatement may be added, or changed fromcurrenttodeprecatedorobsolete, or fromdeprecatedtoobsolete. - A
typestatement may be replaced with anothertypestatement that does not change the syntax or semantics of the type. For example, an inline type definition may be replaced with a typedef, but an int8 type cannot be replaced by an int16, since the syntax would change. - Any set of data definition nodes may be replaced with another set of syntactically and semantically equivalent nodes. For example, a set of leafs may be replaced by a
usesof a grouping with the same leafs. - A module may be split into a set of submodules, or a submodule may be removed, provided the definitions in the module do not change in any other way than allowed here.
- The
prefixstatement may be changed, provided all local uses of the prefix also are changed.
Anything else is a no-go 🚫 if the semantics of any earlier definition change (meaning any non-editorial change to a definition that isn't in the list above), then it MUST be done with a brand new definition that has a brand new identifier. No sneaky edits allowed 🫶
In statements that have any data definition statements as substatements, those data definition substatements MUST NOT be reordered. 🔥
11. YIN ✨
A YANG module can be translated into an XML-based syntax called YIN 🌸 the translated module is called a YIN module, and this section describes the symmetric mapping rules between the two formats 💜
YANG and YIN hold the same information, just written in different notations 🍓 YIN lets developers write YANG data models in XML, which means they get to use all the amazing XML tools for filtering and validating data, auto-generating code and docs, and other tasks ✨ Tools like XSLT or XML validators can be used too 🧚
The mapping between YANG and YIN does not change the information content of the model. Comments and whitespace are not preserved. 🙈
11.1. Formal YIN Definition ✨
okay so YANG keywords and YIN elements are basically twins 👯♀️ 🎀 Every YIN element is named exactly the same as its YANG keyword, no cap. That also means the very first element (the root, aka the document element) of any YIN doc is ALWAYS either <module> or <submodule>. 💅
YIN elements that match the YANG keywords live in the namespace with the URI "urn:ietf:params:xml:ns:yang:yin:1". 🌸
YIN elements that come from extension keywords live in the namespace of whichever YANG module declared that extension with the "extension" statement. So each extension gets its own little home base 🏡 🦋
The names of all YIN elements MUST be properly qualified with their namespaces, using the standard [XML-NAMES] stuff, meaning the "xmlns" and "xmlns:xxx" attributes. no skipping this bestie 😭 🫶
Now the arguments. A YANG statement's argument can show up in YIN as either an XML attribute or a subelement of the keyword element 🧚 Table 1 is the cheat sheet for the built-in keywords. For extensions, the argument mapping gets spelled out inside the "extension" statement (check out Section 7.17 for that 💜). Here are the rules for arguments:
- If the argument is an attribute, that attribute has no namespace. 🍓
- If the argument is an element, it's qualified by the same namespace as its parent keyword element. ✨
- If the argument is an element, it MUST be the first child of the keyword element. 👑
Substatements are just extra children of the keyword element, and their relative order MUST match the order they had in YANG. Order matters, periodt 💅
Comments in YANG MAY be mapped to XML comments. Totally optional, bestie 🙈
Mapping of arguments of the YANG statements.
+------------------+---------------+-------------+
| keyword | argument name | yin-element |
+------------------+---------------+-------------+
| anyxml | name | false |
| argument | name | false |
| augment | target-node | false |
| base | name | false |
| belongs-to | module | false |
| bit | name | false |
| case | name | false |
| choice | name | false |
| config | value | false |
| contact | text | true |
| container | name | false |
| default | value | false |
| description | text | true |
| deviate | value | false |
| deviation | target-node | false |
| enum | name | false |
| error-app-tag | value | false |
| error-message | value | true |
| extension | name | false |
| feature | name | false |
| fraction-digits | value | false |
| grouping | name | false |
| identity | name | false |
| if-feature | name | false |
| import | module | false |
| include | module | false |
| input | <no argument> | n/a |
| key | value | false |
| leaf | name | false |
| leaf-list | name | false |
| length | value | false |
| list | name | false |
| mandatory | value | false |
| max-elements | value | false |
| min-elements | value | false |
| module | name | false |
| must | condition | false |
| namespace | uri | false |
| notification | name | false |
| ordered-by | value | false |
| organization | text | true |
| output | <no argument> | n/a |
| path | value | false |
| pattern | value | false |
| position | value | false |
| prefix | value | false |
| presence | value | false |
| range | value | false |
| reference | text | true |
| refine | target-node | false |
| require-instance | value | false |
| revision | date | false |
| revision-date | date | false |
| rpc | name | false |
| status | value | false |
| submodule | name | false |
| type | name | false |
| typedef | name | false |
| unique | tag | false |
| units | name | false |
| uses | name | false |
| value | value | false |
| when | condition | false |
| yang-version | value | false |
| yin-element | value | false |
+------------------+---------------+-------------+
Table 1
11.1.1. Usage Example 🎀
Okay, let's see this in action. Here's the YANG module:
module acme-foo {
namespace "http://acme.example.com/foo";
prefix "acfoo";
import my-extensions {
prefix "myext";
}
list interface {
key "name";
leaf name {
type string;
}
leaf mtu {
type uint32;
description "The MTU of the interface.";
myext:c-define "MY_MTU";
}
}
}
The extension "c-define" is defined in Section 7.17.3, and this module gets translated into the following YIN 🦋 ✨
<module name="acme-foo"
xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:acfoo="http://acme.example.com/foo"
xmlns:myext="http://example.com/my-extensions">
<namespace uri="http://acme.example.com/foo"/>
<prefix value="acfoo"/>
<import module="my-extensions">
<prefix value="myext"/>
</import>
<list name="interface">
<key value="name"/>
<leaf name="name">
<type name="string"/>
</leaf>
<leaf name="mtu">
<type name="uint32"/>
<description>
<text>The MTU of the interface.</text>
</description>
<myext:c-define name="MY_MTU"/>
</leaf>
</list>
</module>
12. YANG ABNF Grammar 🧚
Okay, the vibe here is that in YANG, almost all statements are unordered 🌈 The ABNF grammar [RFC5234] is what defines the canonical order. To make modules easier to read, it is RECOMMENDED that clauses be entered in this order. Lowkey the best way to keep things tidy 💖
Inside the ABNF grammar, the unordered statements are marked with comments 💜
This grammar assumes the scanner replaces YANG comments with a single space character. Just so you know how the pieces fit together 🫶
<CODE BEGINS> file "yang.abnf"
module-stmt = optsep module-keyword sep identifier-arg-str
optsep
"{" stmtsep
module-header-stmts
linkage-stmts
meta-stmts
revision-stmts
body-stmts
"}" optsep
submodule-stmt = optsep submodule-keyword sep identifier-arg-str
optsep
"{" stmtsep
submodule-header-stmts
linkage-stmts
meta-stmts
revision-stmts
body-stmts
"}" optsep
module-header-stmts = ;; these stmts can appear in any order
[yang-version-stmt stmtsep]
namespace-stmt stmtsep
prefix-stmt stmtsep
submodule-header-stmts =
;; these stmts can appear in any order
[yang-version-stmt stmtsep]
belongs-to-stmt stmtsep
meta-stmts = ;; these stmts can appear in any order
[organization-stmt stmtsep]
[contact-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
linkage-stmts = ;; these stmts can appear in any order
*(import-stmt stmtsep)
*(include-stmt stmtsep)
revision-stmts = *(revision-stmt stmtsep)
body-stmts = *((extension-stmt /
feature-stmt /
identity-stmt /
typedef-stmt /
grouping-stmt /
data-def-stmt /
augment-stmt /
rpc-stmt /
notification-stmt /
deviation-stmt) stmtsep)
data-def-stmt = container-stmt /
leaf-stmt /
leaf-list-stmt /
list-stmt /
choice-stmt /
anyxml-stmt /
uses-stmt
yang-version-stmt = yang-version-keyword sep yang-version-arg-str
optsep stmtend
yang-version-arg-str = < a string that matches the rule
yang-version-arg >
yang-version-arg = "1"
import-stmt = import-keyword sep identifier-arg-str optsep
"{" stmtsep
prefix-stmt stmtsep
[revision-date-stmt stmtsep]
"}"
include-stmt = include-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
[revision-date-stmt stmtsep]
"}")
namespace-stmt = namespace-keyword sep uri-str optsep stmtend
uri-str = < a string that matches the rule
URI in RFC 3986 >
prefix-stmt = prefix-keyword sep prefix-arg-str
optsep stmtend
belongs-to-stmt = belongs-to-keyword sep identifier-arg-str
optsep
"{" stmtsep
prefix-stmt stmtsep
"}"
organization-stmt = organization-keyword sep string
optsep stmtend
contact-stmt = contact-keyword sep string optsep stmtend
description-stmt = description-keyword sep string optsep
stmtend
reference-stmt = reference-keyword sep string optsep stmtend
okay bestie, we're still in the ABNF grammar and it's giving precision 💅✨ these rules are how every YANG statement gets parsed, so no cap, every character matters 🎀
units-stmt = units-keyword sep string optsep stmtend
revision-stmt = revision-keyword sep revision-date optsep
(";" /
"{" stmtsep
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
revision-date = date-arg-str
revision-date-stmt = revision-date-keyword sep revision-date stmtend
extension-stmt = extension-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[argument-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
argument-stmt = argument-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
[yin-element-stmt stmtsep]
"}")
yin-element-stmt = yin-element-keyword sep yin-element-arg-str
stmtend
yin-element-arg-str = < a string that matches the rule
yin-element-arg >
yin-element-arg = true-keyword / false-keyword
identity-stmt = identity-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[base-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
base-stmt = base-keyword sep identifier-ref-arg-str
optsep stmtend
feature-stmt = feature-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
*(if-feature-stmt stmtsep)
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
if-feature-stmt = if-feature-keyword sep identifier-ref-arg-str
optsep stmtend
typedef-stmt = typedef-keyword sep identifier-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
type-stmt stmtsep
[units-stmt stmtsep]
[default-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}"
type-stmt = type-keyword sep identifier-ref-arg-str optsep
(";" /
"{" stmtsep
type-body-stmts
"}")
type-body-stmts = numerical-restrictions /
decimal64-specification /
string-restrictions /
enum-specification /
leafref-specification /
identityref-specification /
instance-identifier-specification /
bits-specification /
union-specification
numerical-restrictions = range-stmt stmtsep
range-stmt = range-keyword sep range-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[error-message-stmt stmtsep]
[error-app-tag-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
decimal64-specification = fraction-digits-stmt
fraction-digits-stmt = fraction-digits-keyword sep
fraction-digits-arg-str stmtend
fraction-digits-arg-str = < a string that matches the rule
fraction-digits-arg >
fraction-digits-arg = ("1" ["0" / "1" / "2" / "3" / "4" /
"5" / "6" / "7" / "8"])
/ "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
string-restrictions = ;; these stmts can appear in any order
[length-stmt stmtsep]
*(pattern-stmt stmtsep)
length-stmt = length-keyword sep length-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[error-message-stmt stmtsep]
[error-app-tag-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
pattern-stmt = pattern-keyword sep string optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[error-message-stmt stmtsep]
[error-app-tag-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
default-stmt = default-keyword sep string stmtend
enum-specification = 1*(enum-stmt stmtsep)
enum-stmt = enum-keyword sep string optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[value-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
leafref-specification =
;; these stmts can appear in any order
path-stmt stmtsep
[require-instance-stmt stmtsep]
path-stmt = path-keyword sep path-arg-str stmtend
require-instance-stmt = require-instance-keyword sep
require-instance-arg-str stmtend
require-instance-arg-str = < a string that matches the rule
require-instance-arg >
require-instance-arg = true-keyword / false-keyword
instance-identifier-specification =
[require-instance-stmt stmtsep]
identityref-specification =
base-stmt stmtsep
union-specification = 1*(type-stmt stmtsep)
bits-specification = 1*(bit-stmt stmtsep)
bit-stmt = bit-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[position-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}"
"}")
position-stmt = position-keyword sep
position-value-arg-str stmtend
position-value-arg-str = < a string that matches the rule
position-value-arg >
position-value-arg = non-negative-integer-value
status-stmt = status-keyword sep status-arg-str stmtend
status-arg-str = < a string that matches the rule
status-arg >
status-arg = current-keyword /
obsolete-keyword /
deprecated-keyword
config-stmt = config-keyword sep
config-arg-str stmtend
config-arg-str = < a string that matches the rule
config-arg >
config-arg = true-keyword / false-keyword
mandatory-stmt = mandatory-keyword sep
mandatory-arg-str stmtend
mandatory-arg-str = < a string that matches the rule
mandatory-arg >
mandatory-arg = true-keyword / false-keyword
presence-stmt = presence-keyword sep string stmtend
ordered-by-stmt = ordered-by-keyword sep
ordered-by-arg-str stmtend
ordered-by-arg-str = < a string that matches the rule
ordered-by-arg >
ordered-by-arg = user-keyword / system-keyword
must-stmt = must-keyword sep string optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[error-message-stmt stmtsep]
[error-app-tag-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
error-message-stmt = error-message-keyword sep string stmtend
error-app-tag-stmt = error-app-tag-keyword sep string stmtend
min-elements-stmt = min-elements-keyword sep
min-value-arg-str stmtend
min-value-arg-str = < a string that matches the rule
min-value-arg >
min-value-arg = non-negative-integer-value
max-elements-stmt = max-elements-keyword sep
max-value-arg-str stmtend
max-value-arg-str = < a string that matches the rule
max-value-arg >
max-value-arg = unbounded-keyword /
positive-integer-value
value-stmt = value-keyword sep integer-value stmtend
grouping-stmt = grouping-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*((typedef-stmt /
grouping-stmt) stmtsep)
*(data-def-stmt stmtsep)
"}")
container-stmt = container-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
*(must-stmt stmtsep)
[presence-stmt stmtsep]
[config-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*((typedef-stmt /
grouping-stmt) stmtsep)
*(data-def-stmt stmtsep)
"}")
okay bestie, the grammar is where the rules get spicy 🎀 we're in the statement rules now: leaf, leaf-list, list, choice, case, uses, refine, augment and friends. the little comments saying "these stmts can appear in any order" are doing a LOT of work 💅 no cap, read them carefully 🦋
leaf-stmt = leaf-keyword sep identifier-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
type-stmt stmtsep
[units-stmt stmtsep]
*(must-stmt stmtsep)
[default-stmt stmtsep]
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}"
leaf-list-stmt = leaf-list-keyword sep identifier-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
type-stmt stmtsep
[units-stmt stmtsep]
*(must-stmt stmtsep)
[config-stmt stmtsep]
[min-elements-stmt stmtsep]
[max-elements-stmt stmtsep]
[ordered-by-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}"
list-stmt = list-keyword sep identifier-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
*(must-stmt stmtsep)
[key-stmt stmtsep]
*(unique-stmt stmtsep)
[config-stmt stmtsep]
[min-elements-stmt stmtsep]
[max-elements-stmt stmtsep]
[ordered-by-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*((typedef-stmt /
grouping-stmt) stmtsep)
1*(data-def-stmt stmtsep)
"}"
key-stmt = key-keyword sep key-arg-str stmtend
key-arg-str = < a string that matches the rule
key-arg >
key-arg = node-identifier *(sep node-identifier)
unique-stmt = unique-keyword sep unique-arg-str stmtend
unique-arg-str = < a string that matches the rule
unique-arg >
unique-arg = descendant-schema-nodeid
*(sep descendant-schema-nodeid)
choice-stmt = choice-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
[default-stmt stmtsep]
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*((short-case-stmt / case-stmt) stmtsep)
"}")
short-case-stmt = container-stmt /
leaf-stmt /
leaf-list-stmt /
list-stmt /
anyxml-stmt
case-stmt = case-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*(data-def-stmt stmtsep)
"}")
anyxml-stmt = anyxml-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
*(must-stmt stmtsep)
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
uses-stmt = uses-keyword sep identifier-ref-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*(refine-stmt stmtsep)
*(uses-augment-stmt stmtsep)
"}")
refine-stmt = refine-keyword sep refine-arg-str optsep
(";" /
"{" stmtsep
(refine-container-stmts /
refine-leaf-stmts /
refine-leaf-list-stmts /
refine-list-stmts /
refine-choice-stmts /
refine-case-stmts /
refine-anyxml-stmts)
"}")
refine-arg-str = < a string that matches the rule
refine-arg >
refine-arg = descendant-schema-nodeid
refine-container-stmts =
;; these stmts can appear in any order
*(must-stmt stmtsep)
[presence-stmt stmtsep]
[config-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
refine-leaf-stmts = ;; these stmts can appear in any order
*(must-stmt stmtsep)
[default-stmt stmtsep]
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
refine-leaf-list-stmts =
;; these stmts can appear in any order
*(must-stmt stmtsep)
[config-stmt stmtsep]
[min-elements-stmt stmtsep]
[max-elements-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
refine-list-stmts = ;; these stmts can appear in any order
*(must-stmt stmtsep)
[config-stmt stmtsep]
[min-elements-stmt stmtsep]
[max-elements-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
refine-choice-stmts = ;; these stmts can appear in any order
[default-stmt stmtsep]
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
refine-case-stmts = ;; these stmts can appear in any order
[description-stmt stmtsep]
[reference-stmt stmtsep]
refine-anyxml-stmts = ;; these stmts can appear in any order
*(must-stmt stmtsep)
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
uses-augment-stmt = augment-keyword sep uses-augment-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
1*((data-def-stmt stmtsep) /
(case-stmt stmtsep))
"}"
uses-augment-arg-str = < a string that matches the rule
uses-augment-arg >
uses-augment-arg = descendant-schema-nodeid
augment-stmt = augment-keyword sep augment-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
[when-stmt stmtsep]
*(if-feature-stmt stmtsep)
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
1*((data-def-stmt stmtsep) /
(case-stmt stmtsep))
"}"
augment-arg-str = < a string that matches the rule
augment-arg >
augment-arg = absolute-schema-nodeid
unknown-statement = prefix ":" identifier [sep string] optsep
(";" / "{" *unknown-statement2 "}")
unknown-statement2 = [prefix ":"] identifier [sep string] optsep
(";" / "{" *unknown-statement2 "}")
when-stmt = when-keyword sep string optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
[description-stmt stmtsep]
[reference-stmt stmtsep]
"}")
rpc-stmt = rpc-keyword sep identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
*(if-feature-stmt stmtsep)
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*((typedef-stmt /
grouping-stmt) stmtsep)
[input-stmt stmtsep]
[output-stmt stmtsep]
"}")
input-stmt = input-keyword optsep
"{" stmtsep
;; these stmts can appear in any order
*((typedef-stmt /
grouping-stmt) stmtsep)
1*(data-def-stmt stmtsep)
"}"
output-stmt = output-keyword optsep
"{" stmtsep
;; these stmts can appear in any order
*((typedef-stmt /
grouping-stmt) stmtsep)
1*(data-def-stmt stmtsep)
"}"
notification-stmt = notification-keyword sep
identifier-arg-str optsep
(";" /
"{" stmtsep
;; these stmts can appear in any order
*(if-feature-stmt stmtsep)
[status-stmt stmtsep]
[description-stmt stmtsep]
[reference-stmt stmtsep]
*((typedef-stmt /
grouping-stmt) stmtsep)
*(data-def-stmt stmtsep)
"}")
deviation-stmt = deviation-keyword sep
deviation-arg-str optsep
"{" stmtsep
;; these stmts can appear in any order
[description-stmt stmtsep]
[reference-stmt stmtsep]
(deviate-not-supported-stmt /
1*(deviate-add-stmt /
deviate-replace-stmt /
deviate-delete-stmt))
"}"
deviation-arg-str = < a string that matches the rule
deviation-arg >
deviation-arg = absolute-schema-nodeid
deviate-not-supported-stmt =
deviate-keyword sep
not-supported-keyword optsep
(";" /
"{" stmtsep
"}")
deviate-add-stmt = deviate-keyword sep add-keyword optsep
(";" /
"{" stmtsep
[units-stmt stmtsep]
*(must-stmt stmtsep)
*(unique-stmt stmtsep)
[default-stmt stmtsep]
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[min-elements-stmt stmtsep]
[max-elements-stmt stmtsep]
"}")
deviate-delete-stmt = deviate-keyword sep delete-keyword optsep
(";" /
"{" stmtsep
[units-stmt stmtsep]
*(must-stmt stmtsep)
*(unique-stmt stmtsep)
[default-stmt stmtsep]
"}")
deviate-replace-stmt = deviate-keyword sep replace-keyword optsep
(";" /
"{" stmtsep
[type-stmt stmtsep]
[units-stmt stmtsep]
[default-stmt stmtsep]
[config-stmt stmtsep]
[mandatory-stmt stmtsep]
[min-elements-stmt stmtsep]
[max-elements-stmt stmtsep]
"}")
;; Ranges
range-arg-str = < a string that matches the rule
range-arg >
range-arg = range-part *(optsep "|" optsep range-part)
range-part = range-boundary
[optsep ".." optsep range-boundary]
range-boundary = min-keyword / max-keyword /
integer-value / decimal-value
;; Lengths
length-arg-str = < a string that matches the rule
length-arg >
length-arg = length-part *(optsep "|" optsep length-part)
length-part = length-boundary
[optsep ".." optsep length-boundary]
length-boundary = min-keyword / max-keyword /
non-negative-integer-value
;; Date
date-arg-str = < a string that matches the rule
date-arg >
date-arg = 4DIGIT "-" 2DIGIT "-" 2DIGIT
;; Schema Node Identifiers
schema-nodeid = absolute-schema-nodeid /
descendant-schema-nodeid
absolute-schema-nodeid = 1*("/" node-identifier)
descendant-schema-nodeid =
node-identifier
absolute-schema-nodeid
node-identifier = [prefix ":"] identifier
;; Instance Identifiers
instance-identifier = 1*("/" (node-identifier *predicate))
predicate = "[" *WSP (predicate-expr / pos) *WSP "]"
predicate-expr = (node-identifier / ".") *WSP "=" *WSP
((DQUOTE string DQUOTE) /
(SQUOTE string SQUOTE))
pos = non-negative-integer-value
;; leafref path
path-arg-str = < a string that matches the rule
path-arg >
path-arg = absolute-path / relative-path
absolute-path = 1*("/" (node-identifier *path-predicate))
relative-path = 1*(".." "/") descendant-path
descendant-path = node-identifier
[*path-predicate absolute-path]
path-predicate = "[" *WSP path-equality-expr *WSP "]"
path-equality-expr = node-identifier *WSP "=" *WSP path-key-expr
path-key-expr = current-function-invocation *WSP "/" *WSP
rel-path-keyexpr
rel-path-keyexpr = 1*(".." *WSP "/" *WSP)
*(node-identifier *WSP "/" *WSP)
node-identifier
;;; Keywords, using abnfgen's syntax for case-sensitive strings
;; statement keywords
anyxml-keyword = 'anyxml'
argument-keyword = 'argument'
augment-keyword = 'augment'
base-keyword = 'base'
belongs-to-keyword = 'belongs-to'
bit-keyword = 'bit'
case-keyword = 'case'
choice-keyword = 'choice'
config-keyword = 'config'
contact-keyword = 'contact'
container-keyword = 'container'
default-keyword = 'default'
description-keyword = 'description'
enum-keyword = 'enum'
error-app-tag-keyword = 'error-app-tag'
error-message-keyword = 'error-message'
extension-keyword = 'extension'
deviation-keyword = 'deviation'
deviate-keyword = 'deviate'
feature-keyword = 'feature'
fraction-digits-keyword = 'fraction-digits'
grouping-keyword = 'grouping'
identity-keyword = 'identity'
if-feature-keyword = 'if-feature'
import-keyword = 'import'
include-keyword = 'include'
input-keyword = 'input'
key-keyword = 'key'
leaf-keyword = 'leaf'
leaf-list-keyword = 'leaf-list'
length-keyword = 'length'
list-keyword = 'list'
mandatory-keyword = 'mandatory'
max-elements-keyword = 'max-elements'
min-elements-keyword = 'min-elements'
module-keyword = 'module'
must-keyword = 'must'
namespace-keyword = 'namespace'
notification-keyword= 'notification'
ordered-by-keyword = 'ordered-by'
organization-keyword= 'organization'
output-keyword = 'output'
path-keyword = 'path'
pattern-keyword = 'pattern'
position-keyword = 'position'
prefix-keyword = 'prefix'
presence-keyword = 'presence'
range-keyword = 'range'
reference-keyword = 'reference'
refine-keyword = 'refine'
require-instance-keyword = 'require-instance'
revision-keyword = 'revision'
revision-date-keyword = 'revision-date'
rpc-keyword = 'rpc'
status-keyword = 'status'
submodule-keyword = 'submodule'
type-keyword = 'type'
typedef-keyword = 'typedef'
unique-keyword = 'unique'
units-keyword = 'units'
uses-keyword = 'uses'
value-keyword = 'value'
when-keyword = 'when'
yang-version-keyword= 'yang-version'
yin-element-keyword = 'yin-element'
;; other keywords
add-keyword = 'add'
current-keyword = 'current'
delete-keyword = 'delete'
deprecated-keyword = 'deprecated'
false-keyword = 'false'
max-keyword = 'max'
min-keyword = 'min'
not-supported-keyword = 'not-supported'
obsolete-keyword = 'obsolete'
replace-keyword = 'replace'
system-keyword = 'system'
true-keyword = 'true'
unbounded-keyword = 'unbounded'
user-keyword = 'user'
current-function-invocation = current-keyword *WSP "(" *WSP ")"
;; Basic Rules
prefix-arg-str = < a string that matches the rule
prefix-arg >
prefix-arg = prefix
prefix = identifier
identifier-arg-str = < a string that matches the rule
identifier-arg >
identifier-arg = identifier
;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
identifier = (ALPHA / "_")
*(ALPHA / DIGIT / "_" / "-" / ".")
identifier-ref-arg-str = < a string that matches the rule
identifier-ref-arg >
identifier-ref-arg = [prefix ":"] identifier
string = < an unquoted string as returned by
the scanner >
integer-value = ("-" non-negative-integer-value) /
non-negative-integer-value
non-negative-integer-value = "0" / positive-integer-value
positive-integer-value = (non-zero-digit *DIGIT)
zero-integer-value = 1*DIGIT
stmtend = ";" / "{" *unknown-statement "}"
sep = 1*(WSP / line-break)
; unconditional separator
optsep = *(WSP / line-break)
stmtsep = *(WSP / line-break / unknown-statement)
line-break = CRLF / LF
non-zero-digit = %x31-39
decimal-value = integer-value ("." zero-integer-value)
SQUOTE = %x27
; ' (Single Quote)
;;
;; RFC 5234 core rules.
;;
ALPHA = %x41-5A / %x61-7A
; A-Z / a-z
CR = %x0D
; carriage return
CRLF = CR LF
; Internet standard new line
DIGIT = %x30-39
; 0-9
DQUOTE = %x22
; " (Double Quote)
HEXDIG = DIGIT /
%x61 / %x62 / %x63 / %x64 / %x65 / %x66
; only lower-case a..f
HTAB = %x09
; horizontal tab
LF = %x0A
; linefeed
SP = %x20
; space
VCHAR = %x21-7E
; visible (printing) characters
WSP = SP / HTAB
; whitespace
13. Error Responses for YANG Related Errors ✨
okay so, this part is about the NETCONF errors that pop up when something YANG-related goes wrong with the data model 🫶 there's a whole bunch of them, and each one gets a default value. the twist? if the relevant YANG statement has an error-app-tag substatement, that overrides the default, so the statement itself gets the final say 💅🎀
13.1. Error Message for Data That Violates a unique Statement 💖
okay so if a NETCONF operation would leave config data where a unique constraint gets broken, you get this error back 🙈
error-tag: operation-failed
error-app-tag: data-not-unique
error-info: <non-unique>: Contains an instance identifier that
points to a leaf that invalidates the unique
constraint. This element is present once for each
non-unique leaf.
The <non-unique> element is in the YANG
namespace ("urn:ietf:params:xml:ns:yang:1").
13.2. Error Message for Data That Violates a max-elements Statement 🎀
if a NETCONF operation would leave config data where a list or a leaf-list has too many entries, this is the error you get ✨
error-tag: operation-failed
error-app-tag: too-many-elements
it only comes back once, with the error-path pointing at the list node, even if there's more than one extra child 💅
13.3. Error Message for Data That Violates a min-elements Statement 🌸
same vibe but backwards 🙃 if a NETCONF operation would leave a list or a leaf-list with too few entries, you get this:
error-tag: operation-failed
error-app-tag: too-few-elements
again, it's returned once, with the error-path on the list node, even if more than one child is missing 🧚
13.4. Error Message for Data That Violates a must Statement 🔥
if a NETCONF operation would leave config data where the restrictions from a "must" statement get violated, this error shows up. the only exception is when the "must" statement has its own "error-app-tag" substatement 👀
error-tag: operation-failed
error-app-tag: must-violation
13.5. Error Message for Data That Violates a require-instance Statement 🦋
if a NETCONF operation would leave config data where an instance-identifier leaf marked with require-instance "true" points at something that doesn't exist, you get:
error-tag: data-missing
error-app-tag: instance-required
error-path: Path to the instance-identifier leaf.
13.6. Error Message for Data That Does Not Match a leafref Type 🍓
if a NETCONF operation would leave config data where a leafref leaf points at a non-existing instance, this one's the error ✨
error-tag: data-missing
error-app-tag: instance-required
error-path: Path to the leafref leaf.
13.7. Error Message for Data That Violates a mandatory choice Statement 🌈
if a NETCONF operation would leave config data where no nodes exist in a mandatory choice, the error is:
error-tag: data-missing
error-app-tag: missing-choice
error-path: Path to the element with the missing choice.
error-info: <missing-choice>: Contains the name of the missing
mandatory choice.
The <missing-choice> element is in the YANG
namespace ("urn:ietf:params:xml:ns:yang:1").
13.8. Error Message for the "insert" Operation 💜
if the "insert" and "key" or "value" attributes get used in an <edit-config> for a list or leaf-list node, and the "key" or "value" points at a non-existing instance, you get this one:
error-tag: bad-attribute
error-app-tag: missing-instance
14. IANA Considerations ✨
okay this is the registry bit 📋 this document creates a registry for YANG module and submodule names. the registry is called "YANG Module Names".
The registry shall record the following for each entry:
- the name of the module or submodule
- for modules, the assigned XML namespace
- for modules, the prefix of the module
- for submodules, the name of the module it belongs to
- a reference to the (sub)module's documentation (e.g., the RFC number)
There are no initial assignments 🫶
for allocation, RFC publication is required as per RFC 5226 [RFC5226]. all registered YANG module names MUST comply with the rules for identifiers stated in Section 6.2, and MUST have a module name prefix. no skipping that one, bestie 💅
the module name prefix 'ietf-' is reserved for IETF stream documents [RFC4844], while the module name prefix 'irtf-' is reserved for IRTF stream documents. modules published in other RFC streams MUST have a similar suitable prefix 🌸
all module and submodule names in the registry MUST be unique.
all XML namespaces in the registry MUST be unique.
this document registers two URIs for the YANG and YIN XML namespaces in the IETF XML registry [RFC3688]. following the format in RFC 3688, these are the ones that got registered:
URI: urn:ietf:params:xml:ns:yang:yin:1
URI: urn:ietf:params:xml:ns:yang:1
Registrant Contact: The IESG.
XML: N/A, the requested URIs are XML namespaces.
this document also registers two new media types, as described in the following sections 🦋
14.1. Media type application/yang 🎀
MIME media type name: application
MIME subtype name: yang
Mandatory parameters: none
Optional parameters: none
Encoding considerations: 8-bit
Security considerations: See Section 15 in RFC 6020
Interoperability considerations: None
Published specification: RFC 6020
Applications that use this media type:
YANG module validators, web servers used for downloading YANG
modules, email clients, etc.
Additional information:
Magic Number: None
File Extension: .yang
Macintosh file type code: 'TEXT'
Personal and email address for further information:
Martin Bjorklund <mbj@tail-f.com>
Intended usage: COMMON
Author:
This specification is a work item of the IETF NETMOD working group,
with mailing list address <netmod@ietf.org>.
Change controller:
The IESG <iesg@ietf.org>
14.2. Media type application/yin+xml 🌷
MIME media type name: application
MIME subtype name: yin+xml
Mandatory parameters: none
Optional parameters:
"charset": This parameter has identical semantics to the charset
parameter of the "application/xml" media type as specified in
[RFC3023].
Encoding considerations:
Identical to those of "application/xml" as
described in [RFC3023], Section 3.2.
Security considerations: See Section 15 in RFC 6020
Interoperability considerations: None
Published specification: RFC 6020
Applications that use this media type:
YANG module validators, web servers used for downloading YANG
modules, email clients, etc.
Additional information:
Magic Number: As specified for "application/xml" in [RFC3023],
Section 3.2.
File Extension: .yin
Macintosh file type code: 'TEXT'
Personal and email address for further information:
Martin Bjorklund <mbj@tail-f.com>
Intended usage: COMMON
Author:
This specification is a work item of the IETF NETMOD working group,
with mailing list address <netmod@ietf.org>.
Change controller:
The IESG <iesg@ietf.org>
15. Security Considerations 🔐
okay, this section is serious, so listen up 🚨 this document defines a language for writing and reading descriptions of management information. the language itself has no security impact on the Internet.
the same considerations apply as for the base NETCONF protocol (see [RFC4741], Section 9).
data modeled in YANG might contain sensitive information. RPCs or notifications defined in YANG might transfer sensitive information 😬
security issues come from how data modeled in YANG gets used. those issues shall be dealt with in documents describing the data models and in documents about the interfaces used to manipulate the data, e.g., the NETCONF documents.
data modeled in YANG depends on:
- the security of the transmission infrastructure used to send sensitive information.
- the security of applications that store or release such sensitive information.
- adequate authentication and access control mechanisms to restrict the usage of sensitive data.
YANG parsers need to be robust with respect to malformed documents. reading malformed documents from unknown or untrusted sources could let an attacker gain the privileges of the user running the YANG parser. in an extreme situation, the entire machine could be compromised 💀🚨
16. Contributors 🫶
the following people all contributed significantly to the initial YANG document:
- Andy Bierman (Brocade)
- Balazs Lengyel (Ericsson)
- David Partain (Ericsson)
- Juergen Schoenwaelder (Jacobs University Bremen)
- Phil Shafer (Juniper Networks)
17. Acknowledgements 💖
the editor wishes to thank the following individuals, who all provided helpful comments on various versions of this document: Mehmet Ersue, Washam Fan, Joel Halpern, Leif Johansson, Ladislav Lhotka, Gerhard Muenz, Tom Petch, Randy Presuhn, David Reid, and Bert Wijnen 🌸
18. References 📚
18.1. Normative References
- [ISO.10646] International Organization for Standardization, "Information Technology - Universal Multiple-Octet Coded Character Set (UCS)", ISO Standard 10646:2003, 2003.
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
- [RFC3023] Murata, M., St. Laurent, S., and D. Kohn, "XML Media Types", RFC 3023, January 2001.
- [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, November 2003.
- [RFC3688] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, January 2004.
- [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, January 2005.
- [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, October 2006.
- [RFC4741] Enns, R., "NETCONF Configuration Protocol", RFC 4741, December 2006.
- [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 5226, May 2008.
- [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, January 2008.
- [RFC5277] Chisholm, S. and H. Trevino, "NETCONF Event Notifications", RFC 5277, July 2008.
- [XML-NAMES] Hollander, D., Tobin, R., Thompson, H., Bray, T., and A. Layman, "Namespaces in XML 1.0 (Third Edition)", World Wide Web Consortium Recommendation REC-xml-names-20091208, December 2009,
<http://www.w3.org/TR/2009/REC-xml-names-20091208>. - [XPATH] Clark, J. and S. DeRose, "XML Path Language (XPath) Version 1.0", World Wide Web Consortium Recommendation REC-xpath-19991116, November 1999,
<http://www.w3.org/TR/1999/REC-xpath-19991116>. - [XSD-TYPES] Malhotra, A. and P. Biron, "XML Schema Part 2: Datatypes Second Edition", World Wide Web Consortium Recommendation REC-xmlschema-2-20041028, October 2004,
<http://www.w3.org/TR/2004/REC-xmlschema-2-20041028>.
18.2. Informative References ✨
okay bestie, this is the last stretch 🫶 these are the "good to know" sources. they're not required reading to implement YANG, but they're the receipts for where ideas like SMI, SMIng, the RFC Series rules, XPath, and XSLT come from 💅
[RFC2578] McCloghrie, K., Ed., Perkins, D., Ed., and J.
Schoenwaelder, Ed., "Structure of Management Information
Version 2 (SMIv2)", STD 58, RFC 2578, April 1999.
[RFC2579] McCloghrie, K., Ed., Perkins, D., Ed., and J.
Schoenwaelder, Ed., "Textual Conventions for SMIv2",
STD 58, RFC 2579, April 1999.
[RFC3780] Strauss, F. and J. Schoenwaelder, "SMIng - Next
Generation Structure of Management Information",
RFC 3780, May 2004.
[RFC4844] Daigle, L. and Internet Architecture Board, "The RFC
Series and RFC Editor", RFC 4844, July 2007.
[XPATH2.0] Berglund, A., Boag, S., Chamberlin, D., Fernandez, M.,
Kay, M., Robie, J., and J. Simeon, "XML Path Language
(XPath) 2.0", World Wide Web Consortium
Recommendation REC-xpath20-20070123, January 2007,
<http://www.w3.org/TR/2007/REC-xpath20-20070123>.
[XSLT] Clark, J., "XSL Transformations (XSLT) Version 1.0",
World Wide Web Consortium Recommendation REC-xslt-
19991116, November 1999,
<http://www.w3.org/TR/1999/REC-xslt-19991116>.
Author's Address 🎀
and that's the whole crew! the editor is Martin Bjorklund over at Tail-f Systems, so if you ever want to ask him something, his contact is right here 💌
Martin Bjorklund (editor)
Tail-f Systems
EMail: mbj@tail-f.com
ngl this doc is a whole main character moment 👑 we made it through all 28 parts 🌈 periodt 🦋
📦 where this page comes from
- ietf: rfc6020 2010-10
- 💖 girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms