RFC 7950

The YANG 1.1 Data Modeling Language

๐Ÿ“… published August 2016, from ietf

RFC 7950: The YANG 1.1 Data Modeling Language โœจ

Part 1 of 33, bestie ๐ŸŽ€ more parts are coming!

  • Document: RFC 7950
  • Organization: Internet Engineering Task Force (IETF)
  • Authors: M. Bjorklund, Ed., of Tail-f Systems
  • Category: Standards Track
  • Date: August 2016
  • ISSN: 2070-1721
The copy I was working from doesn't include any Obsoletes or Updates lines, so I'm not going to guess what this RFC obsoletes or updates. ๐Ÿ™ˆ

Abstract

YANG is a data modeling language that describes configuration data, state data, Remote Procedure Calls, and notifications for network management protocols. This doc walks through the syntax and semantics of version 1.1 of YANG. YANG 1.1 is a maintenance release that cleans up ambiguities and defects from the original spec, and there are a small number of backward incompatibilities from YANG version 1. The doc also spells out how YANG maps onto the Network Configuration Protocol (NETCONF). ๐Ÿ’…

Status of This Memo

This is an Internet Standards Track document. โœจ

This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community, it went through public review, and the Internet Engineering Steering Group (IESG) approved it for publication. If you want more on Internet Standards, check out Section 2 of RFC 7841. ๐Ÿฆ‹

For the current status, any errata, and how to send feedback, head to http://www.rfc-editor.org/info/rfc7950. ๐Ÿ’œ

Copyright (c) 2016 IETF Trust and the persons identified as the document authors. All rights 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 stand on the publication date. Please read these carefully, because they spell out your rights and restrictions for this document. Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Simplified BSD License. ๐Ÿ“œ

This document may include material from IETF Documents or IETF Contributions that were published or made publicly available before November 10, 2008. The people who control the copyright in some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. Without an adequate license from those copyright holders, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process. The exception is formatting it for publication as an RFC or translating it into languages other than English. ๐Ÿซถ

Table of Contents

   1. Introduction ....................................................9
      1.1. Summary of Changes from RFC 6020 ..........................10
   2. Key Words ......................................................12
   3. Terminology ....................................................12
      3.1. A Note on Examples ........................................16
   4. YANG Overview ..................................................16
      4.1. Functional Overview .......................................16
      4.2. Language Overview .........................................18
           4.2.1. Modules and Submodules .............................18
           4.2.2. Data Modeling Basics ...............................19
           4.2.3. Configuration and State Data .......................23
           4.2.4. Built-In Types .....................................24
           4.2.5. Derived Types (typedef) ............................25
           4.2.6. Reusable Node Groups (grouping) ....................25
           4.2.7. Choices ............................................27
           4.2.8. Extending Data Models (augment) ....................28
           4.2.9. Operation Definitions ..............................29
           4.2.10. Notification Definitions ..........................31
   5. Language Concepts ..............................................32
      5.1. Modules and Submodules ....................................32
           5.1.1. Import and Include by Revision .....................33
           5.1.2. Module Hierarchies .................................34
      5.2. File Layout ...............................................36
      5.3. XML Namespaces ............................................36
           5.3.1. YANG XML Namespace .................................36
      5.4. Resolving Grouping, Type, and Identity Names ..............37
      5.5. Nested Typedefs and Groupings .............................37
      5.6. Conformance ...............................................38
           5.6.1. Basic Behavior .....................................38
           5.6.2. Optional Features ..................................38
           5.6.3. Deviations .........................................39
           5.6.4. Announcing Conformance Information in NETCONF ......40
           5.6.5. Implementing a Module ..............................40
      5.7. Datastore Modification ....................................44
   6. YANG Syntax ....................................................44
      6.1. Lexical Tokenization ......................................45
           6.1.1. Comments ...........................................45
           6.1.2. Tokens .............................................45
           6.1.3. Quoting ............................................45
      6.2. Identifiers ...............................................47
           6.2.1. Identifiers and Their Namespaces ...................47
      6.3. Statements ................................................48
           6.3.1. Language Extensions ................................48
      6.4. XPath Evaluations .........................................49
           6.4.1. XPath Context ......................................50
      6.5. Schema Node Identifier ....................................54

okay the table of contents keeps going bestie ๐ŸŽ€ here's the rest of section 7 and everything after it ๐Ÿ’…

   7. YANG Statements ................................................55
      7.1. The "module" Statement ....................................55
           7.1.1. The module's Substatements .........................56
           7.1.2. The "yang-version" Statement .......................57
           7.1.3. The "namespace" Statement ..........................57
           7.1.4. The "prefix" Statement .............................57
           7.1.5. The "import" Statement .............................58
           7.1.6. The "include" Statement ............................59
           7.1.7. The "organization" Statement .......................60
           7.1.8. The "contact" Statement ............................60
           7.1.9. The "revision" Statement ...........................60
           7.1.10. Usage Example .....................................61
      7.2. The "submodule" Statement .................................62
           7.2.1. The submodule's Substatements ......................63
           7.2.2. The "belongs-to" Statement .........................63
           7.2.3. Usage Example ......................................64
      7.3. The "typedef" Statement ...................................65
           7.3.1. The typedef's Substatements ........................65
           7.3.2. The typedef's "type" Statement .....................65
           7.3.3. The "units" Statement ..............................65
           7.3.4. The typedef's "default" Statement ..................66
           7.3.5. Usage Example ......................................66
      7.4. The "type" Statement ......................................66
           7.4.1. The type's Substatements ...........................67
      7.5. The "container" Statement .................................67
           7.5.1. Containers with Presence ...........................67
           7.5.2. The container's Substatements ......................68
           7.5.3. The "must" Statement ...............................69
           7.5.4. The must's Substatements ...........................70
           7.5.5. The "presence" Statement ...........................71
           7.5.6. The container's Child Node Statements ..............71
           7.5.7. XML Encoding Rules .................................71
           7.5.8. NETCONF <edit-config> Operations ...................72
           7.5.9. Usage Example ......................................72
      7.6. The "leaf" Statement ......................................73
           7.6.1. The leaf's Default Value ...........................74
           7.6.2. The leaf's Substatements ...........................75
           7.6.3. The leaf's "type" Statement ........................75
           7.6.4. The leaf's "default" Statement .....................75
           7.6.5. The leaf's "mandatory" Statement ...................76
           7.6.6. XML Encoding Rules .................................76
           7.6.7. NETCONF <edit-config> Operations ...................76
           7.6.8. Usage Example ......................................77
      7.7. The "leaf-list" Statement .................................77
           7.7.1. Ordering ...........................................78
           7.7.2. The leaf-list's Default Values .....................79
           7.7.3. The leaf-list's Substatements ......................80
           7.7.4. The leaf-list's "default" Statement ................80
           7.7.5. The "min-elements" Statement .......................80
           7.7.6. The "max-elements" Statement .......................81
           7.7.7. The "ordered-by" Statement .........................81
           7.7.8. XML Encoding Rules .................................82
           7.7.9. NETCONF <edit-config> Operations ...................82
           7.7.10. Usage Example .....................................83
      7.8. The "list" Statement ......................................84
           7.8.1. The list's Substatements ...........................85
           7.8.2. The list's "key" Statement .........................85
           7.8.3. The list's "unique" Statement ......................86
           7.8.4. The list's Child Node Statements ...................87
           7.8.5. XML Encoding Rules .................................88
           7.8.6. NETCONF <edit-config> Operations ...................88
           7.8.7. Usage Example ......................................90
      7.9. The "choice" Statement ....................................93
           7.9.1. The choice's Substatements .........................94
           7.9.2. The choice's "case" Statement ......................94
           7.9.3. The choice's "default" Statement ...................96
           7.9.4. The choice's "mandatory" Statement .................98
           7.9.5. XML Encoding Rules .................................98
           7.9.6. Usage Example ......................................99
      7.10. The "anydata" Statement .................................100
           7.10.1. The anydata's Substatements ......................100
           7.10.2. XML Encoding Rules ...............................101
           7.10.3. NETCONF <edit-config> Operations .................101
           7.10.4. Usage Example ....................................101
      7.11. The "anyxml" Statement ..................................102
           7.11.1. The anyxml's Substatements .......................103
           7.11.2. XML Encoding Rules ...............................103
           7.11.3. NETCONF <edit-config> Operations .................103
           7.11.4. Usage Example ....................................104
      7.12. The "grouping" Statement ................................104
           7.12.1. The grouping's Substatements .....................105
           7.12.2. Usage Example ....................................105
      7.13. The "uses" Statement ....................................106
           7.13.1. The uses's Substatements .........................106
           7.13.2. The "refine" Statement ...........................106
           7.13.3. XML Encoding Rules ...............................107
           7.13.4. Usage Example ....................................107
      7.14. The "rpc" Statement .....................................108
           7.14.1. The rpc's Substatements ..........................109
           7.14.2. The "input" Statement ............................109
           7.14.3. The "output" Statement ...........................110
           7.14.4. NETCONF XML Encoding Rules .......................111
           7.14.5. Usage Example ....................................112

      7.15. The "action" Statement ..................................113
           7.15.1. The action's Substatements .......................114
           7.15.2. NETCONF XML Encoding Rules .......................114
           7.15.3. Usage Example ....................................115
      7.16. The "notification" Statement ............................116
           7.16.1. The notification's Substatements .................117
           7.16.2. NETCONF XML Encoding Rules .......................117
           7.16.3. Usage Example ....................................118
      7.17. The "augment" Statement .................................119
           7.17.1. The augment's Substatements ......................121
           7.17.2. XML Encoding Rules ...............................121
           7.17.3. Usage Example ....................................122
      7.18. The "identity" Statement ................................124
           7.18.1. The identity's Substatements .....................124
           7.18.2. The "base" Statement .............................124
           7.18.3. Usage Example ....................................125
      7.19. The "extension" Statement ...............................126
           7.19.1. The extension's Substatements ....................126
           7.19.2. The "argument" Statement .........................127
           7.19.3. Usage Example ....................................127
      7.20. Conformance-Related Statements ..........................128
           7.20.1. The "feature" Statement ..........................128
           7.20.2. The "if-feature" Statement .......................130
           7.20.3. The "deviation" Statement ........................131
      7.21. Common Statements .......................................134
           7.21.1. The "config" Statement ...........................134
           7.21.2. The "status" Statement ...........................135
           7.21.3. The "description" Statement ......................136
           7.21.4. The "reference" Statement ........................136
           7.21.5. The "when" Statement .............................136
   8. Constraints ...................................................138
      8.1. Constraints on Data ......................................138
      8.2. Configuration Data Modifications .........................139
      8.3. NETCONF Constraint Enforcement Model .....................139
           8.3.1. Payload Parsing ...................................139
           8.3.2. NETCONF <edit-config> Processing ..................140
           8.3.3. Validation ........................................141
   9. Built-In Types ................................................141
      9.1. Canonical Representation .................................141
      9.2. The Integer Built-In Types ...............................142
           9.2.1. Lexical Representation ............................142
           9.2.2. Canonical Form ....................................143
           9.2.3. Restrictions ......................................143
           9.2.4. The "range" Statement .............................143
           9.2.5. Usage Example .....................................144

1. Introduction โœจ

okay so YANG is a data modeling language ๐ŸŽ€ It was originally designed to model configuration and state data that gets manipulated by the Network Configuration Protocol (NETCONF), NETCONF Remote Procedure Calls, and NETCONF notifications [RFC6241]. literally the whole point, bestie ๐Ÿ’…

Since YANG version 1 came out [RFC6020], people have been using it, or proposing to use it, for other protocols too. Like RESTCONF [RESTCONF] and the Constrained Application Protocol (CoAP) Management Interface (CoMI) [CoMI] ๐Ÿฆ‹ Encodings other than XML have also been proposed, like JSON [RFC7951]. the way YANG is spreading is kinda iconic ngl ๐ŸŒˆ

This document describes the syntax and semantics of version 1.1 of the YANG language โœจ It also covers how a data model defined in a YANG module gets encoded in the Extensible Markup Language (XML) [XML], and how NETCONF operations are used to manipulate that data ๐Ÿงš Other protocols and encodings are possible, but they are out of scope for this specification. not me saying "out of scope" like it's a sad little plot twist ๐Ÿ˜ญ

In terms of developing YANG data models, [YANG-Guidelines] gives some guidelines and recommendations ๐ŸŒธ

Note that this document does not obsolete RFC 6020 [RFC6020]. ๐Ÿ’œ

1.1. Summary of Changes from RFC 6020 ๐ŸŽ€

Okay so this doc defines YANG version 1.1 ๐Ÿ’– It's a maintenance release of the YANG language that fixes ambiguities and defects in the original spec [RFC6020] โœจ

These changes are not backward compatible with YANG version 1 ๐Ÿ˜ญ

  • Changed the rules for how escaped characters work in double-quoted strings. This is a backward-incompatible change from YANG version 1. When you update a YANG version 1 module to 1.1 and it uses a character sequence that is now illegal, the string must be changed to match the new rules. See Section 6.1.3 for details. ๐Ÿ™ˆ
  • An unquoted string cannot contain any single or double quote characters. This is a backward-incompatible change from YANG version 1. When you update a YANG version 1 module to 1.1 and it uses those quote characters, the string must be changed to match the new rules. See Section 6.1.3 for details. ๐Ÿ’…
  • when and if-feature are now illegal on list keys. This is a backward-incompatible change from YANG version 1. When you update a YANG version 1 module to 1.1 and it uses these constructs, they must be removed to match the new rules. ๐Ÿ’€
  • Defined the legal characters in YANG modules. When you update a YANG version 1 module to 1.1, any characters that are now illegal must be removed. See Section 6 for details. ๐Ÿงš
  • Made noncharacters illegal in the built-in type "string". This change affects the runtime behavior of YANG-based protocols. ๐Ÿ”ฅ

Here are the other changes to YANG, which are also iconic โœจ

  • Changed the YANG version from "1" to "1.1".
  • Made the yang-version statement mandatory in YANG version "1.1".
  • Extended the if-feature syntax so it's a boolean expression over feature names. Very main character ๐Ÿ‘‘
  • Allowed if-feature in bit, enum, and identity.
  • Allowed if-feature in refine.
  • Allowed choice as a shorthand case statement (see Section 7.9.2).
  • Added a new substatement modifier to the pattern statement (see Section 9.4.6). ๐Ÿฆ‹
  • Allowed must in input, output, and notification.
  • Allowed require-instance in leafref.
  • Allowed description and reference in import and include.
  • Allowed imports of multiple revisions of a module.
  • Allowed augment to add conditionally mandatory nodes (see Section 7.17).
  • Added a set of new XML Path Language (XPath) functions in Section 10. ๐ŸŒˆ
  • Clarified the XPath context's tree in Section 6.4.1.
  • Defined the string value of an identityref in XPath expressions (see Section 9.10).
  • Clarified what unprefixed names mean in leafrefs in typedefs (see Sections 6.4.1 and 9.9.2).
  • Allowed identities to be derived from multiple base identities (see Sections 7.18 and 9.10).
  • Allowed enumerations and bits to be subtyped (see Sections 9.6 and 9.7).
  • Allowed leaf-lists to have default values (see Section 7.7.2).
  • Allowed non-unique values in non-configuration leaf-lists (see Section 7.7).
  • Used syntax for case-sensitive strings (as per [RFC7405]) in the grammar. ๐Ÿง 
  • Changed the module advertisement mechanism (see Section 5.6.4).
  • Changed the scoping rules for definitions in submodules. A submodule can now reference all definitions in all submodules that belong to the same module, without using the include statement. Bestie, this is so much cleaner โœจ
  • Added a new statement action, which is used to define operations tied to data nodes. ๐Ÿซถ
  • Allowed notifications to be tied to data nodes.
  • Added a new data definition statement anydata (see Section 7.10). It is RECOMMENDED to use this instead of anyxml when the data can be modeled in YANG. ๐Ÿ’œ
  • Allowed types empty and leafref in unions.
  • Allowed type empty in a key.
  • Removed the restriction that identifiers could not start with the characters "xml". Finally ๐Ÿ™Œ

The following changes were made to the NETCONF mapping ๐ŸŒธ

  • A server advertises support for YANG 1.1 modules by using ietf-yang-library [RFC7895] instead of listing them as capabilities in the <hello> message.

2. Key Words โœจ

Okay, the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be read the way BCP 14 [RFC2119] describes them. These are the official vibes ๐Ÿ’…


3. Terminology ๐Ÿ“–

Here are the terms used in this document, bestie ๐Ÿงš

  • action: An operation defined for a node in the data tree.
  • anydata: A data node that can contain an unknown set of nodes that can be modeled by YANG, except anyxml.
  • 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 a derived type was derived from. This may be a built-in type or another derived type.
  • built-in type: A YANG data type defined in the YANG language, like uint32 or string.
  • choice: A schema node where only one of several identified alternatives is valid.
  • client: An entity that can access YANG-defined data on a server, over some network management protocol.
  • conformance: A measure of how accurately a server 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, just 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", "anydata", 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, anydata, and anyxml.
  • data tree: An instantiated tree of any data modeled with YANG, e.g., configuration data, state data, combined configuration and state data, RPC or action input, RPC or action output, or notification. ๐ŸŒณ
  • derived type: A type derived from a built-in type (such as uint32) or another derived type.
  • 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 servers that support that feature. ๐Ÿฆ‹
  • grouping: A reusable set of schema nodes, which may be used locally in the module and by other modules that import from it. The "grouping" statement is not a data definition statement, so it doesn't define any nodes in the schema tree.
  • identifier: A string used to identify different kinds of YANG items by name.
  • identity: A globally unique, abstract, and untyped name.
  • instance identifier: A mechanism for identifying a particular node in a data tree.
  • interior node: A node within a hierarchy that is not a leaf node.
  • 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 it 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, just a set of child nodes.
  • mandatory node: A mandatory node is one of:
    • A leaf, choice, anydata, or anyxml node with a "mandatory" statement with the value "true".
    • A list or leaf-list node with a "min-elements" statement with a value greater than zero.
    • A container node without a "presence" statement that has at least one mandatory node as a child.
  • module: A YANG module defines hierarchies of schema nodes. With its own definitions plus the definitions it imports or includes from elsewhere, a module is self-contained and "compilable". ๐ŸŽ€
  • non-presence container: A container that has no meaning of its own. It only exists to contain child nodes.
  • presence container: A container where the presence of the container itself carries some meaning.
  • RPC: A Remote Procedure Call.
  • RPC operation: A specific Remote Procedure Call.
  • schema node: A node in the schema tree. One of action, container, leaf, leaf-list, list, choice, case, rpc, input, output, notification, anydata, 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.
  • server: An entity that provides access to YANG-defined data to a client, over some network management protocol.
  • server deviation: A failure of the server to implement a module faithfully. Yikes ๐Ÿ˜ฌ
  • submodule: A partial module definition that contributes derived types, groupings, data nodes, RPCs, actions, and notifications to a module. A YANG module can be built from a number of submodules.
  • top-level data node: A data node with no other data node between it and a "module" or "submodule" statement.
  • uses: The "uses" statement instantiates the set of schema nodes defined in a "grouping" statement. The instantiated nodes may be refined and augmented to fit any specific needs. โœจ
  • value space: For a data type, the set of values the data type permits. For a leaf or leaf-list instance, the value space of its data type.

These terms are defined in [RFC6241], so go check them out ๐ŸŒธ

  • configuration data
  • configuration datastore
  • datastore
  • state data

When modeled with YANG, a datastore is realized as an instantiated data tree. ๐ŸŒˆ

When modeled with YANG, a configuration datastore is realized as an instantiated data tree with configuration data. ๐Ÿ’œ

3.1. A Note on Examples ๐Ÿ™ˆ

Throughout this document there are a lot of YANG statement examples. They're there to illustrate certain features. They are not supposed to be complete, valid YANG modules, so don't copy-paste and expect them to compile ๐Ÿ’€


4. YANG Overview ๐Ÿงš

This non-normative section gives a high-level overview of YANG for first-time readers. Welcome to the group chat, babe ๐Ÿซถ

4.1. Functional Overview โœจ

okay so YANG was originally built to model data for the NETCONF protocol ๐ŸŽ€ A YANG module defines hierarchies of data that NETCONF-based operations can use, including configuration, state data, RPCs, and notifications. That means you can get a complete description of all the data sent between a NETCONF client and server ๐Ÿ’… YANG can be used with protocols other than NETCONF too, but that's out of scope for this spec, so we're not going there today ๐ŸŒธ

YANG models the hierarchical organization of data as a tree ๐ŸŒณ Every node has a name, plus either a value or a set of child nodes. YANG gives super clear and concise descriptions of the nodes, and how the nodes interact with each other. Iconic ๐Ÿ‘‘

YANG structures data models into modules and submodules ๐Ÿ“ฆ A module can import definitions from other external modules and can include definitions from submodules. The hierarchy can be augmented, so one module can add data nodes to the hierarchy defined in another module ๐Ÿฆ‹ That augmentation can even be conditional, so new nodes only show up if certain conditions are met. Main character energy โœจ

YANG data models can describe constraints that get enforced on the data ๐Ÿ”’ They can restrict the presence or value of nodes based on the presence or value of other nodes in the hierarchy. These constraints are enforceable by either the client or the server, so both of them can be the bouncer ๐Ÿ’€

YANG comes with a set of built-in types, and it has a type mechanism for defining more ๐Ÿงš Derived types can restrict their base type's valid values using things like range or pattern restrictions, which clients or servers can enforce. They can also define usage conventions, like a string-based type that holds a hostname. Lowkey so helpful ๐Ÿ“

YANG lets you define reusable groupings of nodes ๐Ÿงถ Using a grouping can refine or augment those nodes, so you can tailor them to your exact needs. Derived types and groupings can be defined in one module and then used in that same module or in another module that imports it ๐Ÿซถ

YANG's data hierarchy has constructs for defining lists, where list entries are identified by keys that tell them apart from each other ๐Ÿ”‘ Lists can be sorted by user or automatically sorted by the system. For user-sorted lists, there are operations for manipulating the order of the entries ๐Ÿ’œ

YANG modules can be translated into an equivalent XML syntax called YANG Independent Notation (YIN) (Section 13) ๐Ÿฉท That lets applications using XML parsers and Extensible Stylesheet Language Transformations (XSLT) scripts operate on the models. The conversion from YANG to YIN is semantically lossless, so YIN content can be round-tripped back into YANG. Periodt ๐ŸŽ€

YANG is extensible too ๐ŸŒˆ Standards bodies, vendors, and individuals can all define extensions. The statement syntax lets these extensions coexist with standard YANG statements in a natural way, and extensions still stand out enough that the reader will notice them ๐Ÿ‘€

YANG resists the tendency to solve every possible problem ๐Ÿ™…โ€โ™€๏ธ It limits the problem space so it can express data models for network management protocols like NETCONF, not arbitrary XML documents or arbitrary data models. Focus, bestie ๐Ÿ’–

To the extent 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 [RFC6643]. However, YANG is not concerned with reverse translation from YANG to SMIv2, so that's a no from YANG ๐Ÿ™ˆ

4.2. Language Overview ๐ŸŒธ

This section introduces some important constructs used in YANG that will aid in the understanding of the language specifics in later sections ๐Ÿ“–

4.2.1. Modules and Submodules ๐Ÿ“ฆ

YANG data models are defined in modules. A module contains a collection of related definitions ๐Ÿ’…

A module contains three types of statements: module header statements, "revision" statements, and definition statements. The module header statements describe the module and give information about the module itself, the "revision" statements give information about the module's history, and the definition statements are the body of the module where the data model is defined ๐ŸŽ€

A server may implement a number of modules, which allows multiple views of the same data or multiple views of disjoint subsections of the server's data ๐Ÿชž Alternatively, the server may implement only one module that defines all available data ๐Ÿซง

A module may have portions of its definitions separated into submodules, based on what the module designer needs. The external view is still that of a single module, no matter how many submodules it has or how big they are ๐Ÿงฉ

The "import" statement lets a module or submodule reference definitions in other modules ๐Ÿ”—

The "include" statement is used in a module to identify each submodule that belongs to it ๐Ÿซถ

4.2.2. Data Modeling Basics ๐Ÿงธ

YANG defines four main types of data nodes for data modeling โœจ In each of the following subsections, the examples show the YANG syntax as well as a corresponding XML encoding. The syntax of YANG statements is defined in Section 6.3 ๐Ÿ’œ

4.2.2.1. Leaf Nodes ๐Ÿƒ

A leaf instance holds simple data like an integer or a string. It has exactly one value of a particular type and no child nodes ๐ŸŒท

YANG Example:

leaf host-name {
  type string;
  description
    "Hostname for this system.";
}

XML Encoding 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 defines a sequence of values of a particular type ๐Ÿ“œ

YANG Example:

leaf-list domain-search {
  type string;
  description
    "List of domain names to search.";
}

XML Encoding 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 is used to group related nodes in a subtree. A container has only child nodes and no value. A container may contain any number of child nodes of any type (leafs, lists, containers, leaf-lists, actions, and notifications) ๐Ÿ—‚๏ธ

YANG Example:

container system {
  container login {
    leaf message {
      type string;
      description
        "Message given at start of login session.";
    }
  }
}

XML Encoding 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 is like a container and is uniquely identified by the values of its key leafs if it has any key leafs defined. A list can define multiple key leafs and may contain any number of child nodes of any type (including leafs, lists, containers, etc.) ๐Ÿ”‘

YANG Example:

list user {
  key "name";
  leaf name {
    type string;
  }
  leaf full-name {
    type string;
  }
  leaf class {
    type string;
  }
}

XML Encoding 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 ๐ŸŒŸ

These statements are combined to define the module ๐Ÿง

// Contents of "example-system.yang"
module example-system {
  yang-version 1.1;
  namespace "urn:example:system";
  prefix "sys";

  organization "Example Inc.";
  contact "joe@example.com";
  description
    "The module for entities implementing the Example 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. Configuration and State Data โš™๏ธ

YANG can model state data as well as configuration data, based on the "config" statement ๐Ÿง  When a node is tagged with "config false", its subhierarchy is flagged as state data. If it is tagged with "config true", its subhierarchy is flagged as configuration data. Parent containers, lists, and key leafs are reported too, which gives the context for the state data ๐Ÿซง

In this example, two leafs are defined for each interface: a configured speed and an observed speed ๐ŸŽ๏ธ

list interface {
  key "name";
  config true;

  leaf name {
    type string;
  }
  leaf speed {
    type enumeration {
      enum 10m;
      enum 100m;
      enum auto;
    }
  }
  leaf observed-speed {
    type uint32;
    config false;
  }
}

The "config" statement is covered in Section 7.21.1 ๐Ÿฅน

4.2.4. Built-In Types โœจ

okay so YANG comes with a squad of built-in types ๐Ÿ’… they're kinda like the types in lots of programming languages, but they've got some tweaks because network management has its own special needs ๐ŸŒธ here's the whole 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         | One of an enumerated set of strings |
| identityref         | A reference to an abstract identity |
| instance-identifier | A reference to 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              | A character 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) ๐Ÿฆ‹

bestie, you can make your own derived types from base types using the "typedef" statement ๐Ÿ’– a base type can be a built-in type or another derived type, so you can stack them into a whole hierarchy โœจ

you can pass a derived type as the argument for the "type" statement, and it's giving very reusable energy ๐Ÿซถ

YANG Example:

typedef percent {
  type uint8 {
    range "0 .. 100";
  }
}

leaf completed {
  type percent;
}

XML Encoding Example:

<completed>20</completed>

The "typedef" statement is covered in Section 7.3. ๐Ÿ’œ

4.2.6. Reusable Node Groups (grouping) ๐ŸŒˆ

this one is so satisfying ๐Ÿ˜ญ you can bundle nodes into a reusable collection using the "grouping" statement, and then drop that collection in wherever you want with the "uses" statement ๐Ÿ“

YANG Example:

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;
  }
}

XML Encoding Example:

<peer>
  <destination>
    <address>2001:db8::2</address>
    <port>830</port>
  </destination>
</peer>

ngl the refining part is my favourite ๐Ÿงš when you use a grouping, you can override certain statements so each spot gets its own description. In this example, the description gets refined:

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.12. ๐Ÿ‘‘

4.2.7. Choices ๐ŸŒธ

YANG lets your data model keep incompatible nodes apart in distinct choices, using the "choice" and "case" statements ๐Ÿ™ˆ the "choice" holds a bunch of "case" statements, and each case defines a set of schema nodes that can't show up together. A case can hold multiple nodes, but each node can only live in one "case" under a given "choice" โœจ

the choice and case nodes only show up in the schema tree, not the data tree. They're just extra layers of hierarchy that you don't need beyond the conceptual schema ๐Ÿ’… a case counts as present when one or more of the nodes inside it are present ๐Ÿฆ‹

only one of a choice's cases can be valid at any moment, so when a node from one case gets created in the data tree, every node from all the other cases is implicitly deleted ๐Ÿ˜ญ the server takes care of enforcing this, so incompatible stuff never ends up sitting in the configuration. iconic ๐Ÿ”ฅ

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;
        }
      }
    }
  }
}

XML Encoding 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, and that includes the current module (plus its submodules) and an external module too ๐Ÿ’– this is super useful for vendors, who can add vendor-specific parameters to standard data models in an interoperable way. main character energy ๐Ÿ‘‘

the "augment" statement says where in the data model hierarchy the new nodes go, and the "when" statement says under what conditions they're valid ๐ŸŒˆ

when a server implements a module that has an "augment" statement, that means the server's version of the augmented module has those extra nodes too ๐Ÿซถ

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 one module augments another, the XML elements it adds to the encoding live in the namespace of the augmenting module ๐Ÿ’œ so if the augmentation above lived in a module with prefix "other", the XML would look like this:

XML Encoding 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.17. โœจ

4.2.9. Operation Definitions ๐ŸŽ€

YANG lets you define operations ๐Ÿ’… each operation's name, input parameters and output parameters are modeled with YANG data definition statements. Operations sitting at the top level of a module use the "rpc" statement. Operations can also be attached to a container or list data node, and those use the "action" statement ๐Ÿฆ‹

YANG Example for an operation at the top level:

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://example.com/system">
    <image-name>example-fw-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://example.com/system">
    The image example-fw-2.3 is being installed.
  </status>
</rpc-reply>

YANG Example for an operation tied to a list data node:

list interface {
  key "name";

  leaf name {
    type string;
  }

  action ping {
    input {
      leaf destination {
        type inet:ip-address;
      }
    }
    output {
      leaf packet-loss {
        type uint8;
      }
    }
  }
}

NETCONF XML Example:

<rpc message-id="102"
     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <action xmlns="urn:ietf:params:xml:ns:yang:1">
    <interface xmlns="http://example.com/system">
      <name>eth1</name>
      <ping>
        <destination>192.0.2.1</destination>
      </ping>
    </interface>
  </action>
</rpc>

<rpc-reply message-id="102"
           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
           xmlns:sys="http://example.com/system">
  <sys:packet-loss>60</sys:packet-loss>
</rpc-reply>

The "rpc" statement is covered in Section 7.14, and the "action" statement is covered in Section 7.15. ๐Ÿ’–

4.2.10. Notification Definitions ๐ŸŒธ

YANG also lets you define notifications ๐ŸŒˆ you use YANG data definition statements to model what goes in the notification content ๐Ÿ“

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="urn:example: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.16. ๐Ÿงš


5. Language Concepts ๐Ÿ’œ

Okay bestie, let's get into it โœจ

5.1. Modules and Submodules ๐Ÿ’–

5.1. Modules and Submodules

The module is the base unit of definition in YANG ๐ŸŽ€ A module defines a single data model, and it can also augment an existing data model with extra nodes. Lowkey the main building block, no cap.

Submodules are partial modules that contribute definitions to a module ๐Ÿฆ‹ A module can include any number of submodules, but each submodule may belong to only one module. Think of it like one bestie per friend group, you know? ๐Ÿซถ

Developers of YANG modules and submodules are RECOMMENDED to choose names for their modules that will have a low probability of colliding with standard or other enterprise modules, e.g., by using the enterprise or organization name as a prefix for the module name. Within a server, all module names MUST be unique ๐Ÿ’… So pick a name that's giving originality, because two modules with the same name on one server is a whole mess.

A module uses the "include" statement to list all its submodules ๐ŸŒธ A module, or submodule belonging to that module, can reference definitions in the module and all submodules included by the module.

A module or submodule uses the "import" statement to reference external modules โœจ Statements in the module or submodule can reference definitions in the external module using the prefix specified in the "import" statement.

For backward compatibility with YANG version 1, a submodule MAY use the "include" statement to reference other submodules within its module, but this is not necessary in YANG version 1.1 ๐Ÿงš A submodule can reference any definition in the module it belongs to and in all submodules included by the module. A submodule MUST NOT include different revisions of other submodules than the revisions that its module includes. Like, don't go mixing up versions, that's just chaotic ๐Ÿ’€

A module or submodule MUST NOT include submodules from other modules, and a submodule MUST NOT import its own module ๐Ÿ™ˆ Stay in your lane, babe.

The "import" and "include" statements are used to make definitions available from other modules:

  • For a module or submodule to reference definitions in an external module, the external module MUST be imported. ๐Ÿ“
  • A module MUST include all its submodules. ๐ŸŒˆ
  • A module, or submodule belonging to that module, MAY reference definitions in the module and all submodules included by the module. ๐Ÿฆ‹

There MUST NOT be any circular chains of imports ๐Ÿ”ฅ For example, if module "a" imports module "b", then "b" cannot import "a". Hard no, that's a loop and nobody wants a loop.

When a definition in an external module is referenced, a locally defined prefix MUST be used, followed by a colon (":") and then the external identifier. References to definitions in the local module MAY use the prefix notation. Since built-in data types do not belong to any module and have no prefix, references to built-in data types (e.g., int32) cannot use the prefix notation. The syntax for a reference to a definition is formally defined by the rule "identifier-ref" in Section 14 ๐Ÿ“š iykyk, it's the rule you'll want to look up.

5.1.1. Import and Include by Revision ๐ŸŒท

Published modules evolve independently over time, which is so relatable honestly ๐Ÿ˜ญ To allow for this evolution, modules can be imported using specific revisions. Initially, a module imports the revisions of other modules that are current when the module is written. As future revisions of the imported modules are published, the importing module is unaffected and its contents are unchanged. When the author of the module is prepared to move to the most recently published revision of an imported module, the module is republished with an updated "import" statement. By republishing with the new revision, the authors explicitly indicate their acceptance of any changes in the imported module. Bestie, that's the consent moment ๐Ÿ’œ

For submodules, the issue is related but simpler ๐ŸŽ€ A module or submodule that includes submodules may specify the revision of the included submodules. If a submodule changes, any module or submodule that includes it by revision needs to be updated to reference the new revision.

For example, module "b" imports module "a".

  module a {
    yang-version 1.1;
    namespace "urn:example:a";
    prefix "a";

    revision 2008-01-01 { ... }
    grouping a {
      leaf eh { .... }
    }
  }

  module b {
    yang-version 1.1;
    namespace "urn:example:b";
    prefix "b";

    import a {
      prefix "p";
      revision-date 2008-01-01;
    }

    container bee {
      uses p:a;
    }
  }

When the author of "a" publishes a new revision, the changes may not be acceptable to the author of "b" ๐Ÿ˜ฌ If the new revision is acceptable, the author of "b" can republish with an updated revision in the "import" statement. Totally their call, no pressure.

If a module is not imported with a specific revision, it is undefined which revision is used. Yikes, so be specific or it's giving chaos ๐Ÿ’€

5.1.2. Module Hierarchies ๐Ÿงš

YANG allows modeling of data in multiple hierarchies, where data may have more than one top-level node. Each top-level data node in a module defines a separate hierarchy. Models that have multiple top-level nodes are sometimes convenient and are supported by YANG โœจ

5.1.2.1. NETCONF XML Encoding ๐Ÿฆ‹

NETCONF is capable of carrying any XML content as the payload in the <config> and <data> elements. The top-level nodes of YANG modules are encoded as child elements, in any order, within these elements. This encapsulation guarantees that the corresponding NETCONF messages are always well-formed XML documents ๐Ÿซถ

For example, an instance of:

  module example-config {
    yang-version 1.1;
    namespace "urn:example:config";
    prefix "co";

    container system { ... }
    container routing { ... }
  }

could be encoded in NETCONF as:

  <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="urn:example:config">
          <!-- system data here -->
        </system>
        <routing xmlns="urn:example:config">
          <!-- routing data here -->
        </routing>
      </config>
    </edit-config>
  </rpc>

5.2. File Layout ๐Ÿ—‚๏ธ

YANG modules and submodules are typically stored in files, one "module" or "submodule" statement per file. The name of the file SHOULD be of the form:

  module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' )

"module-or-submodule-name" is the name of the module or submodule, and the optional "revision-date" is the latest revision of the module or submodule, as defined by the "revision" statement (Section 7.1.9). Like, it's basically a naming pattern, and it's giving tidy ๐ŸŒธ

The file extension ".yang" denotes that the contents of the file are written with YANG syntax (Section 6), and ".yin" denotes that the contents of the file are written with YIN syntax (Section 13) ๐Ÿ’œ

YANG parsers can find imported modules and included submodules via this convention ๐Ÿ”

5.3. XML Namespaces ๐ŸŽ€

All YANG definitions are specified within a module. Each module is bound to a distinct XML namespace [XML-NAMES], which is a globally unique URI [RFC3986]. A NETCONF client or server uses the namespace during XML encoding of data ๐Ÿฆ‹

XML namespaces for modules published in RFC streams [RFC4844] MUST be assigned by IANA; see Section 14 in [RFC6020]. Big deal, IANA handles these, so don't try to DIY it ๐Ÿ’…

XML namespaces for private modules are assigned by the organization owning the module without a central registry. Namespace URIs MUST be chosen so they cannot collide with standard or other enterprise namespaces -- for example, by using the enterprise or organization name in the namespace ๐ŸŒˆ Protect your namespace like it's your main character era.

The "namespace" statement is covered in Section 7.1.3 โœจ

5.3.1. YANG XML Namespace ๐ŸŒธ

YANG defines an XML namespace for NETCONF <edit-config> operations, <error-info> content, and the <action> element. The name of this namespace is "urn:ietf:params:xml:ns:yang:1" ๐Ÿงš

5.4. Resolving Grouping, Type, and Identity Names ๐Ÿ”ฎ

Grouping, type, and identity names are resolved in the context in which they are defined, rather than the context in which they are used. Users of groupings, typedefs, and identities are not required to import modules or include submodules to satisfy all references made by the original definition. This behaves like static scoping in a conventional programming language, so it's lowkey very neat ๐Ÿซถ

For example, if a module defines a grouping in which a type is referenced, when the grouping is used in a second module, the type is resolved in the context of the original module, not the second module. There is no ambiguity if both modules define the type ๐Ÿ’–

5.5. Nested Typedefs and Groupings ๐Ÿ“

Typedefs and groupings may appear nested under many YANG statements, allowing these to be lexically scoped by the statement hierarchy under which they appear. This allows types and groupings to be defined near where they are used, rather than placing them at the top level of the hierarchy. The close proximity increases readability, and I am here for it ๐Ÿ‘‘

Scoping also allows types to be defined without concern for naming conflicts between types in different submodules. Type names can be specified without adding leading strings designed to prevent name collisions within large modules ๐Ÿ’…

Finally, scoping allows the module author to keep types and groupings private to their module or submodule, preventing their reuse. Since only top-level types and groupings (i.e., those appearing as substatements to a "module" or "submodule" statement) can be used outside the module or submodule, the developer has more control over what pieces of their module are presented to the outside world, supporting the need to hide internal information and maintaining a boundary between what is shared with the outside world and what is kept private. Privacy era, bestie ๐Ÿ™ˆ

Scoped definitions MUST NOT shadow definitions at a higher scope. A type or grouping cannot be defined if a higher level in the statement hierarchy has a definition with a matching identifier. No copying the name of a bigger scope, that's a no-go ๐Ÿšซ

A reference to an unprefixed type or grouping, or one that uses the prefix of the current module, is resolved by locating the matching "typedef" or "grouping" statement among the immediate substatements of each ancestor statement ๐Ÿ”

5.6. Conformance ๐ŸŽ€

Conformance to a model is a measure of how accurately a server follows the model. Generally speaking, servers are responsible for implementing the model faithfully, allowing applications to treat servers that implement the model identically. Deviations from the model can reduce the utility of the model and increase the fragility of applications that use it ๐Ÿ˜ฌ

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 will consider each of these in sequence ๐ŸŒŸ

5.6.1. Basic Behavior ๐Ÿฆ‹

The model defines a contract between a YANG-based client and server; this contract allows both parties to have faith that the other knows the syntax and semantics behind the modeled data. The strength of YANG lies in the strength of this contract ๐Ÿ’œ

5.6.2. Optional Features โœจ

okay so modelers love making some parts of a model conditional ๐ŸŽ€ and the server is the one who decides whether those parts actually apply to it. The server controls that, not the model. Obsessed with this, honestly.

For example, a syslog data model might let you save logs locally, but the modeler knows that only works if the server has local storage. If there's no local storage, an application should not tell the server to save logs. Literally don't do it ๐Ÿ™ˆ

YANG handles this with a construct called "feature" ๐Ÿฆ‹ Features let the modeler make parts of the module conditional in a way the server controls. The model can describe things that aren't on every server. Because the features live inside the model definition, everyone gets a consistent view, and applications can check which features are supported and adjust their behavior to match the server. Iconic. ๐Ÿ‘‘

A module can declare any number of features, each identified by a simple string, and it can make parts of itself optional based on those features. If the server supports a feature, the matching parts of the module are valid for that server. If the server 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 are marked with the if-feature statement.

Further details are available in Section 7.20.1.


5.6.3. Deviations

In a perfect world, every server would implement the model exactly as defined, and deviations wouldn't be allowed ๐Ÿฅบ But in the real world, servers often can't or weren't designed to implement the model exactly as written. So for YANG-based automation to handle these server deviations, there has to be a way for servers to tell applications the specifics of their deviations ๐ŸŒธ

For example, a BGP module might allow any number of BGP peers, but one particular server only supports 16. Any application that tries to configure the 17th peer will get an error. An error does tell the application it can't add another peer, but it's way better if the application already knows about the limit, so it can stop the user before they start down a path that can't work ๐Ÿ’€โœจ

Server deviations are declared with the deviation statement. It takes a string argument that identifies a node in the schema tree. The contents of the statement describe how the server implementation deviates from the contract defined in the module.

Further details are available in Section 7.20.3.


5.6.4. Announcing Conformance Information in NETCONF

This document defines the following mechanism for announcing conformance information. Other mechanisms may be defined by future specifications ๐Ÿงš

A NETCONF server MUST announce the modules it implements (see Section 5.6.5) by implementing the YANG module "ietf-yang-library" defined in [RFC7895] and listing all implemented modules in the "/modules-state/module" list.

The server also MUST advertise the following capability in the <hello> message (line breaks and whitespace are only used for formatting):

  urn:ietf:params:netconf:capability:yang-library:1.0?
    revision=<date>&module-set-id=<id>

The parameter "revision" has the same value as the revision date of the "ietf-yang-library" module implemented by the server. This parameter MUST be present.

The parameter "module-set-id" has the same value as the leaf "/modules-state/module-set-id" from "ietf-yang-library". This parameter MUST be present.

With this mechanism, a client can cache the supported modules for a server and only update that cache when the "module-set-id" value in the <hello> message changes. Caching glow-up unlocked ๐ŸŒˆ


5.6.5. Implementing a Module

A server implements a module if it implements that module's data nodes, RPCs, actions, notifications, and deviations ๐Ÿ“

A server MUST NOT implement more than one revision of a module.

If a server implements a module A that imports a module B, and A uses any node from B in an "augment" or "path" statement that the server supports, then the server MUST implement a revision of module B that has those nodes defined. This applies whether or not module B is imported by revision.

If a server implements a module A that imports a module C without specifying C's revision date, and the server does not implement C (for example, if C only defines some typedefs), the server MUST list module C in the "/modules-state/module" list from "ietf-yang-library" [RFC7895], and it MUST set the leaf "conformance-type" to "import" for this module.

If a server lists a module C in the "/modules-state/module" list from "ietf-yang-library", and there are other listed modules Ms that import C without specifying C's revision date, the server MUST use the definitions from the most recent revision of C that is listed for modules Ms.

The reason for these rules is that clients need to be able to know the exact data model structure and types of every leaf and leaf-list implemented on a server. It's all about clients knowing the vibe of the data ๐Ÿ”ฅ

For example, with these modules:

  module a {
    yang-version 1.1;
    namespace "urn:example:a";
    prefix "a";

    import b {
      revision-date 2015-01-01;
    }
    import c;

    revision 2015-01-01;

    feature foo;

    augment "/b:x" {
      if-feature foo;

      leaf y {
        type b:myenum;
      }
    }

    container a {
      leaf x {
        type c:bar;
      }
    }
  }
  module b {
    yang-version 1.1;
    namespace "urn:example:b";
    prefix "b";

    revision 2015-01-01;

    typedef myenum {
      type enumeration {
        enum zero;
      }
    }

    container x {
    }
  }

  module b {
    yang-version 1.1;
    namespace "urn:example:b";
    prefix "b";

    revision 2015-04-04;
    revision 2015-01-01;

    typedef myenum {
      type enumeration {
        enum zero; // added in 2015-01-01
        enum one;  // added in 2015-04-04
      }
    }

    container x {  // added in 2015-01-01
      container y; // added in 2015-04-04
    }
  }

  module c {
    yang-version 1.1;
    namespace "urn:example:c";
    prefix "c";

    revision 2015-02-02;

    typedef bar {
      ...
    }
  }
  module c {
    yang-version 1.1;
    namespace "urn:example:c";
    prefix "c";

    revision 2015-03-03;
    revision 2015-02-02;

    typedef bar {
      ...
    }
  }

A server that implements revision "2015-01-01" of module "a" and supports feature "foo" can implement revision "2015-01-01" or "2015-04-04" of module "b". Since "b" was imported by revision, the type of leaf "/b:x/a:y" is the same no matter which revision of "b" the server implements. ๐ŸŽ€

A server that implements module "a" but does not support feature "foo" does not have to implement module "b".

A server that implements revision "2015-01-01" of module "a" picks any revision of module "c" and lists it in the "/modules-state/module" list from "ietf-yang-library".

The following XML encoding example shows valid data for the "/modules-state/module" list for a server that implements module "a":

  <modules-state
      xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
    <module-set-id>ee1ecb017370cafd</module-set-id>
    <module>
      <name>a</name>
      <revision>2015-01-01</revision>
      <namespace>urn:example:a</namespace>
      <feature>foo</feature>
      <conformance-type>implement</conformance-type>
    </module>
    <module>
      <name>b</name>
      <revision>2015-04-04</revision>
      <namespace>urn:example:b</namespace>
      <conformance-type>implement</conformance-type>
    </module>
    <module>
      <name>c</name>
      <revision>2015-02-02</revision>
      <namespace>urn:example:c</namespace>
      <conformance-type>import</conformance-type>
    </module>
  </modules-state>

5.7. Datastore Modification

Data models may let the server change the configuration datastore in ways that weren't explicitly asked for through network management protocol messages ๐Ÿ‘€ For example, a data model might define leafs that get system-generated values when the client doesn't supply one. A formal mechanism for specifying when these changes are allowed is out of scope for this specification.

6. YANG Syntax ๐Ÿ’–

The YANG syntax looks a lot like SMIng [RFC3780] and programming languages like C and C++. This C-like syntax was picked on purpose for readability, because YANG puts the time and effort of model readers ahead of the time and effort of module writers and YANG tool-chain developers. This section introduces the YANG syntax.

Legal characters in YANG modules are the Unicode and ISO/IEC 10646 [ISO.10646] characters, including tab, carriage return, and line feed. Everything else in the C0 control characters, the surrogate blocks, and the noncharacters is excluded. The character syntax is formally defined by the rule "yang-char" in Section 14.

YANG modules and submodules are stored in files using the UTF-8 [RFC3629] character encoding.

Lines in a YANG module end with a carriage return-line feed combination or with a line feed alone. A carriage return that is not followed by a line feed may only appear inside a quoted string (Section 6.1.3). Carriage returns and line feeds that appear inside quoted strings become part of the value of the string without modification. The value of a multi-line quoted string contains the same form of line ends as those lines of the YANG module.

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 simple and powerful at the same time. The simplicity keeps parsers easy to implement, and 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 starts with "/*" and ends with the nearest following "*/".

Inside a quoted string (Section 6.1.3), these character pairs are never interpreted as the start or end of a comment.

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 a colon (":"), followed by a language extension keyword. Keywords are case sensitive. See Section 6.2 for a formal definition of identifiers. ๐Ÿ’…

6.1.3. Quoting ๐ŸŽ€

okay so an unquoted string is any run of characters with no space, tab, carriage return, or line feed in it. It also can't have a single or double quote, a semicolon (";"), braces ("{" or "}"), or comment sequences ("//", "/", or "/") ๐Ÿ’…

ngl this means any keyword can legally show up as an unquoted string ๐Ÿงš

inside an unquoted string, every single character is preserved ๐Ÿซถ so the backslash has no special meaning there at all. ๐Ÿ™ˆ

okay now double-quoted strings get a little spicy โœจ if there's a line break followed by spaces or tabs that are just used to indent the text to match the layout of the YANG file, that leading whitespace gets stripped off. It gets stripped up to and including the column of the starting double quote, or up to the first non-whitespace character, whichever comes first. And any tab on a following line that needs checking gets converted into 8 space characters first. ๐ŸŒธ

if a double-quoted string has spaces or tabs right before a line break, that trailing whitespace gets stripped too. ๐Ÿฆ‹

single-quoted strings (the ones inside ' ') are the chill ones: they preserve every character inside them. the catch is that a single quote can never show up in one, not even if you put a backslash in front of it ๐Ÿ’€

now the backslash in double-quoted strings is the main character ๐Ÿ‘‘ it introduces a special character, and what it does depends on the character right after it:

\n      newline
\t      a tab character
\"      a double quote
\\      a single backslash

the backslash MUST NOT be followed by any other character. no exceptions bestie ๐Ÿšซ

concatenation is a whole vibe too ๐ŸŒˆ if a quoted string is followed by a plus character ("+") and then another quoted string, the two get glued together into one string. you can keep stacking them to build one big string. whitespace, line breaks, and comments are all allowed between the quoted strings and the plus character. ๐Ÿ“

the order of operations in double-quoted strings is very specific: whitespace trimming happens before the backslash-escaped characters get substituted, and concatenation happens last of all. ๐Ÿง โœจ

6.1.3.1. Quoting Examples ๐Ÿ“

these strings are all equivalent, and yes they really are the same string ๐Ÿ˜ญ

hello
"hello"
'hello'
"hel" + "lo"
'hel' + "lo"

some special strings, served hot ๐Ÿ”ฅ

"\""  - string containing a double quote
'"'   - string containing a double quote
"\n"  - string containing a newline character
'\n'  - string containing a backslash followed
        by the character n

and these are the illegal ones, so don't you dare try them ๐Ÿ™…โ€โ™€๏ธ

''''  - a single-quoted string cannot contain single quotes
"""   - a double quote must be escaped in a double-quoted string

these two are equivalent too, which is kind of iconic for the indentation rules ๐ŸŽ€

    "first line
       second line"

"first line\n" + "  second line"

6.2. Identifiers ๐Ÿ’–

identifiers are how you name different kinds of YANG items. each one starts with an uppercase or lowercase ASCII letter or an underscore, and then it can have zero or more ASCII letters, digits, underscores, hyphens, and dots after that ๐Ÿฆ‹ implementations MUST support identifiers up to 64 characters in length and MAY support longer ones. identifiers are case sensitive, so capitalisation matters, babe ๐Ÿ’… the formal rule for the syntax is the one called "identifier" in Section 14, and identifiers can be written as quoted or unquoted strings.

6.2.1. Identifiers and Their Namespaces ๐Ÿงš

each identifier lives in a namespace that depends on what kind of YANG item it is. and every identifier defined in a namespace MUST be unique, no copycats allowed ๐Ÿ™…โ€โ™€๏ธ

  • 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. so any descendant node can 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. so any descendant node can use that grouping, and it MUST NOT define a grouping with the same name. ๐Ÿ’œ
  • all leafs, leaf-lists, lists, containers, choices, rpcs, actions, notifications, anydatas, 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, so you don't have to introduce something before you use it โœจ

6.3. Statements ๐ŸŒˆ

a YANG module is basically a sequence of statements. each statement starts with a keyword, then has zero or one argument, and then ends either with a semicolon (";") or with a block of substatements wrapped in braces ("{ }"). here's the shape of it:

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.19). other modules can import those extensions with the "import" statement (see Section 7.1.5). when an imported extension is used, its keyword MUST be qualified using the prefix that the extension's module was imported with. and if an extension is used in the module where it's defined, its keyword MUST be qualified with the prefix of that module. ๐Ÿซถ

how extensions get processed depends on whether a YANG parser, or the tool set it lives in, claims support for them. an unsupported extension that shows up in a YANG module as an unknown-statement (see Section 14) MAY be ignored in its entirety. but any supported extension MUST be processed according to the specification that governs that extension. ๐Ÿ’…

care must be taken when defining extensions so that modules using them still make sense for applications that don't support those extensions ๐Ÿฅบ

6.4. XPath Evaluations ๐Ÿ”ฎ

YANG relies on XML Path Language (XPath) 1.0 [XPATH] as a notation for specifying lots of inter-node references and dependencies. an implementation is not required to implement an XPath interpreter, but it MUST ensure that the requirements encoded in the data model are enforced. how that enforcement happens is up to the implementation ๐Ÿ’– 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 instead of using the XPath expression directly, which is pretty iconic honestly ๐Ÿ‘‘

the data model used in the XPath expressions is the same one used in XPath 1.0 [XPATH], with the same extension for root node children that XSLT 1.0 uses (see Section 3.1 in [XSLT]). specifically, that means the root node may have any number of element nodes as its children. ๐ŸŒธ

the data tree has no concept of document order. an implementation needs to choose some document order, but how it does that is an implementation decision. so XPath expressions in YANG modules SHOULD NOT rely on any specific document order, babe, don't build your life on it ๐Ÿ™ˆ

numbers in XPath 1.0 are IEEE 754 [IEEE754-2008] double-precision floating-point values (see Section 3.5 in [XPATH]). this means some values of int64, uint64, and decimal64 types (see Sections 9.2 and 9.3) can't be exactly represented in XPath expressions. so you should be extra careful when using nodes with 64-bit numeric values in XPath expressions. especially for equality comparisons, the results can be unexpected ๐Ÿ˜ญ

here's a little example to show what I mean ๐Ÿฆ‹

leaf lxiv {
  type decimal64 {
    fraction-digits 18;
  }
  must ". <= 10";
}

an instance of the "lxiv" leaf with the value 10.0000000000000001 will then successfully pass validation. ๐Ÿซ โœจ

6.4.1. XPath Context โœจ

okay so every single YANG XPath expression shares the same XPath context definition ๐Ÿ’… here's the tea:

  • the set of namespace declarations is every "import" statement's prefix and namespace pair in the module where the XPath expression is specified, plus 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.13). Inside a typedef, that namespace is affected by where the typedef is referenced. If a typedef is defined and referenced within a grouping, the namespace is affected by where the grouping is used (see Section 7.13) ๐Ÿฆ‹
  • the function library is the core function library defined in [XPATH] and the functions defined in Section 10 ๐Ÿงš
  • the set of variable bindings is empty ๐Ÿซถ

the way unprefixed names get handled was borrowed from XPath 2.0 [XPATH2.0], and it keeps XPath expressions in YANG nice and short ๐Ÿ’– no ambiguity may ever arise, because YANG node identifiers are always qualified names with a non-null namespace URI. iconic, honestly ๐Ÿ˜ญ

the accessible tree depends on where the statement with the XPath expression is defined:

  • if the XPath expression is defined in a substatement to a data node that represents configuration, the accessible tree is the data in the datastore where the context node exists. The root node has all top-level configuration data nodes in all modules as children ๐ŸŒธ
  • if the XPath expression is defined in a substatement to a data node that represents state data, the accessible tree is all state data in the server, and the running configuration datastore. The root node has all top-level data nodes in all modules as children โญ
  • if the XPath expression is defined in a substatement to a "notification" statement, the accessible tree is the notification instance, all state data in the server, and the running configuration datastore. If the notification is defined on the top level in a module, then the root node has the node representing the notification being defined and all top-level data nodes in all modules as children. Otherwise, the root node has all top-level data nodes in all modules as children ๐Ÿ”ฅ
  • if the XPath expression is defined in a substatement to an "input" statement in an "rpc" or "action" statement, the accessible tree is the RPC or action operation instance, all state data in the server, and the running configuration datastore. The root node has top-level data nodes in all modules as children. Additionally, for an RPC, the root node also has the node representing the RPC operation being defined as a child. The node representing the operation being defined has the operation's input parameters as children ๐Ÿ‘‘
  • if the XPath expression is defined in a substatement to an "output" statement in an "rpc" or "action" statement, the accessible tree is the RPC or action operation instance, all state data in the server, and the running configuration datastore. The root node has top-level data nodes in all modules as children. Additionally, for an RPC, the root node also has the node representing the RPC operation being defined as a child. The node representing the operation being defined has the operation's output parameters as children ๐ŸŒˆ

in the accessible tree, all leafs and leaf-lists with default values in use exist (see Sections 7.6.1 and 7.7.2) โœจ

if a node that exists in the accessible tree has a non-presence container as a child, then the non-presence container also exists in the accessible tree. no cap, that one's important ๐Ÿ“

the context node varies with the YANG XPath expression and is specified where the YANG statement with the XPath expression is defined ๐Ÿ’œ

6.4.1.1. Examples

given the following module:

module example-a {
  yang-version 1.1;
  namespace urn:example:a;
  prefix a;

  container a {
    list b {
      key id;
      leaf id {
        type string;
      }
      notification down {
        leaf reason {
          type string;
        }
      }
      action reset {
        input {
          leaf delay {
            type uint32;
          }
        }
        output {
          leaf result {
            type string;
          }
        }
      }
    }
  }
  notification failure {
    leaf b-ref {
      type leafref {
        path "/a/b/id";
      }
    }
  }
}

and given the following data tree, specified in XML:

<a xmlns="urn:example:a">
  <b>
    <id>1</id>
  </b>
  <b>
    <id>2</id>
  </b>
</a>

the accessible tree for a notification "down" on /a/b[id="2"] is:

<a xmlns="urn:example:a">
  <b>
    <id>1</id>
  </b>
  <b>
    <id>2</id>
    <down>
      <reason>error</reason>
    </down>
  </b>
</a>
// possibly other top-level nodes here

the accessible tree for an action invocation of "reset" on /a/b[id="1"] with the "when" parameter set to "10" would be:

<a xmlns="urn:example:a">
  <b>
    <id>1</id>
    <reset>
      <delay>10</delay>
    </reset>
  </b>
  <b>
    <id>2</id>
  </b>
</a>
// possibly other top-level nodes here

the accessible tree for the action output of this action is:

<a xmlns="urn:example:a">
  <b>
    <id>1</id>
    <reset>
      <result>ok</result>
    </reset>
  </b>
  <b>
    <id>2</id>
  </b>
</a>
// possibly other top-level nodes here

the accessible tree for a notification "failure" could be:

<a xmlns="urn:example:a">
  <b>
    <id>1</id>
  </b>
  <b>
    <id>2</id>
  </b>
</a>
<failure>
  <b-ref>2</b-ref>
</failure>
// possibly other top-level nodes here

6.5. Schema Node Identifier ๐Ÿฆ‹

a schema node identifier is a string that identifies a node in the schema tree ๐ŸŒธ it has two forms, "absolute" and "descendant", defined by the rules "absolute-schema-nodeid" and "descendant-schema-nodeid" in Section 14, respectively. a schema node identifier consists of 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 an imported module.

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 does not have any substatements defined in YANG can have vendor-specific extensions as substatements. for example, the "description" statement does not have any substatements defined in YANG, but the following is legal:

description "Some text." {
  ex:documentation-flag 5;
}

7.1. The "module" Statement ๐ŸŽ€

the "module" statement defines the module's name and groups all statements that belong to the module together. the "module" statement's argument is the name of the module, followed by a block of substatements that holds detailed module information. the module name is an identifier (see Section 6.2).

Names of modules published in RFC streams [RFC4844] MUST be assigned by IANA; see Section 14 in [RFC6020]. ๐Ÿ’–

private module names are assigned by the organization owning the module without a central registry. See Section 5.1 for recommendations on how to name modules ๐ŸŒˆ

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 |
+--------------+---------+-------------+
| anydata      | 7.10    | 0..n        |
| anyxml       | 7.11    | 0..n        |
| augment      | 7.17    | 0..n        |
| choice       | 7.9     | 0..n        |
| contact      | 7.1.8   | 0..1        |
| container    | 7.5     | 0..n        |
| description  | 7.21.3  | 0..1        |
| deviation    | 7.20.3  | 0..n        |
| extension    | 7.19    | 0..n        |
| feature      | 7.20.1  | 0..n        |
| grouping     | 7.12    | 0..n        |
| identity     | 7.18    | 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.16    | 0..n        |
| organization | 7.1.7   | 0..1        |
| prefix       | 7.1.4   | 1           |
| reference    | 7.21.4  | 0..1        |
| revision     | 7.1.9   | 0..n        |
| rpc          | 7.14    | 0..n        |
| typedef      | 7.3     | 0..n        |
| uses         | 7.13    | 0..n        |
| yang-version | 7.1.2   | 1           |
+--------------+---------+-------------+

7.1.2. The "yang-version" Statement ๐Ÿ“

the "yang-version" statement specifies which version of the YANG language was used in developing the module. the statement's argument is a string. It MUST contain the value "1.1" for YANG modules defined based on this specification. ๐Ÿซถ

a module or submodule that doesn't contain the "yang-version" statement, or one that contains the value "1", is developed for YANG version 1, defined in [RFC6020]. โญ

handling of the "yang-version" statement for versions other than "1.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 such a higher version ๐Ÿ™ˆ

for compatibility between YANG versions 1 and 1.1, see Section 12 ๐ŸŒธ

7.1.3. The "namespace" Statement ๐ŸŒˆ

the "namespace" statement defines the XML namespace that all identifiers defined by the module are qualified by in the XML encoding, with the exception of identifiers for data nodes, action nodes, and notification nodes defined inside a grouping (see Section 7.13 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

okay so the "prefix" statement is how a module names its own nickname ๐ŸŽ€ Its argument is the prefix string used to access the module, and that string MAY be used with the module to point at definitions inside it, like "if:ifName". A prefix is an identifier (see Section 6.2) โœจ

When it's used inside the "module" statement, the "prefix" statement sets the prefix that's suggested when someone imports this module ๐Ÿ’…

To make NETCONF XML easier to read, a NETCONF client or server that generates XML or XPath with prefixes SHOULD use the module's prefix as the XML namespace prefix, unless that causes a conflict ๐ŸŒธ

Inside the "import" statement, the "prefix" statement sets the prefix you use to reach definitions in the imported module ๐Ÿงš You write the prefix, then a colon, then the identifier, like if:ifIndex. To keep YANG modules readable, the prefix a module defines SHOULD be used when that module is imported, unless there's a conflict. And if there IS a conflict (two different modules that both define the same prefix are imported), at least one of them MUST be imported with a different prefix ๐Ÿ˜ญ

All prefixes, including the module's own prefix, MUST be unique within the module or submodule ๐Ÿ”ฅ

7.1.5. The "import" Statement

the "import" statement pulls definitions from one module into another module or submodule ๐Ÿฆ‹ The argument is the name of the module you want to import, and it's followed by a block of substatements with the detailed import info. When a module gets imported, the importing module is allowed to:

  • 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 gives the imported module a prefix that's scoped to the importing module or submodule ๐Ÿ’– You can have multiple "import" statements to pull from different modules ๐Ÿ“

If the optional "revision-date" substatement is there, any typedef, grouping, extension, feature, and identity that the local module references gets taken from that specified revision of the imported module. It's an error if that revision doesn't exist. If there's no "revision-date" substatement, it's undefined which revision they come from ๐Ÿ‘€

You can import multiple revisions of the same module, as long as you use different prefixes for each one โœจ

                 +---------------+---------+-------------+
                 | substatement  | section | cardinality |
                 +---------------+---------+-------------+
                 | description   | 7.21.3  | 0..1        |
                 | prefix        | 7.1.4   | 1           |
                 | reference     | 7.21.4  | 0..1        |
                 | revision-date | 7.1.5.1 | 0..1        |
                 +---------------+---------+-------------+

                        The import's Substatements

7.1.5.1. The import's "revision-date" Statement

the import's "revision-date" statement is how you specify which version of the module to import ๐Ÿ“…

7.1.6. The "include" Statement

the "include" statement brings content from a submodule up to that submodule's parent module ๐ŸŒˆ The argument is an identifier, the name of the submodule to include. A module can only include submodules that belong to it, as defined by the "belongs-to" statement (see Section 7.2.2) ๐Ÿ”’

When a module includes a submodule, the submodule's contents get folded into the module's node hierarchy ๐ŸŒณ

For backward compatibility with YANG version 1, a submodule is allowed to include another submodule that belongs to the same module. That isn't necessary in YANG version 1.1 (see Section 5.1) ๐Ÿ™ˆ

If the optional "revision-date" substatement is there, that specified revision of the submodule gets included in the module. It's an error if the revision doesn't exist. Without the "revision-date" substatement, it's undefined which revision of the submodule gets included ๐Ÿคทโ€โ™€๏ธ

Multiple revisions of the same submodule MUST NOT be included. Not even a little ๐Ÿ™…โ€โ™€๏ธ

                 +---------------+---------+-------------+
                 | substatement  | section | cardinality |
                 +---------------+---------+-------------+
                 | description   | 7.21.3  | 0..1        |
                 | reference     | 7.21.4  | 0..1        |
                 | revision-date | 7.1.5.1 | 0..1        |
                 +---------------+---------+-------------+

                       The includes's Substatements

7.1.7. The "organization" Statement

the "organization" statement names the party responsible for this module ๐Ÿข The argument is a string giving a textual description of the organization or organizations that developed the module under their auspices ๐Ÿ’ผ

7.1.8. The "contact" Statement

the "contact" statement is where the contact info lives ๐Ÿ“ฎ The argument is a string with contact details for the person or people who should get technical questions about this module, like their name, postal address, phone number, and email address ๐Ÿ’Œ

7.1.9. The "revision" Statement

the "revision" statement tracks the editorial revision history of the module, starting with the initial revision ๐Ÿ—“๏ธ A series of "revision" statements details 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 "revision" statement ๐Ÿ’… For every published editorial change, a new one SHOULD be added in front of the revisions sequence, so that all the revisions stay in reverse chronological order ๐Ÿ•ฐ๏ธ

7.1.9.1. The revision's Substatements

                 +--------------+---------+-------------+
                 | substatement | section | cardinality |
                 +--------------+---------+-------------+
                 | description  | 7.21.3  | 0..1        |
                 | reference    | 7.21.4  | 0..1        |
                 +--------------+---------+-------------+

7.1.10. Usage Example

this example relies on [RFC6991] ๐ŸŽ€

  module example-system {
    yang-version 1.1;
    namespace "urn:example:system";
    prefix "sys";

    import ietf-yang-types {
      prefix "yang";
      reference "RFC 6991: Common YANG Data Types";
    }

    include example-types;

    organization "Example Inc.";
    contact
      "Joe L. User

       Example Inc.
       42 Anywhere Drive
       Nowhere, CA 95134
       USA

       Phone: +1 800 555 0100
       Email: joe@example.com";

    description
      "The module for entities implementing the Example system.";

    revision 2007-06-09 {
      description "Initial revision.";
    }

    // definitions follow...
  }

7.2. The "submodule" Statement

okay so a YANG module can be built out of several submodules, even though the module is the main unit in YANG ๐Ÿงฉ That lets a module designer split a complicated model into pieces, where every submodule adds to one shared namespace that's defined by the module that includes them ๐ŸŒธ

The "submodule" statement defines the submodule's name and groups together all the statements that belong to it ๐Ÿ’œ Its argument is the submodule's name, followed by a block of substatements with the detailed submodule info. The submodule name is an identifier (see Section 6.2) โœจ

Names of submodules published in RFC streams [RFC4844] MUST be assigned by IANA; see Section 14 in [RFC6020] ๐Ÿท๏ธ

Private submodule names are assigned by the organization that owns the submodule, without a central registry. See Section 5.1 for recommendations on naming submodules ๐Ÿฆ‹

A submodule typically has the following layout:

  submodule <module-name> {

    <yang-version statement>

    // module identification
    <belongs-to statement>

    // linkage statements
    <import 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 |
                 +--------------+---------+-------------+
                 | anydata      | 7.10    | 0..n        |
                 | anyxml       | 7.11    | 0..n        |
                 | augment      | 7.17    | 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.21.3  | 0..1        |
                 | deviation    | 7.20.3  | 0..n        |
                 | extension    | 7.19    | 0..n        |
                 | feature      | 7.20.1  | 0..n        |
                 | grouping     | 7.12    | 0..n        |
                 | identity     | 7.18    | 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.16    | 0..n        |
                 | organization | 7.1.7   | 0..1        |
                 | reference    | 7.21.4  | 0..1        |
                 | revision     | 7.1.9   | 0..n        |
                 | rpc          | 7.14    | 0..n        |
                 | typedef      | 7.3     | 0..n        |
                 | uses         | 7.13    | 0..n        |
                 | yang-version | 7.1.2   | 1           |
                 +--------------+---------+-------------+

7.2.2. The "belongs-to" Statement

the "belongs-to" statement says which module a submodule belongs to ๐Ÿ’– The argument is an identifier, the name of that module ๐ŸŒท

A submodule MUST only be included by either the module it belongs to or another submodule that belongs to that same module ๐Ÿ™Œ

The mandatory "prefix" substatement gives the module that the submodule belongs to a prefix ๐Ÿ’… Every definition in that module, and in all of its submodules, can be accessed using that prefix ๐Ÿ”‘

                 +--------------+---------+-------------+
                 | substatement | section | cardinality |
                 +--------------+---------+-------------+
                 | prefix       | 7.1.4   | 1           |
                 +--------------+---------+-------------+

                       The belongs-to's Substatement

7.2.3. Usage Example ๐ŸŽ€

okay bestie, here's a tiny submodule so you can see what one actually looks like ๐Ÿ’…

     submodule example-types {
       yang-version 1.1;
       belongs-to "example-system" {
         prefix "sys";
       }

       import ietf-yang-types {
         prefix "yang";
       }

       organization "Example Inc.";
       contact
         "Joe L. User

          Example Inc.
          42 Anywhere Drive
          Nowhere, CA 95134
          USA

          Phone: +1 800 555 0100
          Email: joe@example.com";

       description
         "This submodule defines common Example types.";

       revision "2007-06-09" {
         description "Initial revision.";
       }

       // definitions follow...
     }

7.3. The "typedef" Statement ๐Ÿฆ‹

okay so the "typedef" statement lets you make a brand new type ๐Ÿ’– it can be used locally in the module or submodule, and by other modules that import from it, following the rules in Section 5.5. the new one 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 giving family tree ๐ŸŒณ

the argument is an identifier, the name of the type you're making, and it MUST be followed by a block of substatements that holds the detailed typedef info โœจ

the name of the type MUST NOT be one of the YANG built-in types ๐Ÿ™… and if the typedef is defined at the top level of a YANG module or submodule, the 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.21.3  | 0..1        |
                 | reference    | 7.21.4  | 0..1        |
                 | status       | 7.21.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, which MUST be present, defines the base type this type is derived from. check Section 7.4 for all the deets.

7.3.3. The "units" Statement ๐Ÿ“

the "units" statement is optional, and it takes a string that's a textual definition of the units associated with the type.

7.3.4. The typedef's "default" Statement ๐ŸŒธ

the "default" statement takes a string that holds a default value for the new type.

the value of the "default" statement MUST be valid according to the type specified in the "type" statement, no cap ๐Ÿ’€

if the base type has a default value and the new derived type doesn't specify a new default value, 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, that derived type or leaf definition MUST specify a new default value that's compatible with the restrictions ๐Ÿซถ

7.3.5. Usage Example ๐Ÿงš

     typedef listen-ipv4-address {
       type inet:ipv4-address;
       default "0.0.0.0";
     }

7.4. The "type" Statement ๐ŸŒˆ

the "type" statement takes a string argument that's the name of a YANG built-in type (see Section 9) or a derived type (see Section 7.3), followed by an optional block of substatements that puts further restrictions on the type โœจ

which restrictions you can apply depends on the type being restricted ๐Ÿ‘‘ the restriction statements for all built-in types are described in the subsections of Section 9.

7.4.1. The type's Substatements

               +------------------+---------+-------------+
               | substatement     | section | cardinality |
               +------------------+---------+-------------+
               | base             | 7.18.2  | 0..n        |
               | bit              | 9.7.4   | 0..n        |
               | enum             | 9.6.4   | 0..n        |
               | fraction-digits  | 9.3.4   | 0..1        |
               | length           | 9.4.4   | 0..1        |
               | path             | 9.9.2   | 0..1        |
               | pattern          | 9.4.5   | 0..n        |
               | range            | 9.2.4   | 0..1        |
               | require-instance | 9.9.3   | 0..1        |
               | type             | 7.4     | 0..n        |
               +------------------+---------+-------------+

7.5. The "container" Statement ๐Ÿงš

the "container" statement is used to define an interior data node in the schema tree. it takes one argument, which is an identifier, followed by a block of substatements that holds the detailed container info.

a container node doesn't have a value, but it does have a list of child nodes in the data tree. those child nodes are defined in the container's substatements.

7.5.1. Containers with Presence ๐ŸŒท

YANG supports two styles of containers: ones that only exist to organize the hierarchy of data nodes, and ones whose presence in the data tree has an explicit meaning ๐ŸŒŸ

in the first style, the container has no meaning of its own and only exists to hold child nodes. basically, a container with no child nodes is semantically the same as the container not being there at all. YANG calls this style a "non-presence container", and it's the default style ๐Ÿซง

for example, the set of scrambling options for Synchronous Optical Network (SONET) interfaces can be put inside a "scrambling" container, which keeps the organization neat and keeps these nodes together. the "scrambling" node itself has no meaning, so removing it when it's empty takes that chore off the user's plate ๐Ÿงน

in the second style, the presence of the container itself carries some meaning, representing a single bit of data.

for configuration data, the container is both a configuration knob and a way of organizing related configuration nodes. these containers get explicitly created and deleted ๐Ÿ”ง

YANG calls this style a "presence container", and it's marked with the "presence" statement, which takes a text string as its argument saying what the presence of the node means ๐Ÿ’–

for example, an "ssh" container can turn on the ability to log into the server using Secure SHell (SSH), but it can also hold any SSH-related config knobs, like connection rates or retry limits ๐Ÿ”

the "presence" statement (see Section 7.5.5) is what gives semantics to the existence of the container in the data tree โœจ

7.5.2. The container's Substatements

                 +--------------+---------+-------------+
                 | substatement | section | cardinality |
                 +--------------+---------+-------------+
                 | action       | 7.15    | 0..n        |
                 | anydata      | 7.10    | 0..n        |
                 | anyxml       | 7.11    | 0..n        |
                 | choice       | 7.9     | 0..n        |
                 | config       | 7.21.1  | 0..1        |
                 | container    | 7.5     | 0..n        |
                 | description  | 7.21.3  | 0..1        |
                 | grouping     | 7.12    | 0..n        |
                 | if-feature   | 7.20.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        |
                 | notification | 7.16    | 0..n        |
                 | presence     | 7.5.5   | 0..1        |
                 | reference    | 7.21.4  | 0..1        |
                 | status       | 7.21.2  | 0..1        |
                 | typedef      | 7.3     | 0..n        |
                 | uses         | 7.13    | 0..n        |
                 | when         | 7.21.5  | 0..1        |
                 +--------------+---------+-------------+

7.5.3. The "must" Statement ๐Ÿ’…

the "must" statement, which is optional, takes a string argument holding an XPath expression (see Section 6.4). it's how you formally declare a constraint on valid data, and the constraint gets enforced according to the rules in Section 8 ๐Ÿ”’

when a datastore is validated, all "must" constraints are conceptually evaluated once for each node in the accessible tree (see Section 6.4.1) ๐ŸŒŠ

all such constraints MUST evaluate to "true" for the data to be valid ๐ŸŽฏ

the XPath expression is conceptually evaluated in the following context, in addition to the definition in Section 6.4.1:

  • if the "must" statement is a substatement of a "notification" statement, the context node is the node representing the notification in the accessible tree.
  • if the "must" statement is a substatement of an "input" statement, the context node is the node representing the operation in the accessible tree.
  • if the "must" statement is a substatement of an "output" statement, the context node is the node representing the operation in the accessible tree.
  • otherwise, the context node is the node in the accessible tree for which the "must" statement is defined.

the result of the XPath expression gets converted to 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 Section 9.1), any XPath comparisons are done on the canonical value ๐Ÿฌ

also note that the XPath expression is conceptually evaluated. this means an implementation doesn't have to use an XPath evaluator in the server. how the evaluation is actually done is an implementation decision ๐Ÿง 

7.5.4. The must's Substatements

                 +---------------+---------+-------------+
                 | substatement  | section | cardinality |
                 +---------------+---------+-------------+
                 | description   | 7.21.3  | 0..1        |
                 | error-app-tag | 7.5.4.2 | 0..1        |
                 | error-message | 7.5.4.1 | 0..1        |
                 | reference     | 7.21.4  | 0..1        |
                 +---------------+---------+-------------+

7.5.4.1. The "error-message" Statement ๐Ÿšจ

the "error-message" statement, which is optional, takes a string as an argument. if the constraint evaluates to "false", that string is passed as <error-message> in the <rpc-error> in NETCONF.

7.5.4.2. The "error-app-tag" Statement ๐Ÿท๏ธ

the "error-app-tag" statement, which is optional, takes a string as an argument. if the constraint evaluates to "false", that string is passed as <error-app-tag> in the <rpc-error> in NETCONF.

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 ifMTU = 1500' {
         error-message "An Ethernet MTU must be 1500";
       }
       must 'ifType != "atm" or'
          + ' (ifMTU <= 17966 and ifMTU >= 64)' {
         error-message "An ATM MTU must be 64 .. 17966";
       }
     }

7.5.5. The "presence" Statement โœจ

okay so the "presence" statement is how a container gets a meaning just by existing ๐Ÿฆ‹ It takes one argument, a string with a textual description of what the node's presence means.

If a container has a "presence" statement, then the container showing up in the data tree actually says something ๐Ÿ’… Otherwise, the container is just there to give the data some structure and carries no meaning by itself ๐Ÿงš

See Section 7.5.1 for more deets ๐Ÿ’–

7.5.6. The container's Child Node Statements ๐ŸŒธ

Inside a container, you can use the "container", "leaf", "list", "leaf-list", "uses", "choice", "anydata", and "anyxml" statements to define child nodes. That's the whole squad of child-node statements โœจ

7.5.7. XML Encoding 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 become subelements of the container element. If the container defines RPC or action input or output parameters, those subelements come out in the same order they're defined in the "container" statement. Otherwise, they can be in any order, no cap ๐Ÿซถ

Whitespace between the subelements to the container is insignificant, so an implementation MAY insert whitespace characters between subelements. Totally optional, bestie ๐ŸŒˆ

If a non-presence container doesn't have any child nodes, the container may or may not be present in the XML encoding ๐Ÿ™ˆ

7.5.8. NETCONF <edit-config> Operations ๐Ÿ”ฅ

Containers can be created, deleted, replaced, and modified through <edit-config> by using the "operation" attribute (see Section 7.2 in [RFC6241]) in the container's XML element. Main character energy ๐Ÿ‘‘

If a container does not have a "presence" statement and the last child node is deleted, the NETCONF server MAY delete the container ๐Ÿ’…

When a NETCONF server processes an <edit-config> request, here's the elements of procedure for the container node:

  • 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, here's a container definition to vibe with:

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 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="urn:example: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, followed by a block of substatements that holds 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 exists in zero or one instance in the data tree ๐Ÿงš

The "leaf" statement is used to define a scalar variable of a particular built-in or derived type. Simple, clean, iconic โœจ

7.6.1. The leaf's Default Value ๐ŸŒˆ

The default value of a leaf is the value the server uses if the leaf does not exist in the data tree. How it gets used depends on 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 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 the case node is the choice's default case, and if 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.

When any of those cases apply, the default value is said to be in use ๐Ÿ’…

Note that if the leaf or any of its ancestors has a "when" condition or "if-feature" expression that evaluates to "false", then the default value is not in use. Plot twist ๐Ÿ™ˆ

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. It's literally pretending it's there ๐Ÿซถ

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 ๐ŸŽ€

Here's the whole substatement lineup, with the section and cardinality for each one:

+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| config       | 7.21.1  | 0..1        |
| default      | 7.6.4   | 0..1        |
| description  | 7.21.3  | 0..1        |
| if-feature   | 7.20.2  | 0..n        |
| mandatory    | 7.6.5   | 0..1        |
| must         | 7.5.3   | 0..n        |
| reference    | 7.21.4  | 0..1        |
| status       | 7.21.2  | 0..1        |
| type         | 7.6.3   | 1           |
| units        | 7.3.3   | 0..1        |
| when         | 7.21.5  | 0..1        |
+--------------+---------+-------------+

7.6.3. The leaf's "type" Statement ๐Ÿฆ‹

The "type" statement, which MUST be present, takes as an argument the name of an existing built-in or derived type. The optional substatements specify restrictions on this type. See Section 7.4 for details ๐Ÿ’–

7.6.4. The leaf's "default" Statement ๐ŸŒธ

The "default" statement is optional, and 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. No sneaky invalid values ๐Ÿ‘‘

The "default" statement MUST NOT be present on nodes where "mandatory" is "true". Mandatory means no defaults, bestie ๐Ÿ™…โ€โ™€๏ธ

The definition of the default value MUST NOT be marked with an "if-feature" statement. For example, the following is illegal:

leaf color {
  type enumeration {
    enum blue { if-feature blue; }
    ...
  }
  default blue; // illegal - enum value is conditional
}

7.6.5. The leaf's "mandatory" Statement ๐Ÿ”ฅ

The "mandatory" statement is optional, and it takes as an argument the string "true" or "false" and 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. Enforcement era ๐Ÿซถ

7.6.6. XML Encoding 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 ๐Ÿงš

See Section 7.6.8 for an example โœจ

7.6.7. NETCONF <edit-config> Operations ๐ŸŒธ

When a NETCONF server processes an <edit-config> request, here's the elements of procedure for the leaf node:

  • If the operation is "merge" or "replace", the node is created if it does not 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 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.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.";
}

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="urn:example: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 ๐ŸŒˆ It takes one argument, which is an identifier, followed by a block of substatements that holds the detailed leaf-list info.

In configuration data, the values in a leaf-list MUST be unique. No duplicates allowed, periodt ๐Ÿ’…

The definitions of the default values MUST NOT be marked with an "if-feature" statement ๐Ÿ™ˆ

Conceptually, the values in the data tree MUST be in the canonical form (see Section 9.1) โœจ

7.7.1. Ordering โœจ

okay so YANG has two vibes for ordering entries in lists and leaf-lists ๐Ÿ’…. In a lot of lists the order doesn't change how the config gets set up, so the server is free to sort the entries however it wants ๐Ÿงš. The list's description can hint at an order for the implementor, lowkey. YANG calls this style "system ordered" and marks it with ordered-by system.

For example, a list of valid users would usually be sorted alphabetically, since the order they showed up in the config doesn't matter for creating their accounts ๐Ÿ“.

The other style is where the order actually matters ๐Ÿ˜ญ. The user is responsible for ordering the entries, and the server keeps that order. YANG calls this style "user ordered" and marks it with ordered-by user.

For example, the order that packet filter entries get applied to incoming traffic can change how that traffic is filtered ๐Ÿ”ฅ. The user has to decide whether the entry that discards all TCP traffic goes before or after the entry that allows all traffic from trusted interfaces. The choice of order would be crucial.

YANG gives you a whole toolkit inside NETCONF's <edit-config> operation to control the order of user-ordered list entries ๐Ÿ‘‘. You can insert or rearrange entries, put one first or last, or position it before or after another specific entry.

The ordered-by statement is covered in Section 7.7.7.


7.7.2. The leaf-list's Default Values ๐ŸŒธ

The default values of a leaf-list are what the server uses when the leaf-list isn't in the data tree ๐Ÿซถ. Which rule applies depends on the leaf-list'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 default values MUST be used.
  • Otherwise, if this ancestor is a case node, the default values MUST be used if any node from the case exists in the data tree or the case node is the choice's default case, and if no nodes from any other case exist in the data tree.
  • Otherwise, the default values MUST be used if the ancestor node exists in the data tree.

In these cases, the default values are said to be in use.

Note that if the leaf-list or any of its ancestors has a when condition or if-feature expression that evaluates to false, then the default values are not in use ๐Ÿ™ˆ.

When the default values are in use, the server MUST operationally behave as if the leaf-list was present in the data tree with the default values as its values โœจ.

If a leaf-list has one or more default statements, the leaf-list's default values are the values of those default statements. If the leaf-list is user ordered, the default values are used in the order of the default statements. Otherwise, if the leaf-list's type has a default value and the leaf-list does not have a min-elements statement with a value greater than or equal to one, then the leaf-list's default value is one instance of the type's default value. In all other cases, the leaf-list does not have any default values ๐Ÿ’œ.


7.7.3. The leaf-list's Substatements ๐ŸŽ€

Here's the full lineup of substatements, bestie ๐Ÿ’–

                 +--------------+---------+-------------+
                 | substatement | section | cardinality |
                 +--------------+---------+-------------+
                 | config       | 7.21.1  | 0..1        |
                 | default      | 7.7.4   | 0..n        |
                 | description  | 7.21.3  | 0..1        |
                 | if-feature   | 7.20.2  | 0..n        |
                 | max-elements | 7.7.6   | 0..1        |
                 | min-elements | 7.7.5   | 0..1        |
                 | must         | 7.5.3   | 0..n        |
                 | ordered-by   | 7.7.7   | 0..1        |
                 | reference    | 7.21.4  | 0..1        |
                 | status       | 7.21.2  | 0..1        |
                 | type         | 7.4     | 1           |
                 | units        | 7.3.3   | 0..1        |
                 | when         | 7.21.5  | 0..1        |
                 +--------------+---------+-------------+

7.7.4. The leaf-list's "default" Statement ๐Ÿฌ

The default statement is optional. It takes a string as its argument, and that string holds a default value for the leaf-list ๐Ÿงšโ€โ™€๏ธ.

The value of the default statement MUST be valid according to the type specified in the leaf-list's type statement.

The default statement MUST NOT be present on nodes where min-elements has a value greater than or equal to one ๐Ÿ™…โ€โ™€๏ธ.


7.7.5. The "min-elements" Statement ๐ŸŒท

The min-elements statement is optional. It takes a non-negative integer as its argument, and that puts a constraint on valid list entries ๐Ÿซง. A valid leaf-list or list MUST have at least min-elements entries.

If no min-elements statement is present, it defaults to zero.

How the constraint behaves 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 no such ancestor exists in the schema tree, the constraint is enforced.
  • Otherwise, 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.6. The "max-elements" Statement ๐Ÿฆ‹

The max-elements statement is optional. It takes either a positive integer or the string "unbounded" as its argument, and that puts a constraint on valid list entries ๐Ÿ’…. A valid leaf-list or list always has at most max-elements entries.

If no max-elements statement is present, it defaults to "unbounded".

The max-elements constraint is enforced according to the rules in Section 8 โœจ.


7.7.7. The "ordered-by" Statement ๐Ÿ‘‘

The ordered-by statement defines whether the order of entries within a list is determined by the user or by the system ๐ŸŒˆ. The argument is one of the strings "system" or "user". If it's not present, ordering defaults to "system".

This statement is ignored if the list represents state data, RPC output parameters, or notification content ๐Ÿ’€.

See Section 7.7.1 for additional information.

7.7.7.1. ordered-by system ๐Ÿง

The entries in the list are ordered according to an order determined by the system. The description string for the list can hint at an order for the server implementor. If it doesn't, an implementation is free to order the entries in any order ๐Ÿซถ.

An implementation SHOULD use the same order for the same data, regardless of how the data were created. Using a deterministic order makes comparisons possible with simple tools like "diff" ๐Ÿ”.

This is the default order ๐Ÿ’–.

7.7.7.2. ordered-by user ๐ŸŽ€

The entries in the list are ordered according to an order defined by the user. In NETCONF, this order is controlled by special XML attributes in the <edit-config> request. See Section 7.7.9 for details ๐Ÿฆ‹.


7.7.8. XML Encoding 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 is encoded to XML according to the type and is 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 elements for siblings of the leaf-list, unless the leaf-list defines RPC or action input or output parameters ๐Ÿ“.

See Section 7.7.10 for an example.


7.7.9. NETCONF <edit-config> Operations ๐Ÿ’œ

Leaf-list entries can be created and deleted, but not modified, through <edit-config>, by using the "operation" attribute in 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 is inserted. These can be used during "create" operations to insert a new leaf-list entry, or during "merge" or "replace" operations to insert a new leaf-list 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 no "insert" attribute is present in the "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 in 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 elements of procedure for a leaf-list node are as follows:

  • 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.10. Usage Example โœจ

Okay, so picture a leaf-list called allow-user ๐Ÿ’–. It's just a bunch of user name patterns, and nothing fancy is going on:

leaf-list allow-user {
  type string;
  description
    "A list of user name patterns to allow.";
}

Here's what an XML instance of it looks like ๐ŸŽ€:

<allow-user>alice</allow-user>
<allow-user>bob</allow-user>

Now say you want to add a brand-new element to that list, using the default <edit-config> operation merge. Your <rpc> would look 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="urn:example:config">
        <services>
          <ssh>
            <allow-user>eric</allow-user>
          </ssh>
        </services>
      </system>
    </config>
  </edit-config>
</rpc>

Next up is an ordered-by user leaf-list, which is where the order actually matters ๐Ÿ‘‘:

leaf-list cipher {
  type string;
  ordered-by user;
  description
    "A list of ciphers.";
}

If you want to slide a new cipher, blowfish-cbc, in right after 3des-cbc, you'd send this ๐ŸŒˆ:

<rpc message-id="102"
     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="urn:example: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 the way to define an interior data node in the schema tree ๐Ÿงš. A list node can show up in multiple instances in the data tree, and each one of those instances is called a list entry. The statement takes exactly one argument, an identifier, and then a block of substatements that holds all the detailed list info ๐Ÿ’….

A list entry gets its unique identity from the values of the list's keys, but only if keys are actually defined โœจ.

7.8.1. The list's Substatements ๐Ÿ“

Here's the full cheat sheet of what can live inside a list ๐Ÿ’œ:

+--------------+---------+-------------+
| substatement | section | cardinality |
+--------------+---------+-------------+
| action       | 7.15    | 0..n        |
| anydata      | 7.10    | 0..n        |
| anyxml       | 7.11    | 0..n        |
| choice       | 7.9     | 0..n        |
| config       | 7.21.1  | 0..1        |
| container    | 7.5     | 0..n        |
| description  | 7.21.3  | 0..1        |
| grouping     | 7.12    | 0..n        |
| if-feature   | 7.20.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.6   | 0..1        |
| min-elements | 7.7.5   | 0..1        |
| must         | 7.5.3   | 0..n        |
| notification | 7.16    | 0..n        |
| ordered-by   | 7.7.7   | 0..1        |
| reference    | 7.21.4  | 0..1        |
| status       | 7.21.2  | 0..1        |
| typedef      | 7.3     | 0..n        |
| unique       | 7.8.3   | 0..n        |
| uses         | 7.13    | 0..n        |
| when         | 7.21.5  | 0..1        |
+--------------+---------+-------------+

7.8.2. The list's "key" Statement ๐Ÿ”‘

The "key" statement is a bit of a big deal ๐Ÿ”ฅ. It MUST be present if the list represents configuration, and it MAY be present otherwise. It takes one argument, a string holding a space-separated list of one or more leaf identifiers from this list. A leaf identifier MUST NOT appear more than once in the key ๐Ÿ™ˆ. Each leaf identifier listed MUST refer to a child leaf of the list. Those leafs can be defined straight inside the list's substatements, or inside groupings that the list uses ๐Ÿฆ‹.

Together, the values of all the key leafs are what make a list entry unique ๐Ÿ’–. Every key leaf MUST be given a value whenever a list entry gets created. That means any default values on the key leafs, or on their types, get ignored ๐Ÿ™ƒ. Any "mandatory" statements on the key leafs get ignored too.

A key leaf can be any built-in type or any derived type, so you've got lots of flexibility ๐ŸŒˆ.

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 14 ๐Ÿ“œ.

7.8.3. The list's "unique" Statement ๐Ÿงท

The "unique" statement puts constraints on which list entries are valid ๐Ÿ’…. It takes a string argument with a space-separated list of schema node identifiers, and those MUST be written in the descendant form (check the rule "descendant-schema-nodeid" in Section 14). Each schema node identifier in there MUST refer to a leaf ๐ŸŽ€.

If one of the referenced leafs represents configuration data, then all of the referenced leafs MUST represent configuration data too ๐Ÿ”’.

The "unique" constraint says that the combined values of all the leaf instances in that argument string, default-valued leafs included, MUST be unique across every list entry where all of the referenced leafs either exist or have default values โœจ. The constraint gets enforced according to the rules in Section 8.

The unique string syntax is formally defined by the rule "unique-arg" in Section 14 ๐Ÿ“œ.

7.8.3.1. Usage Example ๐Ÿญ

Let's look at this list ๐Ÿง:

list server {
  key "name";
  unique "ip port";
  leaf name {
    type string;
  }
  leaf ip {
    type inet:ip-address;
  }
  leaf port {
    type inet:port-number;
  }
}

With that list, this 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>

This one, though, is totally valid ๐Ÿซถ. The "http" and "ftp" list entries don't have values for all of the referenced leafs, so they don't get counted 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 ๐Ÿงฉ

Inside a list, the "container", "leaf", "list", "leaf-list", "uses", "choice", "anydata", and "anyxml" statements can all be used to define child nodes of the list ๐ŸŒธ.

7.8.5. XML Encoding Rules ๐ŸŽ€

A list gets encoded as a series of XML elements, one per 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). There's no XML element wrapping the list as a whole, so no extra layer ๐Ÿ™….

The list's key nodes get encoded 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 or action input or output parameters, those subelements are encoded in the same order they're defined in the "list" statement. Otherwise, they can come in any order ๐Ÿฆ‹.

Whitespace between the subelements of a list entry doesn't mean anything, so an implementation MAY insert whitespace characters between them ๐Ÿซง.

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 XML elements for list entries MAY be interleaved with elements for siblings of the list, unless the list defines RPC or action input or output parameters ๐Ÿ’….

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 ๐Ÿ’–. Every time, the values of all the keys are what uniquely identify a list entry. If not all keys are specified for a list entry, a "missing-element" error gets returned ๐Ÿ˜ฌ.

In an "ordered-by user" list, the "insert" and "key" attributes in the YANG XML namespace (Section 5.3.1) control where the entry lands in the list ๐Ÿงญ. They can be used 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, to 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 that list entry ๐ŸŽฏ.

If there's no "insert" attribute in 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 their XML elements show up 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 matches 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 ๐Ÿ“‹:

  • If the operation is "merge" or "replace", the list entry gets created if it doesn't exist yet. If it already exists and both the "insert" and "key" attributes are present, the entry gets moved according to those attributes' values. If it already exists and neither attribute is 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 is deleted from the list if it exists. If it doesn't exist, a "data-missing" error is returned ๐Ÿ’€.

7.8.7. Usage Example ๐ŸŽ€

Okay, let's see this in action! ๐Ÿ’… Here's a list of users, and yes, the names are iconic ๐Ÿ˜ญ

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;
  }
}

And here's what a matching XML instance looks like ๐Ÿฆ‹

<user>
  <name>fred</name>
  <type>admin</type>
  <full-name>Fred Flintstone</full-name>
</user>

Next, we want to create a brand new user named "barney" ๐Ÿงš So we send an <rpc> with <edit-config> and tag the new entry with nc:operation="create":

<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="urn:example:config">
        <user nc:operation="create">
          <name>barney</name>
          <type>admin</type>
          <full-name>Barney Rubble</full-name>
        </user>
      </system>
    </config>
  </edit-config>
</rpc>

Now for a glow-up ๐ŸŒธ To change the type of "fred" to "superuser", we send another <rpc>. This time there's no operation attribute, so we're just touching the fields we want to change:

<rpc message-id="102"
     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="urn:example:config">
        <user>
          <name>fred</name>
          <type>superuser</type>
        </user>
      </system>
    </config>
  </edit-config>
</rpc>

Now the ordered-by user list, which is where things get spicy ๐Ÿ”ฅ Order actually matters here, so the list is defined like this:

list user {
  description
    "This is a list of users in the system.";
  ordered-by user;
  config true;

  key "first-name surname";

  leaf first-name {
    type string;
  }
  leaf surname {
    type string;
  }
  leaf type {
    type string;
  }
}

To insert a new user "barney rubble" right after "fred flintstone" ๐Ÿ“ we use yang:insert="after" together with a yang:key that points at the anchor entry:

<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="urn:example:config"
           xmlns:ex="urn:example:config">
        <user nc:operation="create"
              yang:insert="after"
              yang:key="[ex:first-name='fred']
                        [ex:surname='flintstone']">
          <first-name>barney</first-name>
          <surname>rubble</surname>
          <type>admin</type>
        </user>
      </system>
    </config>
  </edit-config>
</rpc>

And to move "barney rubble" so that it sits before "fred flintstone" ๐Ÿ‘‘ we send this one:

<rpc message-id="102"
     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="urn:example:config"
           xmlns:ex="urn:example:config">
        <user nc:operation="merge"
              yang:insert="before"
              yang:key="[ex:name='fred']
                        [ex:surname='flintstone']">
          <first-name>barney</first-name>
          <surname>rubble</surname>
        </user>
      </system>
    </config>
  </edit-config>
</rpc>

7.9. The "choice" Statement ๐Ÿฆ‹

Okay, the choice statement is all about alternatives, and it's giving "only one of you can be in the room at a time" ๐Ÿ’€ Only one of the options in a choice may be present in any one data tree. The statement takes an identifier, followed by a block of substatements that hold the detailed choice info. That identifier is how we point at the choice node in the schema tree. Here's the tea โ˜• a choice node does not exist in the data tree itself.

A choice is made up of several branches, and each branch is defined with a "case" substatement. Every branch holds a bunch of child nodes. At most one branch's nodes can exist at the same time, so no love triangles allowed ๐Ÿ’…

Since only one case can be valid in the data tree at once, creating a node from one case implicitly deletes every node from all the other cases. So if a request creates a node from a case, the server will delete any existing nodes that are defined in other cases inside the choice. Main character energy, but make it exclusive ๐Ÿ”ฅ

7.9.1. The choice's Substatements ๐ŸŒˆ

Here's the list of what can go inside a choice, with section references and how many of each you can have:

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | anydata      | 7.10    | 0..n        |
             | anyxml       | 7.11    | 0..n        |
             | case         | 7.9.2   | 0..n        |
             | choice       | 7.9     | 0..n        |
             | config       | 7.21.1  | 0..1        |
             | container    | 7.5     | 0..n        |
             | default      | 7.9.3   | 0..1        |
             | description  | 7.21.3  | 0..1        |
             | if-feature   | 7.20.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.21.4  | 0..1        |
             | status       | 7.21.2  | 0..1        |
             | when         | 7.21.5  | 0..1        |
             +--------------+---------+-------------+

7.9.2. The choice's "case" Statement ๐Ÿ’–

The case statement is how we define the branches of a choice. It takes an identifier as its argument, followed by a block of substatements with the detailed case info.

That identifier is how we identify the case node in the schema tree. Same deal as before: a case node does not exist in the data tree ๐Ÿซถ

Inside a case, the "anydata", "anyxml", "choice", "container", "leaf", "list", "leaf-list", and "uses" statements can all be used to define child nodes for the case node. The identifiers of all these child nodes MUST be unique within all cases in a choice. Here's an example that is NOT okay, so don't do it bestie ๐Ÿ™ˆ

choice interface-type {     // This example is illegal YANG
  case a {
    leaf ethernet { ... }
  }
  case b {
    container ethernet { ...}
  }
}

Now for a shortcut โœจ the case statement can be left out if the branch holds a single "anydata", "anyxml", "choice", "container", "leaf", "list", or "leaf-list" statement. Even then, the case node still exists in the schema tree, and its identifier is the same as the identifier of the child node. One catch: schema node identifiers (Section 6.5) MUST always explicitly include case node identifiers, so the shortcut doesn't get you out of that. Here's the shorthand:

choice interface-type {
  container ethernet { ... }
}

and it is equivalent to:

choice interface-type {
  case ethernet {
    container ethernet { ... }
  }
}

Last rule for this bit: the case identifier MUST be unique within a choice ๐Ÿงš

7.9.2.1. The case's Substatements ๐ŸŒธ

Here's what can go inside a case:

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | anydata      | 7.10    | 0..n        |
             | anyxml       | 7.11    | 0..n        |
             | choice       | 7.9     | 0..n        |
             | container    | 7.5     | 0..n        |
             | description  | 7.21.3  | 0..1        |
             | if-feature   | 7.20.2  | 0..n        |
             | leaf         | 7.6     | 0..n        |
             | leaf-list    | 7.7     | 0..n        |
             | list         | 7.8     | 0..n        |
             | reference    | 7.21.4  | 0..1        |
             | status       | 7.21.2  | 0..1        |
             | uses         | 7.13    | 0..n        |
             | when         | 7.21.5  | 0..1        |
             +--------------+---------+-------------+

7.9.3. The choice's "default" Statement ๐Ÿ“

The default statement says which case should be treated as the default when no child nodes from any of the choice's cases exist. Its argument is the identifier of the default "case" statement. If the default statement is missing, 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 looking at the "default" statements of nodes under the cases, meaning default values of leafs and leaf-lists, and default cases of nested choices. The default values and nested default cases under the default case are used if none of the nodes under any of the cases are present.

There MUST NOT be any mandatory nodes (Section 3) directly under the default case. ๐Ÿ˜ค

Default values for child nodes under a case are only 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 the case is not the default case, the default values of the case's child nodes are ignored. Plot twist, but it makes sense ๐ŸŒˆ

In this example, the choice defaults to "interval", so that default value gets used 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;
        units minutes;
        default 30;
      }
    }
    case daily {
      leaf daily {
        type empty;
      }
      leaf time-of-day {
        type string;
        units 24-hour-clock;
        default "01.00";
      }
    }
    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. Not one from each, just one from exactly one branch, so it's giving pick-one energy ๐Ÿ’…

If it's not specified, the default is "false".

How strict the constraint gets depends on the type of the choice's closest ancestor node in the schema tree that is not a non-presence container (see Section 7.5.1). Here's how that breaks down:

  • If no such ancestor exists in the schema tree, the constraint is enforced.
  • Otherwise, 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 Encoding Rules ๐ŸŽ€

The choice and case nodes are not visible in XML. Totally invisible, no cap ๐Ÿ‘ป

The child nodes of the selected "case" statement MUST be encoded in the same order as they are defined in the "case" statement, but only if they are part of an RPC or action input or output parameter definition. Otherwise, the subelements can be encoded in any order. โœจ

7.9.6. Usage Example

okay bestie, here's a choice to look at ๐Ÿ‘€โœจ We've got a container called protocol, and inside it a choice named name with two cases, a (which holds a udp leaf) and b (which holds a tcp leaf). Both leaves are type empty, so they're just presence flags ๐Ÿ’…

container protocol {
  choice name {
    case a {
      leaf udp {
        type empty;
      }
    }
    case b {
      leaf tcp {
        type empty;
      }
    }
  }
}

Here's the XML instance that matches it ๐ŸŽ€ Since the tcp leaf is set, the protocol is TCP ๐Ÿฆ‹

<protocol>
  <tcp/>
</protocol>

And if you want to switch from TCP to UDP, you send an <edit-config> in an RPC. It's giving "create the udp one and the choice takes care of the rest" ๐Ÿ”ฅ (the nc:operation="create" attribute is what asks for that).

<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="urn:example:config">
        <protocol>
          <udp nc:operation="create"/>
        </protocol>
      </system>
    </config>
  </edit-config>
</rpc>

7.10. The "anydata" Statement

The "anydata" statement makes an interior node in the schema tree ๐ŸŒธ It takes one argument, which is an identifier, and then a block of substatements that hold the detailed anydata info.

The "anydata" statement is for representing a set of nodes that we don't know at design time, but that can still be modeled with YANG (anyxml doesn't count here). Maybe the data model for that content becomes known later through protocol signaling or something else outside this document. That's optional, not required ๐Ÿงš

An example where anydata is super useful is a list of received notifications, where you can't know which notifications will show up when you're designing the thing ๐Ÿ’œ

Some quick rules, no cap:

An anydata node cannot be augmented (see Section 7.17).

An anydata node exists in zero or one instance in the data tree.

An implementation may or may not know the data model used to model a specific instance of an anydata node.

Because anydata limits how you can manipulate its content, the "anydata" statement SHOULD NOT be used to define configuration data. Not me using it for config ๐Ÿ™ˆ

7.10.1. The anydata's Substatements

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | config       | 7.21.1  | 0..1        |
             | description  | 7.21.3  | 0..1        |
             | if-feature   | 7.20.2  | 0..n        |
             | mandatory    | 7.6.5   | 0..1        |
             | must         | 7.5.3   | 0..n        |
             | reference    | 7.21.4  | 0..1        |
             | status       | 7.21.2  | 0..1        |
             | when         | 7.21.5  | 0..1        |
             +--------------+---------+-------------+

7.10.2. XML Encoding Rules

An anydata node gets encoded as an XML element. The element's local name is the anydata's identifier, and its namespace is the module's XML namespace (see Section 7.1.3) โœจ The value of the anydata node is a set of nodes, and those get encoded as XML subelements of the anydata element.

7.10.3. NETCONF <edit-config> Operations

An anydata node is treated as an opaque chunk of data ๐Ÿ“ฆ That data can only be modified in its entirety, so no poking at individual bits.

Any "operation" attributes on subelements of an anydata node get ignored by the NETCONF server, so don't bother ๐Ÿ™ƒ

When a NETCONF server processes an <edit-config> request, here's the elements of procedure for an anydata node:

  • If the operation is "merge" or "replace", the node is created if it doesn't exist, and its value is set to the subelements of the anydata node found in the XML RPC data.
  • If the operation is "create", the node is created if it doesn't exist, and its value is set to the subelements of the anydata 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 doesn't exist, a "data-missing" error is returned.

7.10.4. Usage Example

Say we've got this "anydata" statement:

list logged-notification {
  key time;
  leaf time {
    type yang:date-and-time;
  }
  anydata data;
}

And here's a valid encoding of a list instance for it. Look at that notification nested inside <data> ๐Ÿ’–

<logged-notification>
  <time>2014-07-29T13:43:12Z</time>
  <data>
    <notification
      xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
      <eventTime>2014-07-29T13:43:01Z</eventTime>
      <event xmlns="urn:example:event">
        <event-class>fault</event-class>
        <reporting-entity>
          <card>Ethernet0</card>
        </reporting-entity>
        <severity>major</severity>
      </event>
    </notification>
  </data>
</logged-notification>

7.11. The "anyxml" Statement

The "anyxml" statement makes an interior node in the schema tree ๐Ÿฆ‹ Same deal as before: one identifier argument, then a block of substatements with the detailed anyxml info.

It's for representing an unknown chunk of XML, and there are no restrictions on that XML at all. That's handy in RPC replies, for example. The <filter> parameter in the <get-config> operation in NETCONF is one such case ๐ŸŒˆ

An anyxml node cannot be augmented (see Section 7.17).

An anyxml node exists in zero or one instance in the data tree.

Since anyxml limits how the content can be manipulated, the "anyxml" statement SHOULD NOT be used to define configuration data. Same energy as anydata, periodt ๐Ÿ’…

Here's some history tea โ˜• In YANG version 1, "anyxml" was the only statement that could model an unknown hierarchy of data. Nowadays that hierarchy is often actually modeled in YANG, just without the specific data model being known at design time. In those situations it is RECOMMENDED to use "anydata" (Section 7.10) instead of "anyxml" ๐Ÿซถ

7.11.1. The anyxml's Substatements

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | config       | 7.21.1  | 0..1        |
             | description  | 7.21.3  | 0..1        |
             | if-feature   | 7.20.2  | 0..n        |
             | mandatory    | 7.6.5   | 0..1        |
             | must         | 7.5.3   | 0..n        |
             | reference    | 7.21.4  | 0..1        |
             | status       | 7.21.2  | 0..1        |
             | when         | 7.21.5  | 0..1        |
             +--------------+---------+-------------+

7.11.2. XML Encoding Rules

An anyxml node gets 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 โœจ

Heads up: any XML prefixes used in the encoding are local to each instance encoding. So the same XML might end up encoded differently by different implementations ๐Ÿ‘€

7.11.3. NETCONF <edit-config> Operations

An anyxml node is treated as an opaque chunk of data ๐Ÿ“ฆ It can only be modified in its entirety.

Any "operation" attributes on subelements of an anyxml node are ignored by the NETCONF server.

When a NETCONF server processes an <edit-config> request, here's the elements of procedure for an anyxml node:

  • If the operation is "merge" or "replace", the node is created if it doesn't 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 doesn't 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 doesn't exist, a "data-missing" error is returned.

7.11.4. Usage Example

Given this "anyxml" statement:

anyxml html-info;

Here are two valid encodings of the same anyxml value. Different prefixes, same meaning, iconic ๐ŸŒธ

<html-info>
  <p xmlns="http://www.w3.org/1999/xhtml">
    This is <em>very</em> cool.
  </p>
</html-info>
<html-info>
  <x:p xmlns:x="http://www.w3.org/1999/xhtml">
    This is <x:em>very</x:em> cool.
  </x:p>
</html-info>

7.12. The "grouping" Statement

The "grouping" statement defines a reusable block of nodes ๐Ÿงš It can be used locally in the module or submodule, and also by other modules that import from it, following the rules in Section 5.5. It takes one argument, which is an identifier, followed by a block of substatements with the detailed grouping info.

The "grouping" statement is not a data definition statement, so it doesn't define any nodes in the schema tree on its own. Think of a grouping like a "structure" or a "record" in regular programming languages ๐Ÿ’œ

Once a grouping is defined, you can reference it in a "uses" statement (see Section 7.13). A grouping MUST NOT reference itself, neither directly nor indirectly through a chain of other groupings. Absolutely no recursion loops here ๐Ÿ™…

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 copy-paste. It also defines a collection of nodes. Identifiers appearing inside the grouping get 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, that means extensions defined as direct children of a "grouping" statement get applied to the grouping itself ๐ŸŽ€

Note that if a grouping defines an action or a notification node in its hierarchy, then it can't be used in every context. For example, it can't be used in an rpc definition. See Sections 7.15 and 7.16 ๐Ÿ™ˆ

7.12.1. The grouping's Substatements

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | action       | 7.15    | 0..n        |
             | anydata      | 7.10    | 0..n        |
             | anyxml       | 7.11    | 0..n        |
             | choice       | 7.9     | 0..n        |
             | container    | 7.5     | 0..n        |
             | description  | 7.21.3  | 0..1        |
             | grouping     | 7.12    | 0..n        |
             | leaf         | 7.6     | 0..n        |
             | leaf-list    | 7.7     | 0..n        |
             | list         | 7.8     | 0..n        |
             | notification | 7.16    | 0..n        |
             | reference    | 7.21.4  | 0..1        |
             | status       | 7.21.2  | 0..1        |
             | typedef      | 7.3     | 0..n        |
             | uses         | 7.13    | 0..n        |
             +--------------+---------+-------------+

7.12.2. Usage Example

Here's a grouping that pulls in types from another module. It's a reusable endpoint with an IP and a port ๐Ÿฆ‹

  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.13. The "uses" Statement โœจ

okay bestie, uses is the statement that lets you reuse a grouping definition ๐Ÿงš. It takes one argument, which is the name of the grouping you want.

When you uses a grouping, its nodes get copied into the current schema tree, and then they get updated by any refine and augment statements. so you define it once and it shows up wherever you need it ๐Ÿ’–

The identifiers inside a grouping don't get bound to a namespace until the grouping is pulled into the schema tree by a uses statement that is not inside a grouping statement. At that point, they get bound to the namespace of the current module ๐ŸŒธ

7.13.1. The uses's Substatements

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | augment      | 7.17    | 0..n        |
             | description  | 7.21.3  | 0..1        |
             | if-feature   | 7.20.2  | 0..n        |
             | reference    | 7.21.4  | 0..1        |
             | refine       | 7.13.2  | 0..n        |
             | status       | 7.21.2  | 0..1        |
             | when         | 7.21.5  | 0..1        |
             +--------------+---------+-------------+

7.13.2. The "refine" Statement

Some of the properties of each node in the grouping can be tweaked with the refine statement ๐ŸŽ€. Its argument is a string that identifies a node in the grouping. 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 as 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.
  • A leaf-list node may get a set of default values, or a new set of default values if it already had defaults; i.e., the set of refined default values replaces the defaults already given.
  • 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, anydata, anyxml, or choice node may get a different "mandatory" statement.
  • A container node may get a "presence" statement.
  • A leaf, leaf-list, list, container, anydata, or anyxml node may get additional "must" expressions.
  • A leaf-list or list node may get a different "min-elements" or "max-elements" statement.
  • A leaf, leaf-list, list, container, choice, case, anydata, or anyxml node may get additional "if-feature" expressions.
  • Any node can get refined extensions, if the extension allows refinement. See Section 7.19 for details.

7.13.3. XML Encoding Rules

Each node in the grouping gets encoded as if it had been defined inline ๐Ÿซถ, even if it was imported from another module that has a different XML namespace.

7.13.4. Usage Example

To use the "endpoint" grouping defined in Section 7.12.2 in an HTTP server definition inside some other module, we can do this ๐Ÿ’…:

  import example-system {
    prefix "sys";
  }

  container http-server {
    leaf name {
      type string;
    }
    uses sys:endpoint;
  }

A corresponding XML instance example:

  <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, we can add a default:

  container http-server {
    leaf name {
      type string;
    }
    uses sys:endpoint {
      refine port {
        default 80;
      }
    }
  }

If we want to define a list of servers where each server has "ip" and "port" as keys, we can do this โœจ:

  list server {
    key "ip port";
    leaf name {
      type string;
    }
    uses sys:endpoint;
  }
๐Ÿšซ The following is an error, so don't do it:
  container http-server {
    uses sys:endpoint;
    leaf ip {          // illegal - same identifier "ip" used twice
      type string;
    }
  }

7.14. The "rpc" Statement ๐Ÿฆ‹

The rpc statement is used to define an RPC operation. It takes one argument, which is an identifier, followed by a block of substatements that holds the detailed rpc information. That argument is the name of the RPC ๐Ÿ‘‘

The rpc statement defines an rpc node in the schema tree. Under the 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.14.1. The rpc's Substatements

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | description  | 7.21.3  | 0..1        |
             | grouping     | 7.12    | 0..n        |
             | if-feature   | 7.20.2  | 0..n        |
             | input        | 7.14.2  | 0..1        |
             | output       | 7.14.3  | 0..1        |
             | reference    | 7.21.4  | 0..1        |
             | status       | 7.21.2  | 0..1        |
             | typedef      | 7.3     | 0..n        |
             +--------------+---------+-------------+

7.14.2. The "input" Statement

The input statement is optional ๐ŸŒธ. It's used to define the input parameters to the operation. It doesn't take an argument. The substatements of input define nodes under the operation's input node.

If a leaf in the input tree has a "mandatory" statement with the value "true", the leaf MUST be present in an RPC invocation ๐Ÿ”ฅ.

If a leaf in the input tree has a default value, the server MUST use this value in the same cases as those described in Section 7.6.1. In these cases, the server MUST operationally behave as if the leaf was present in the RPC invocation with the default value as its value.

If a leaf-list in the input tree has one or more default values, the server MUST use these values in the same cases as those described in Section 7.7.2. In these cases, the server MUST operationally behave as if the leaf-list was present in the RPC invocation with the default values as its values.

Since the input tree isn't part of any datastore, all "config" statements for nodes in the input tree get 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.14.2.1. The input's Substatements

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | anydata      | 7.10    | 0..n        |
             | anyxml       | 7.11    | 0..n        |
             | choice       | 7.9     | 0..n        |
             | container    | 7.5     | 0..n        |
             | grouping     | 7.12    | 0..n        |
             | leaf         | 7.6     | 0..n        |
             | leaf-list    | 7.7     | 0..n        |
             | list         | 7.8     | 0..n        |
             | must         | 7.5.3   | 0..n        |
             | typedef      | 7.3     | 0..n        |
             | uses         | 7.13    | 0..n        |
             +--------------+---------+-------------+

7.14.3. The "output" Statement

The output statement is optional too ๐Ÿ“. It's used to define the output parameters of the RPC operation. It doesn't take an argument. The substatements of output define nodes under the operation's output node.

If a leaf in the output tree has a "mandatory" statement with the value "true", the leaf MUST be present in an RPC reply ๐Ÿ’œ.

If a leaf in the output tree has a default value, the client MUST use this value in the same cases as those described in Section 7.6.1. In these cases, the client MUST operationally behave as if the leaf was present in the RPC reply with the default value as its value.

If a leaf-list in the output tree has one or more default values, the client MUST use these values in the same cases as those described in Section 7.7.2. In these cases, the client MUST operationally behave as if the leaf-list was present in the RPC reply with the default values as its values.

Since the output tree isn't part of any datastore, all "config" statements for nodes in the output tree get 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.14.3.1. The output's Substatements

             +--------------+---------+-------------+
             | substatement | section | cardinality |
             +--------------+---------+-------------+
             | anydata      | 7.10    | 0..n        |
             | anyxml       | 7.11    | 0..n        |
             | choice       | 7.9     | 0..n        |
             | container    | 7.5     | 0..n        |
             | grouping     | 7.12    | 0..n        |
             | leaf         | 7.6     | 0..n        |
             | leaf-list    | 7.7     | 0..n        |
             | list         | 7.8     | 0..n        |
             | must         | 7.5.3   | 0..n        |
             | typedef      | 7.3     | 0..n        |
             | uses         | 7.13    | 0..n        |
             +--------------+---------+-------------+

7.14.4. NETCONF XML Encoding Rules

An rpc node is encoded as a child XML element of the <rpc> element. This is the substitution group "rpcOperation" defined in [RFC6241] ๐Ÿงš. 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 are encoded as child XML elements of the rpc node's XML element, in the same order as they're defined within the "input" statement โœจ.

If the RPC operation invocation succeeded and no output parameters are returned, the <rpc-reply> contains a single <ok/> element, which is defined in [RFC6241] ๐Ÿซถ. If output parameters are returned, they're encoded as child elements of the <rpc-reply> element defined in [RFC6241], in the same order as they're defined within the "output" statement ๐Ÿ’–.

7.14.5. Usage Example

The following example defines an RPC operation ๐ŸŽ€:

  module example-rock {
    yang-version 1.1;
    namespace "urn:example:rock";
    prefix "rock";

    rpc rock-the-house {
      input {
        leaf zip-code {
          type string;
        }
      }
    }
  }

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="urn:example: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>

Okay bestie, let's get into it โœจ

7.15. The "action" Statement ๐ŸŽฌ

Okay so the "action" statement is how you define an operation that's hooked up to a specific container or list data node ๐Ÿ’… It takes one argument, which is an identifier, and then a block of substatements that holds all the juicy detail about the action. That argument is literally the name of the action ๐ŸŽ€

The "action" statement creates an action node in the schema tree. Under that action node, two schema nodes get defined too: one called "input" and one called "output" ๐Ÿฆ‹ Both of those live in the module's namespace, so they're part of the same family as everything else ๐ŸŒธ

Now here's the vibe check ๐Ÿ™ˆ An action MUST NOT be defined inside an rpc, another action, or a notification. Like, an action node MUST NOT have an rpc, action, or notification node as any of its ancestors in the schema tree. So if you've got a grouping with an action somewhere in its node hierarchy and you use that grouping in a notification definition, that's an error. Not me being dramatic, it's just the rule ๐Ÿ’€

An action also MUST NOT have any ancestor that is a list node without a "key" statement. No keyless lists allowed in this party ๐Ÿšซ

Since an action can't be defined at the top level of a module or inside a "case" statement, it's an error if a grouping that has an action at the top of its node hierarchy gets used at the top level of a module or in a case definition. Iykyk ๐Ÿงš

Here's the tea โ˜• The difference between an action and an rpc is that an action is tied to a node in the datastore, whereas an rpc is NOT. So when you invoke an action, you specify which datastore node it belongs to, along with the action's name and the input parameters ๐ŸŒˆ

7.15.1. The action's Substatements ๐Ÿ“

              +--------------+---------+-------------+
              | substatement | section | cardinality |
              +--------------+---------+-------------+
              | description  | 7.21.3  | 0..1        |
              | grouping     | 7.12    | 0..n        |
              | if-feature   | 7.20.2  | 0..n        |
              | input        | 7.14.2  | 0..1        |
              | output       | 7.14.3  | 0..1        |
              | reference    | 7.21.4  | 0..1        |
              | status       | 7.21.2  | 0..1        |
              | typedef      | 7.3     | 0..n        |
              +--------------+---------+-------------+

7.15.2. NETCONF XML Encoding Rules ๐Ÿง

Okay so when an action gets invoked, an element with the local name "action" in the namespace "urn:ietf:params:xml:ns:yang:1" (see Section 5.3.1) gets encoded as a child XML element of the <rpc> element from [RFC6241]. That's where the substitution group "rpcOperation" in [RFC6241] comes in ๐ŸŽ€

The <action> element holds a hierarchy of nodes that identifies the node in the datastore. It MUST contain all containers and list nodes in the direct path from the top level down to the list or container holding the action. For lists, all key leafs MUST also be included ๐Ÿ”‘ The innermost container or list then contains an XML element carrying the name of the defined action. Inside that element, the input parameters get encoded as child XML elements, in the same order they're defined in the "input" statement ๐Ÿ’–

Only one action can be invoked in one <rpc>, periodt โœ‹ If there's more than one action in the <rpc>, the server MUST reply with a "bad-element" <error-tag> in the <rpc-error> ๐Ÿ˜ญ

If the action invocation succeeded and no output parameters come back, the <rpc-reply> contains a single <ok/> element defined in [RFC6241] โœจ If there ARE output parameters, they get encoded as child elements of the <rpc-reply> element defined in [RFC6241], in the same order they're defined in the "output" statement ๐Ÿซถ

7.15.3. Usage Example ๐ŸŒธ

The following example defines an action to reset one server at a server farm ๐Ÿ–ฅ๏ธ

  module example-server-farm {
    yang-version 1.1;
    namespace "urn:example:server-farm";
    prefix "sfarm";

    import ietf-yang-types {
      prefix "yang";
    }

    list server {
      key name;
      leaf name {
        type string;
      }
      action reset {
        input {
          leaf reset-at {
            type yang:date-and-time;
            mandatory true;
           }
         }
         output {
           leaf reset-finished-at {
             type yang:date-and-time;
             mandatory true;
           }
         }
       }
     }
   }

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">
    <action xmlns="urn:ietf:params:xml:ns:yang:1">
      <server xmlns="urn:example:server-farm">
        <name>apache-1</name>
        <reset>
          <reset-at>2014-07-29T13:42:00Z</reset-at>
        </reset>
      </server>
    </action>
  </rpc>

  <rpc-reply message-id="101"
             xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
    <reset-finished-at xmlns="urn:example:server-farm">
      2014-07-29T13:42:12Z
    </reset-finished-at>
  </rpc-reply>

7.16. The "notification" Statement ๐Ÿ””

Okay bestie, the "notification" statement is how you define a notification ๐Ÿ’œ It takes one argument, which is an identifier, followed by a block of substatements holding all the detailed notification info. The "notification" statement creates a notification node in the schema tree โœจ

A notification can be defined at the top level of a module, or hooked up to a specific container or list data node in the schema tree ๐ŸŒˆ

A notification MUST NOT be defined inside an rpc, an action, or another notification. Basically, a notification node MUST NOT have an rpc, action, or notification node as any of its ancestors in the schema tree ๐Ÿ™… So if a grouping with a notification somewhere in its node hierarchy gets used in an rpc definition, that's an error. No cap ๐Ÿงข

A notification also MUST NOT have any ancestor that is a list node without a "key" statement. Same energy as before, keys or bust ๐Ÿ”‘

Since a notification can't be defined in a "case" statement, it's an error if a grouping with a notification at the top of its node hierarchy gets used in a case definition ๐Ÿ’…

If a leaf in the notification tree has a "mandatory" statement with the value "true", the leaf MUST be present in a notification instance. Non-negotiable, fr fr ๐Ÿ˜ค

If a leaf in the notification tree has a default value, the client MUST use this value in the same cases as those described in Section 7.6.1. In those cases, the client MUST operationally behave as if the leaf was present in the notification instance with the default value as its value ๐ŸŽ€

If a leaf-list in the notification tree has one or more default values, the client MUST use these values in the same cases as those described in Section 7.7.2. In those cases, the client MUST operationally behave as if the leaf-list was present in the notification instance with the default values as its values ๐ŸŒธ

Since the notification tree is NOT part of any datastore, all "config" statements for nodes in the notification tree are ignored ๐Ÿ™ˆ

7.16.1. The notification's Substatements ๐Ÿงš

              +--------------+---------+-------------+
              | substatement | section | cardinality |
              +--------------+---------+-------------+
              | anydata      | 7.10    | 0..n        |
              | anyxml       | 7.11    | 0..n        |
              | choice       | 7.9     | 0..n        |
              | container    | 7.5     | 0..n        |
              | description  | 7.21.3  | 0..1        |
              | grouping     | 7.12    | 0..n        |
              | if-feature   | 7.20.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        |
              | reference    | 7.21.4  | 0..1        |
              | status       | 7.21.2  | 0..1        |
              | typedef      | 7.3     | 0..n        |
              | uses         | 7.13    | 0..n        |
              +--------------+---------+-------------+

7.16.2. NETCONF XML Encoding Rules ๐Ÿฆ‹

A notification node defined at the top level of a module 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) ๐Ÿ’–

When a notification node is defined as a child of a data node, the <notification> element from [RFC5277] holds a hierarchy of nodes that identifies the node in the datastore. It MUST contain all containers and list nodes from the top level down to the list or container holding the notification. For lists, all key leafs MUST also be included ๐Ÿ”‘ The innermost container or list then contains an XML element carrying the name of the defined notification โœจ

The notification's child nodes get encoded as subelements of the notification node's XML element, in any order ๐Ÿซถ

7.16.3. Usage Example ๐ŸŽ€

The following example defines a notification at the top level of a module ๐Ÿ“ฃ

  module example-event {
    yang-version 1.1;
    namespace "urn:example:event";
    prefix "ev";

    notification event {
      leaf event-class {
        type string;
      }
      leaf reporting-entity {
        type instance-identifier;
      }
      leaf severity {
        type string;
      }
    }
  }

Here's a corresponding XML instance example of the complete notification ๐ŸŒˆ

  <notification
    xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
    <eventTime>2008-07-08T00:01:00Z</eventTime>
    <event xmlns="urn:example:event">
      <event-class>fault</event-class>
      <reporting-entity>
        /ex:interface[ex:name='Ethernet0']
      </reporting-entity>
      <severity>major</severity>
    </event>
  </notification>

The following example defines a notification in a data node ๐Ÿ“

  module example-interface-module {
    yang-version 1.1;
    namespace "urn:example:interface-module";
    prefix "if";

    container interfaces {
      list interface {
        key "name";
        leaf name {
          type string;
        }
        notification interface-enabled {
          leaf by-user {
            type string;
          }
        }
      }
    }
  }

Here's a corresponding XML instance example of the complete notification ๐Ÿ’œ

  <notification
    xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
    <eventTime>2008-07-08T00:01:00Z</eventTime>
    <interfaces xmlns="urn:example:interface-module">
      <interface>
        <name>eth1</name>
        <interface-enabled>
          <by-user>fred</by-user>
        </interface-enabled>
      </interface>
    </interfaces>
  </notification>

7.17. The "augment" Statement โœจ

okay bestie, so augment is how you bolt new stuff onto a schema tree that somebody else already built ๐Ÿงš You can do it from a module or submodule, and the tree can live in an external module, in your current module or its submodules, or in a grouping you pull in with a uses statement ๐Ÿซถ The argument is a string that points to a node in the schema tree. That node is called the augment's target node, and the target node MUST be either a container, list, choice, case, input, output, or notification node ๐Ÿ’… Then it gets augmented with whatever nodes you define in the substatements after the augment.

The argument string is a schema node identifier (see Section 6.5) ๐ŸŽ€ If your augment sits at the top level of a module or submodule, you MUST use the absolute form, which is the rule absolute-schema-nodeid in Section 14 ๐ŸŒธ If your augment is inside a uses statement instead, you MUST use the descendant form, which is the rule descendant-schema-nodeid in Section 14 ๐Ÿฆ‹

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 your augment ๐Ÿ“

If the target node is a container or list node, you can also use the action and notification statements inside your augment โœจ

If the target node is a choice node, you can use the case statement, or a shorthand case statement (see Section 7.9.2), inside your augment ๐ŸŒˆ

Okay, this one is a hard no ๐Ÿ™… The augment statement MUST NOT add multiple nodes with the same name from the same module to the target node ๐Ÿ’€

And if your augmentation adds mandatory nodes (see Section 3) that represent configuration to a target node in another module, the augmentation MUST be made conditional with a when statement ๐Ÿ”ฅ Be careful writing that when expression, because clients that don't know about your augmenting module shouldn't break ๐Ÿ˜ญ

Here's a cute example. It's totally fine to augment the "interface" entry with "mandatory-leaf", because the augmentation only kicks in when support for "some-new-iftype" exists ๐Ÿฆ‹ An old client has no idea that type exists, so it would never pick it, and therefore it would never end up adding a mandatory data node ๐Ÿ’–

module example-augment { yang-version 1.1; namespace "urn:example:augment"; prefix mymod;

import ietf-interfaces { prefix if; }

identity some-new-iftype { base if:interface-type; } augment "/if:interfaces/if:interface" { when 'derived-from-or-self(if:type, "mymod:some-new-iftype")';

leaf mandatory-leaf { mandatory true; type string; } } }

7.17.1. The augment's Substatements

+--------------+---------+-------------+

substatementsectioncardinality

+--------------+---------+-------------+

action7.150..n
anydata7.100..n
anyxml7.110..n
case7.9.20..n
choice7.90..n
container7.50..n
description7.21.30..1
if-feature7.20.20..n
leaf7.60..n
leaf-list7.70..n
list7.80..n
notification7.160..n
reference7.21.40..1
status7.21.20..1
uses7.130..n
when7.21.50..1

+--------------+---------+-------------+

7.17.2. XML Encoding Rules

All data nodes defined in the augment statement are defined as XML elements in the XML namespace of the module where the augment is specified ๐ŸŽ€

When a node is augmented, the augmenting child nodes get encoded as subelements of the augmented node, and they can show up in any order ๐ŸŒธ

7.17.3. Usage Example

Say we have, in namespace urn:example:interface-module:

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 urn:example:ds0, we have:

import example-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="urn:example:interface-module" xmlns:ds0="urn:example: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>

Another one! Suppose we have the choice defined in Section 7.9.6 ๐Ÿ‘‘ This construct extends the protocol definition:

augment /ex:system/ex:protocol/ex:name { case c { leaf smtp { type empty; } } }

Corresponding XML instance example, sis ๐Ÿ’…

<ex:system> <ex:protocol> <ex:tcp/> </ex:protocol> </ex:system>

or

<ex:system> <ex:protocol> <other:smtp/> </ex:protocol> </ex:system>


7.18. The "identity" Statement ๐Ÿฆ‹

the identity statement is how you define a new globally unique, abstract, and untyped identity ๐Ÿ’œ Its whole job is to carry a name, its semantics, and the fact that it exists, nothing more ๐Ÿซถ You can make an identity from scratch, or derive it from one or more base identities. The identity's argument is an identifier that works as its name, and it's followed by a block of substatements holding the detailed identity info โœจ

The built-in datatype identityref (see Section 9.10) is what you use to reference identities inside a data model ๐ŸŒˆ

7.18.1. The identity's Substatements

+--------------+---------+-------------+

substatementsectioncardinality

+--------------+---------+-------------+

base7.18.20..n
description7.21.30..1
if-feature7.20.20..n
reference7.21.40..1
status7.21.20..1

+--------------+---------+-------------+

7.18.2. The "base" Statement

The base statement is optional ๐ŸŽ€ It takes a string argument, which is the name of an existing identity that your new identity gets derived from. No base statement at all? Then the identity is defined from scratch ๐ŸŒธ If you have multiple base statements, the identity is derived from all of them ๐Ÿ‘‘

If the base name has a prefix, it points at an identity defined in the module that got imported with that prefix, or at the local module when the prefix matches the local module's prefix ๐Ÿ“ Otherwise, an identity with the matching name MUST be defined in the current module or in an included submodule ๐Ÿ”ฅ

An identity MUST NOT reference itself, not directly and not indirectly through some chain of other identities ๐Ÿ’€

Derivation of identities has these properties:

  • It's irreflexive, which means an identity is not derived from itself ๐Ÿ™ˆ
  • It's transitive, which means that if identity B is derived from A and C is derived from B, then C is also derived from A ๐Ÿซถ

7.18.3. Usage Example

module example-crypto-base { yang-version 1.1; namespace "urn:example:crypto-base"; prefix "crypto";

identity crypto-alg { description "Base identity from which all crypto algorithms are derived."; }

identity symmetric-key { description "Base identity used to identify symmetric-key crypto algorithms."; }

identity public-key { description "Base identity used to identify public-key crypto algorithms."; } }

module example-des { yang-version 1.1; namespace "urn:example:des"; prefix "des";

import "example-crypto-base" { prefix "crypto"; }

identity des { base "crypto:crypto-alg"; base "crypto:symmetric-key"; description "DES crypto algorithm."; } identity des3 { base "crypto:crypto-alg"; base "crypto:symmetric-key"; description "Triple DES crypto algorithm."; } }


7.19. The "extension" Statement โœจ

the extension statement lets you define brand new statements inside the YANG language ๐Ÿงš Other modules can import that new statement definition and use it too, which is lowkey iconic ๐Ÿ’–

The extension statement's argument is an identifier, and it's the new keyword for the extension. It must be followed by a block of substatements holding the detailed extension info ๐ŸŽ€ The whole point of the extension statement is to define a keyword so that other modules can import it and use it ๐ŸŒˆ

An extension gets used like a normal YANG statement: you write the statement name, then an argument if the extension statement defined one, then an optional block of substatements ๐Ÿฆ‹ The statement's name is made by smushing together 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 statement itself, using some mechanism that's outside the scope of this specification ๐Ÿซถ Syntactically, the substatements MUST be YANG statements, and that includes extensions defined using extension statements ๐Ÿ”ฅ YANG statements inside extensions MUST follow the syntactical rules in Section 14 ๐Ÿ’€

An extension can allow refinement (see Section 7.13.2) and deviations (Section 7.20.3.2), but how that gets defined is outside the scope of this specification ๐Ÿ™ˆ

7.19.1. The extension's Substatements

+--------------+---------+-------------+

substatementsectioncardinality

+--------------+---------+-------------+

argument7.19.20..1
description7.21.30..1
reference7.21.40..1
status7.21.20..1

+--------------+---------+-------------+

7.19.2. The "argument" Statement

The argument statement is optional ๐ŸŒธ It takes a string argument, which is the name of the argument to the keyword. If there's no argument statement, the keyword expects no argument when it gets used ๐Ÿ“

The argument's name shows up in the YIN mapping, where it's used as an XML attribute or element name, depending on the argument's "yin-element" statement ๐Ÿ’œ

7.19.2.1. The argument's Substatement

                 +--------------+----------+-------------+
                 | substatement | section  | cardinality |
                 +--------------+----------+-------------+
                 | yin-element  | 7.19.2.2 | 0..1        |
                 +--------------+----------+-------------+

7.19.2.2. The "yin-element" Statement

okay so the "yin-element" statement is optional ๐ŸŽ€ and it takes one argument: either "true" or "false" ๐Ÿ’… it tells you whether the argument gets mapped to an XML element in YIN or to an XML attribute instead (check out Section 13 for the deets โœจ).

if you don't include a "yin-element" statement at all, it just defaults to "false" ๐Ÿฆ‹ no drama!

7.19.3. Usage Example

to define an extension ๐ŸŒธ:

  module example-extensions {
    yang-version 1.1;
    ...

    extension c-define {
      description
        "Takes as an argument a name string.
         Makes the code generator use the given name
         in the #define.";
      argument "name";
    }
  }

and to use the extension ๐Ÿงš:

  module example-interfaces {
    yang-version 1.1;

    ...
    import example-extensions {
      prefix "myext";
    }
    ...

    container interfaces {
      ...
      myext:c-define "MY_INTERFACES";
    }
  }

bestie this section is all about the conformance statements ๐Ÿ’œ the stuff we talked about in Section 5.6 โœจ

7.20.1. The "feature" Statement

the "feature" statement is how you mark parts of the schema as conditional ๐ŸŒˆ you define a feature name, and then you can reference it later with the "if-feature" statement (see Section 7.20.2) ๐Ÿ’– any schema nodes tagged with "if-feature" get ignored by the server unless the server supports that feature expression. so the model can actually show what the server can and can't do, which gives you a richer model that works for different server abilities and roles ๐Ÿซถ it's giving flexibility, no cap.

the argument to "feature" is the new feature's name, and it follows the identifier rules in Section 6.2 ๐ŸŽ€ that name is what "if-feature" uses to tie the schema nodes to the feature.

here's an example ๐Ÿ“: a feature called "local-storage" stands for the server being able to store syslog messages on some kind of local storage. the "local-storage-limit" leaf depends on that feature. if the server doesn't say it supports "local-storage", then "local-storage-limit" just isn't supported ๐Ÿ˜ญ

  module example-syslog {
    yang-version 1.1;

    ...
    feature local-storage {
      description
        "This feature means that the server 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 lots of places in the YANG syntax ๐Ÿ”ฅ anything tagged with "if-feature" is ignored when the server doesn't support that feature. main character energy for the conditional stuff ๐Ÿ‘‘

a feature MUST NOT reference itself, neither directly nor indirectly through a chain of other features. no circular feature drama allowed ๐Ÿ’€

if a feature depends on other features (meaning it has one or more "if-feature" substatements), then for a server to support it, the server MUST also support all of those dependent features ๐Ÿซถ no picking and choosing!

7.20.1.1. The feature's Substatements

                 +--------------+---------+-------------+
                 | substatement | section | cardinality |
                 +--------------+---------+-------------+
                 | description  | 7.21.3  | 0..1        |
                 | if-feature   | 7.20.2  | 0..n        |
                 | reference    | 7.21.4  | 0..1        |
                 | status       | 7.21.2  | 0..1        |
                 +--------------+---------+-------------+

7.20.2. The "if-feature" Statement

the "if-feature" statement makes its parent statement conditional ๐Ÿ’– its argument is a boolean expression over feature names. inside that expression, a feature name evaluates to "true" if and only if the server supports that feature ๐Ÿฆ‹ the parent statement gets implemented by servers where the boolean expression comes out "true" โœจ

the if-feature boolean expression syntax is formally defined by the rule "if-feature-expr" in Section 14 ๐Ÿงš you can use parentheses to group stuff. when it gets evaluated, the order of precedence (highest first) is: grouping (parentheses), then "not", then "and", then "or" ๐Ÿ’…

if a feature name in the expression has a prefix, the prefixed name points to a feature defined in the module that got imported with that prefix. or, if the prefix matches the local module's prefix, it points to the local module ๐ŸŒธ otherwise, a feature with the matching name MUST be defined in the current module or an included submodule ๐ŸŽ€

a leaf that is a list key MUST NOT have any "if-feature" statements. list keys are off limits for this one, periodt ๐Ÿ™ˆ

7.20.2.1. Usage Example

in this example, the container "target" gets implemented if the server supports either the "outbound-tls" feature or the "outbound-ssh" feature ๐ŸŒˆ

  container target {
    if-feature "outbound-tls or outbound-ssh";
    ...
  }

these two examples are equivalent ๐Ÿ“:

  if-feature "not foo or bar and baz";

  if-feature "(not foo) or (bar and baz)";

7.20.3. The "deviation" Statement

the "deviation" statement defines a hierarchy of a module that the server doesn't implement faithfully ๐Ÿ˜ญ its argument is a string that identifies the node in the schema tree where the deviation happens. that node is called the deviation's target node ๐Ÿ’œ the contents of the "deviation" statement give you the details about the deviation.

the argument string is an absolute schema node identifier (see Section 6.5) โœจ

deviations describe how a server, or a class of servers, deviates from a standard ๐Ÿ”ฅ so deviations MUST never be part of a published standard, because they're the mechanism for learning how implementations differ from the standards ๐Ÿ’…

server deviations are strongly discouraged and MUST only be used as a last resort. telling the application how a server fails to follow a standard is NOT a replacement for implementing the standard correctly. a server that deviates from a module is not fully compliant with the module. ๐Ÿ’€

sometimes a particular device just doesn't have the hardware or software to support parts of a standard module ๐Ÿฅบ when that happens, the server has to pick one of two options: treat attempts to configure the unsupported parts as an error that gets reported back to the unsuspecting application, or ignore those incoming requests. neither choice is acceptable ๐Ÿ˜ค

so instead, YANG lets servers document the portions of a base module that aren't supported, or that are supported but with different syntax, by using the "deviation" statement ๐Ÿซถ

after applying all deviations a server announces, in any order, the resulting data model MUST still be valid โœจ the model has to hold together no matter how you stack the deviations ๐Ÿฆ‹

7.20.3.1. The deviation's Substatements

                 +--------------+----------+-------------+
                 | substatement | section  | cardinality |
                 +--------------+----------+-------------+
                 | description  | 7.21.3   | 0..1        |
                 | deviate      | 7.20.3.2 | 1..n        |
                 | reference    | 7.21.4   | 0..1        |
                 +--------------+----------+-------------+

7.20.3.2. The "deviate" Statement

the "deviate" statement defines how the server's implementation of the target node deviates from its original definition ๐ŸŒธ its 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 server ๐Ÿ™ˆ

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 ๐Ÿ”ฅ very precise, very important!

             +--------------+--------------+-------------+
             | substatement | section      | cardinality |
             +--------------+--------------+-------------+
             | config       | 7.21.1       | 0..1        |
             | default      | 7.6.4, 7.7.4 | 0..n        |
             | mandatory    | 7.6.5        | 0..1        |
             | max-elements | 7.7.6        | 0..1        |
             | min-elements | 7.7.5        | 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        |
             +--------------+--------------+-------------+

                      The deviate's Substatements

if the target node has a property defined by an extension, that property can be deviated, as long as the extension allows deviations ๐Ÿฆ‹ see Section 7.19 for the details ๐Ÿซถ

7.20.3.3. Usage Example

in this example, the server is telling client applications that it doesn't support the "daytime" service in the style of RFC 867 ๐Ÿ’–

  module example-deviations {
    yang-version 1.1;
    namespace "urn:example:deviations";
    prefix md;

    import example-base {
      prefix base;
    }

    deviation /base:system/base:daytime {
      deviate not-supported;
    }
    ...
  }

a server would advertise both modules, "example-base" and "example-deviations" ๐ŸŒˆ

the following example sets a server-specific default value on a leaf that doesn't have a default value defined ๐Ÿ“

  deviation /base:system/base:user/base:type {
    deviate add {
      default "admin"; // new users are 'admin' by default
    }
  }

in this example, the server 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 server might remove this "must" constraint by doing this ๐Ÿ’…:

  deviation /base:system {
    deviate delete {
      must "daytime or time";
    }
  }

7.21. Common Statements

this section defines substatements that are shared by several other statements โœจ the common crew ๐Ÿซถ

The 'config' Statement, status, description, reference, when: all the good stuff is in here

Okay so, picture this, bestie ๐Ÿ’… we're picking up right where the last part left off, so no intro needed. Let's get into it ๐ŸŽ€

7.21.1. The "config" Statement โœจ

Okay so the "config" statement takes one argument, the string "true" or "false" ๐Ÿ’– If "config" is "true", the definition represents configuration, and data nodes representing configuration are part of configuration datastores ๐ŸŽ€

If "config" is "false", the definition represents state data, and data nodes representing state data are NOT part of configuration datastores ๐Ÿฆ‹

If "config" is not 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 specify a "config" statement, the default is "true" ๐Ÿ’…

If a node has "config" set to "false", no node underneath it can have "config" set to "true" ๐Ÿ™ˆ That's just how it goes, no cap.

7.21.2. The "status" Statement โœจ

The "status" statement takes one of the strings "current", "deprecated", or "obsolete" ๐Ÿซถ

  • "current" means the definition is current and valid ๐Ÿ’œ
  • "deprecated" means it's an obsolete definition, BUT it still permits new or continued implementation so it works with older and 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. Not me referencing something dead inside a live definition ๐Ÿ˜ญ

If a definition is "deprecated", it MUST NOT reference an "obsolete" definition within the same module ๐Ÿ”ฅ

For example, the following is illegal, and I'm lowkey shook by it ๐Ÿ’€:

  typedef my-type {
    status deprecated;
    type int32;
  }

  leaf my-leaf {
    status current;
    type my-type; // illegal, since my-type is deprecated
  }

7.21.3. The "description" Statement โœจ

The "description" statement takes a string that's a human-readable textual description of this definition ๐ŸŒธ The text is written in whatever language or languages the module developer picks. For interoperability though, it is RECOMMENDED to choose a language that's widely understood by the community of network admins who'll be using the module ๐Ÿงš

7.21.4. The "reference" Statement โœจ

The "reference" statement takes a string that's a human-readable cross-reference to an external document ๐Ÿฆ‹ That could be another module that defines related management information, or a document with extra info relevant to this definition ๐Ÿ“š

For example, a typedef for a "uri" data type could look like this ๐ŸŽ€:

  typedef uri {
    type string;
    reference
      "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax";
    ...
  }

7.21.5. The "when" Statement โœจ

The "when" statement makes its parent data definition statement conditional ๐Ÿ’– The node defined by that parent is only valid when the condition in the "when" statement is satisfied. The argument is an XPath expression (see Section 6.4), which formally specifies this condition. If the XPath expression conceptually evaluates to "true" for a particular instance, the node is valid. Otherwise, it's not ๐ŸŒˆ

A leaf that is a list key MUST NOT have a "when" statement. Don't even try it, bestie ๐Ÿ™…

If a key leaf is defined in a grouping that's used in a list, the "uses" statement MUST NOT have a "when" statement ๐Ÿ’…

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 "when" statement is a child of an "augment" statement, the context node is the augment's target node in the data tree, as long as the target node is a data node. Otherwise, the context node is the closest ancestor node to the target node that's also a data node. If no such node exists, the context node is the root node. The accessible tree is tentatively altered during processing of the XPath expression by removing all instances (if any) of the nodes added by the "augment" statement ๐Ÿฆ‹
  • If the "when" statement is a child of a "uses", "choice", or "case" statement, the context node is the closest ancestor node to the node with the "when" statement that's also a data node. If no such node exists, the context node is the root node. The accessible tree is tentatively altered during processing of the XPath expression by removing all instances (if any) of the nodes added by the "uses", "choice", or "case" statement ๐ŸŒธ
  • If the "when" statement is a child of any other data definition statement, the accessible tree is tentatively altered during processing of the XPath expression by replacing all instances of the data node for which the "when" statement is defined with a single dummy node. That dummy node has the same name, but no value and no children. If no such instance exists, the dummy node is tentatively created. The context node is this dummy node ๐Ÿงš

The result of the XPath expression is converted to a boolean value using the standard XPath rules ๐Ÿ”ฅ

If the XPath expression references any node that also has associated "when" statements, those "when" expressions MUST be evaluated first. There MUST NOT be any circular dependencies among "when" expressions. Circular dependencies are a hard no ๐Ÿ’€

Note that the XPath expression is conceptually evaluated. That means an implementation doesn't have to use an XPath evaluator in the server. The "when" statement can very well be implemented with specially written code ๐Ÿ’œ

8. Constraints โœจ

8.1. Constraints on Data ๐ŸŽ€

Several YANG statements define constraints on valid data. These constraints are enforced in different ways, depending on what type 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 valid state data tree.
  • If the constraint is defined on notification content, it MUST be true in any notification data tree.
  • If the constraint is defined on RPC or action input parameters, it MUST be true in an invocation of the RPC or action operation.
  • If the constraint is defined on RPC or action output parameters, it MUST be true in the RPC or action reply.

The following properties are true in all data trees ๐Ÿฆ‹

  • All leaf data values MUST match the type constraints for the leaf, including those defined in the type's "range", "length", and "pattern" properties.
  • All key leafs MUST be present for all list entries.
  • Nodes MUST be present for at most one case branch in all choices.
  • There MUST be no nodes tagged with "if-feature" present if the "if-feature" expression evaluates to "false" in the server.
  • There MUST be no nodes tagged with "when" present if the "when" condition evaluates to "false" in the data tree.

The following properties are true in a valid data tree ๐Ÿ’–

  • All "must" constraints MUST evaluate to "true".
  • All referential integrity constraints defined via the "path" statement MUST be satisfied.
  • All "unique" constraints on lists MUST be satisfied.
  • The "mandatory" constraint is enforced for leafs and choices, unless the node or any of its ancestors has a "when" condition or "if-feature" expression that evaluates to "false".
  • The "min-elements" and "max-elements" constraints are enforced for lists and leaf-lists, unless the node or any of its ancestors has a "when" condition or "if-feature" expression that evaluates to "false".

The running configuration datastore MUST always be valid ๐Ÿ‘‘

8.2. Configuration Data Modifications โœจ

  • If a request creates configuration data nodes under a choice, any existing nodes from other case branches in the data tree are deleted by the server ๐ŸŒธ
  • If a request modifies a configuration data node such that any node's "when" expression becomes false, then the node in the data tree with the "when" expression is deleted by the server ๐Ÿงš

8.3. NETCONF Constraint Enforcement Model ๐Ÿ”ฅ

For configuration data, there are three windows when constraints MUST be enforced:

  • during parsing of RPC payloads
  • during processing of the <edit-config> operation
  • during validation

Each of these scenarios is considered in the following sections ๐Ÿ’…

8.3.1. Payload Parsing ๐ŸŽ€

When content arrives in RPC payloads, it MUST be well-formed XML, following the hierarchy and content rules defined by the set of models the server implements ๐Ÿฆ‹

  • 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 (Section 7.5.4.2) and error-message (Section 7.5.4.1) 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" <error-tag> in the <rpc-error> ๐Ÿ”ฅ
  • If data for a node tagged with "if-feature" is present and the "if-feature" expression evaluates to "false" in the server, the server MUST reply with an "unknown-element" <error-tag> in the <rpc-error> ๐Ÿ’€
  • If data for a node tagged with "when" is 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 values 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" appear 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.
  • Modification requests for nodes tagged with "when", and the "when" condition evaluates to "false". In this case, the server MUST reply with an "unknown-element" <error-tag> in the <rpc-error> โœจ

8.3.3. Validation ๐ŸŒˆ

When datastore processing is complete, the final contents MUST obey all validation constraints ๐Ÿ’… This validation processing happens at differing 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", constraint enforcement is delayed until a <commit> or <validate> operation takes place ๐Ÿ‘‘

9. Built-In Types โœจ

okay so YANG comes with its own set of built-in types ๐ŸŽ€ kinda like the ones in lots of programming languages, but with a few differences because the management information model has its own special needs ๐Ÿ’…

You can also make new types that are derived from those built-ins or from other derived types. ๐Ÿฆ‹ Derived types can use subtyping to formally restrict the set of values they're allowed to hold. Iconic behavior, honestly ๐Ÿ‘‘

The different built-in types and their derived types allow different kinds of subtyping. You get length and regular expression restrictions on strings (Sections 9.4.4 and 9.4.5) and range restrictions on numeric types (Section 9.2.4) ๐ŸŒธ

The lexical representation of a value of a certain type is used in the XML encoding, and also when you're specifying default values and numerical ranges in YANG modules ๐Ÿงš

9.1. Canonical Representation

For most types there's one canonical representation of the type's values โœจ But some types allow multiple lexical representations of the same value. For example, the positive integer "17" can be written as "+17" or "17" ๐Ÿ™ˆ Implementations MUST support all lexical representations specified in this document. No skipping any!

When a server sends XML-encoded data, it MUST use the canonical form defined in this section ๐Ÿ’– Other encodings may introduce alternate representations, but note this: values in the data tree are conceptually stored in the canonical representation as defined here. So any XPath expression evaluations are done using the canonical form if the data type has one ๐Ÿ”ฅ If the data type does not have a canonical form, the format of the value MUST match the data type's lexical representation, but the exact format is implementation dependent ๐Ÿคทโ€โ™€๏ธ

Some types have a lexical representation that depends on the encoding (like the XML context they show up in). 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:

  • int8 represents integer values between -128 and 127, inclusively.
  • int16 represents integer values between -32768 and 32767, inclusively.
  • int32 represents integer values between -2147483648 and 2147483647, inclusively.
  • int64 represents integer values between -9223372036854775808 and 9223372036854775807, inclusively.
  • uint8 represents integer values between 0 and 255, inclusively.
  • uint16 represents integer values between 0 and 65535, inclusively.
  • uint32 represents integer values between 0 and 4294967295, inclusively.
  • uint64 represents integer values between 0 and 18446744073709551615, inclusively.

9.2.1. Lexical Representation

An integer value is lexically written as an optional sign (+ or -), followed by a sequence of decimal digits. If you don't include a sign, + is assumed ๐Ÿ’…

For convenience, when you're specifying a default value for an integer in a YANG module, you can use an alternative lexical representation that writes the value in hexadecimal or octal notation ๐Ÿฆ‹ The hexadecimal notation is an optional sign (+ or -), followed by the characters 0x, followed by a number of hexadecimal digits, where the letters can be uppercase or lowercase. The octal notation is an optional sign (+ or -), followed by the character 0, followed by a number of octal digits.

Heads up, this one is a trap ๐Ÿšจ If a default value in a YANG module has a leading zero (0), it gets interpreted as an octal number. In the XML encoding, though, an integer is always interpreted as a decimal number, and leading zeros are allowed ๐Ÿ˜ญ

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 doesn't include the sign + ๐Ÿ’– Leading zeros are prohibited. The value zero is written as 0 โœจ

9.2.3. Restrictions

All integer types can be restricted with the "range" statement (Section 9.2.4) ๐ŸŽ€

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 them ๐ŸŒˆ

A range is either an explicit value, or a lower-inclusive bound, two consecutive dots .., and an upper-inclusive bound. You can give multiple values or ranges, separated by |. If multiple values or ranges are given, they all MUST be disjoint and MUST be in ascending order. If a range restriction gets applied to a type that's already range-restricted, the new restriction MUST be equally limiting or more limiting. That means raising the lower bounds, reducing the upper bounds, removing explicit values or ranges, or splitting ranges into multiple ranges with intermediate gaps ๐Ÿ”ฅ Each explicit value and range boundary value given in the range expression MUST match the type being restricted, or be one of the special values "min" or "max". "min" and "max" mean the minimum and maximum values accepted for the type being restricted, respectively ๐Ÿ‘‘

The range expression syntax is formally defined by the rule "range-arg" in Section 14 ๐Ÿฆ‹

9.2.4.1. The range's Substatements

                 +---------------+---------+-------------+
                 | substatement  | section | cardinality |
                 +---------------+---------+-------------+
                 | description   | 7.21.3  | 0..1        |
                 | error-app-tag | 7.5.4.2 | 0..1        |
                 | error-message | 7.5.4.1 | 0..1        |
                 | reference     | 7.21.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 built-in 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, i.e., expressible as "i x 10^-n", where i is an integer64 and n is an integer between 1 and 18, inclusively ๐Ÿงฎ

9.3.1. Lexical Representation

A decimal64 value is lexically written as an optional sign ("+" or "-"), followed by a sequence of decimal digits, optionally followed by a period ('.') as a decimal indicator and another sequence of decimal digits. If no sign is specified, "+" is assumed ๐Ÿ’œ

9.3.2. Canonical Form

The canonical form of a positive decimal64 value doesn't include the sign "+". The decimal point is required. Leading and trailing zeros are prohibited, subject to the rule that there MUST be at least one digit before and after the decimal point. The value 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. It MUST be present if the type is "decimal64". It takes an integer between 1 and 18, inclusively, as its argument. It controls the size of the minimum difference between values of a decimal64 type by restricting the value space to numbers expressible as "i x 10^-n", where n is the fraction-digits argument ๐Ÿ”ฅ

The table below lists the minimum and maximum values for each fraction-digit 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 ๐Ÿ’– Legal characters are the Unicode and ISO/IEC 10646 [ISO.10646] characters, including tab, carriage return, and line feed, but excluding the other C0 control characters, the surrogate blocks, and the noncharacters. The string syntax is formally defined by the rule "yang-string" in Section 14 โœจ

9.4.1. Lexical Representation

A string value is lexically represented as character data in the XML encoding ๐ŸŒˆ

9.4.2. Canonical Form

The canonical form is the same as the lexical representation. No Unicode normalization of string values is performed ๐Ÿงš

9.4.3. Restrictions

A string can be restricted with the "length" (Section 9.4.4) and "pattern" (Section 9.4.5) statements ๐Ÿฆ‹

9.4.4. The "length" Statement

okay so this one is the length statement ๐Ÿ“โœจ it's an optional substatement to "type" and it takes a length expression string ๐Ÿ’…. it's used to restrict the built-in types "string" and "binary" (or types derived from them) ๐ŸŽ€

a length statement restricts the number of Unicode characters in the string ๐ŸŒธ

a length range is either an explicit value, or a lower bound, then two consecutive dots "..", then an upper bound ๐Ÿฆ‹. you can list multiple 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 MUST be in ascending order ๐Ÿ’…. if you restrict a type that's already length-restricted, the new restriction MUST be equally limiting or more limiting ๐Ÿ”ฅ, meaning raising the lower bounds, reducing the upper bounds, removing explicit length values or ranges, or splitting ranges into multiple ranges with intermediate gaps ๐Ÿงš. a length value is a non-negative integer or one of the special values "min" or "max" ๐Ÿ’œ, where "min" and "max" mean the minimum and maximum lengths 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 14 ๐Ÿ“š๐Ÿซถ

9.4.4.1. The length's Substatements

                 +---------------+---------+-------------+
                 | substatement  | section | cardinality |
                 +---------------+---------+-------------+
                 | description   | 7.21.3  | 0..1        |
                 | error-app-tag | 7.5.4.2 | 0..1        |
                 | error-message | 7.5.4.1 | 0..1        |
                 | reference     | 7.21.4  | 0..1        |
                 +---------------+---------+-------------+

9.4.5. The "pattern" Statement

the "pattern" statement is an optional substatement to "type" ๐ŸŒˆ and it takes a regular expression string, 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 you apply a pattern restriction to a type that's already pattern-restricted, values must match all patterns in the base type as well as the new patterns ๐Ÿ’…โœจ

9.4.5.1. The pattern's Substatements

                 +---------------+---------+-------------+
                 | substatement  | section | cardinality |
                 +---------------+---------+-------------+
                 | description   | 7.21.3  | 0..1        |
                 | error-app-tag | 7.5.4.2 | 0..1        |
                 | error-message | 7.5.4.1 | 0..1        |
                 | modifier      | 9.4.6   | 0..1        |
                 | reference     | 7.21.4  | 0..1        |
                 +---------------+---------+-------------+

9.4.6. The "modifier" Statement

the "modifier" statement is an optional substatement to "pattern" ๐ŸŽ€ and it takes the string "invert-match" ๐Ÿ™ƒ

if a pattern has the "invert-match" modifier present, the type is restricted to values that do NOT match the pattern ๐Ÿ’€

9.4.7. Usage Example

with the following typedef:

  typedef my-base-str-type {
    type string {
      length "1..255";
    }
  }

this refinement is legal:

  type my-base-str-type {
    // legal length refinement
    length "11 | 42..max"; // 11 | 42..255
  }

and this one is illegal:

  type my-base-str-type {
    // illegal length refinement
    length "1..999";
  }

with the following type:

  type string {
    length "0..4";
    pattern "[0-9a-fA-F]*";
  }

these strings match:

  AB          // legal
  9A00        // legal

and these strings do not match:

  00ABAB      // illegal, too long
  xx00        // illegal, bad characters

with the following type:

  type string {
    length "1..max";
    pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
    pattern '[xX][mM][lL].*' {
      modifier invert-match;
    }
  }

this string matches:

  enabled     // legal

and these strings do not match:

  10-mbit     // illegal, starts with a number
  xml-element // illegal, starts with illegal sequence

9.5. The boolean Built-In Type ๐ŸŒท

the boolean built-in type represents a boolean value ๐Ÿ’–

9.5.1. Lexical Representation

the lexical representation of a boolean value is a string with a value of "true" or "false" ๐Ÿ’…. these values MUST be in lowercase ๐Ÿ™ˆ

9.5.2. Canonical Form

the canonical form is the same as the lexical representation ๐Ÿซถ

9.5.3. Restrictions

a boolean cannot be restricted ๐Ÿšซ

9.6. The enumeration Built-In Type โœจ

the enumeration built-in type represents values from a set of assigned names ๐Ÿ“

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 ๐Ÿ’œ

9.6.3. Restrictions

an enumeration can be restricted with one or more "enum" (Section 9.6.4) statements, which enumerate a subset of the values for the base type ๐Ÿฆ‹

9.6.4. The "enum" Statement

the "enum" statement is a substatement to "type" and it MUST be present if the type is "enumeration" ๐Ÿ‘‘. it's repeatedly used to specify each assigned name of an enumeration type ๐Ÿ’…. it takes a string that is the assigned name. the string MUST NOT be zero-length and MUST NOT have any leading or trailing whitespace characters (any Unicode character with the "White_Space" property) ๐Ÿ™ˆ. 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 ๐Ÿ”ฅ

when an existing enumeration type is restricted, the set of assigned names in the new type MUST be a subset of the base type's set of assigned names. the value of such an assigned name MUST NOT be changed ๐Ÿ’–

9.6.4.1. The enum's Substatements

                 +--------------+---------+-------------+
                 | substatement | section | cardinality |
                 +--------------+---------+-------------+
                 | description  | 7.21.3  | 0..1        |
                 | if-feature   | 7.20.2  | 0..n        |
                 | reference    | 7.21.4  | 0..1        |
                 | status       | 7.21.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 associate an integer value with 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 ๐Ÿ’œ

if a value isn't specified, one is automatically assigned ๐Ÿฆ‹. 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 (so, the highest enum value, implicit or explicit, prior to the current "enum" substatement in the parent "type" statement) ๐ŸŒธ

note that having an "if-feature" statement in an "enum" statement does NOT change the automatically assigned value ๐Ÿ™ƒ

if the current highest value is equal to 2147483647, then an enum value MUST be specified for "enum" substatements following the one with the current highest value ๐Ÿ’€

when an existing enumeration type is restricted, the "value" statement MUST either have the same value as in the base type or not be present, in which case the value is the same as in the base type ๐Ÿซถ

9.6.5. Usage Example

  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>

with the following typedef:

  typedef my-base-enumeration-type {
    type enumeration {
      enum white {
        value 1;
      }
      enum yellow {
        value 2;
      }
      enum red {
        value 3;
      }
    }
  }

this refinement is legal:

  type my-base-enumeration-type {
    // legal enum refinement
    enum yellow;
    enum red {
      value 3;
    }
  }

and this one is illegal:

  type my-base-enumeration-type {
    // illegal enum refinement
    enum yellow {
      value 4; // illegal value change
    }
    enum black; // illegal addition of new name
  }

this example shows how an "enum" can be tagged with "if-feature", so the value is only legal on servers that advertise the matching feature ๐Ÿงšโœจ

  type enumeration {
    enum tcp;
    enum ssh {
      if-feature ssh;
    }
    enum tls {
      if-feature tls;
    }
  }

9.7. The bits Built-In Type ๐Ÿฆ‹

the bits built-in type represents a bit set ๐ŸŽ€. that is, a bits value is a set of flags identified by small integer position numbers starting at 0. each bit number has an assigned name ๐Ÿ’–

when an existing bits type is restricted, the set of assigned names in the new type MUST be a subset of the base type's set of assigned names. the bit position of such an assigned name MUST NOT be changed ๐Ÿ™ˆ

9.7.1. Restrictions

a bits type can be restricted with the "bit" (Section 9.7.4) statement ๐Ÿ’…

9.7.2. Lexical Representation

the lexical representation of the bits type is a space-separated list of the names of the bits that are set. a zero-length 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 "type" and it MUST be present if the type is "bits" ๐Ÿ‘‘. it's repeatedly used to specify each assigned named bit of a bits type ๐ŸŒˆ. it takes a string that is the assigned name of the bit, and 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

                 +--------------+---------+-------------+
                 | substatement | section | cardinality |
                 +--------------+---------+-------------+
                 | description  | 7.21.3  | 0..1        |
                 | if-feature   | 7.20.2  | 0..n        |
                 | position     | 9.7.4.2 | 0..1        |
                 | reference    | 7.21.4  | 0..1        |
                 | status       | 7.21.2  | 0..1        |
                 +--------------+---------+-------------+

9.7.4.2. The "position" Statement ๐ŸŽ€

okay so the position statement is optional, bestie ๐Ÿซถ It takes one argument: a non-negative integer that says where the bit sits inside a hypothetical bit field. The position value MUST be in the range 0 to 4294967295, and it MUST be unique within the bits type. no repeats allowed ๐Ÿ™…โ€โ™€๏ธ

If you don't give a bit a position, one gets auto-assigned for you โœจ If the bit substatement is the very first one defined, its assigned value is zero (0). Otherwise, it gets one more than the current highest bit position, whether that position was implicit or explicit, from before the current bit substatement in the parent type statement. the math is giving clean ๐Ÿ’…

tea time: the presence of an if-feature statement inside a bit statement does NOT change the automatically assigned position ๐Ÿต

Now the edge case ๐Ÿ‘€ If the current highest bit position value is equal to 4294967295, then a position value MUST be specified for the bit substatements that come after the one holding that highest position. no auto-assign past the ceiling, periodt ๐Ÿ›‘

When an existing bits type gets restricted, the position statement MUST either have the same value as in the base type, or not be present at all. If it's not present, the value is the same as in the base type. no sneaky renumbering ๐Ÿฆ‹

9.7.5. Usage Example ๐ŸŒธ

Say we have the following typedef and leaf:

typedef mybits-type {
  type bits {
    bit disable-nagle {
      position 0;
    }
    bit auto-sense-speed {
      position 1;
    }
    bit ten-mb-only {
      position 2;
    }
  }
}

leaf mybits {
  type mybits-type;
  default "auto-sense-speed";
}

The lexical representation of this leaf with the bit values disable-nagle and ten-mb-only set would be:

<mybits>disable-nagle ten-mb-only</mybits>

This refinement is legal, so it's a yes from me ๐Ÿ’–

type mybits-type {
  // legal bit refinement
  bit disable-nagle {
    position 0;
  }
  bit auto-sense-speed {
    position 1;
  }
}

But this one is illegal, so absolutely not ๐Ÿ™…โ€โ™€๏ธ๐Ÿ’€

type mybits-type {
  // illegal bit refinement
  bit disable-nagle {
    position 2; // illegal position change
  }
  bit hundred-mb-only; // illegal addition of new name
}

9.8. The binary Built-In Type ๐Ÿงƒ

The binary built-in type represents any binary data, which is a fancy way of saying a sequence of octets. literally anything goes ๐ŸŒˆ

9.8.1. Restrictions

A binary type can be restricted with the length (Section 9.4.4) statement. The length of a binary value is the number of octets it contains. simple, clean, iconic โœจ

9.8.2. Lexical Representation

Binary values are encoded with the base64 encoding scheme (see Section 4 in [RFC4648]). base64 era forever ๐Ÿซถ

9.8.3. Canonical Form

The canonical form of a binary value follows the rules of "Base 64 Encoding" in [RFC4648]. same rulebook, no drama ๐Ÿ“–

9.9. The leafref Built-In Type ๐Ÿ”—

okay the leafref built-in type is restricted to the value space of some leaf or leaf-list node in the schema tree. It can also be further restricted by corresponding instance nodes in the data tree, so it's basically a pointer with receipts ๐Ÿงพ The path substatement (Section 9.9.2) is what identifies the referred leaf or leaf-list node in the schema tree. The value space of the referring node is the value space of the referred node. it's giving twin energy ๐Ÿ‘ฏโ€โ™€๏ธ

If the require-instance property (Section 9.9.3) is true, there MUST exist a node in the data tree, or a node with a default value in use (see Sections 7.6.1 and 7.7.2), of the referred schema tree leaf or leaf-list node with the same value as the leafref value in a valid data tree. no ghosting the referred node ๐Ÿ‘ป

If the referring node represents configuration data and the require-instance property (Section 9.9.3) is true, the referred node MUST also represent configuration. same vibe, same category ๐ŸŽ€

There MUST NOT be any circular chains of leafrefs. no loops, we don't do loops here ๐Ÿ”๐Ÿšซ

If the leaf that the leafref refers to is conditional based on one or more features (see Section 7.20.2), then the leaf with the leafref type MUST also be conditional based on at least the same set of features. if the bestie is feature-gated, you gate too ๐Ÿšชโœจ

9.9.1. Restrictions

A leafref can be restricted with the require-instance statement (Section 9.9.3). ๐Ÿ’…

9.9.2. The "path" Statement ๐Ÿ›ค๏ธ

The path statement is a substatement to the type statement, and it MUST be present if the type is leafref. It takes as an argument a string that MUST refer to a leaf or leaf-list node. no path, no leafref, that's the whole deal ๐Ÿซถ

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 of 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 14. the grammar lives there, go check it ๐Ÿ“œ

Predicates only show up when more than one key reference is needed to uniquely identify a leaf instance. That happens if a list has multiple keys, or if you need a reference to a leaf that isn't the key in a list. In these cases, you usually specify multiple leafrefs, and predicates are what tie them together ๐Ÿงต very main character energy.

The path expression evaluates to a node set made of zero, one, or more nodes. If the require-instance property is true, this node set MUST be non-empty. no empty sets when it matters ๐Ÿ™…โ€โ™€๏ธ

The path XPath expression is conceptually evaluated in the following context, in addition to the definition in Section 6.4.1:

  • If the path statement is defined within a typedef, the context node is the leaf or leaf-list node in the data tree that references the typedef.
  • Otherwise, the context node is the node in the data tree for which the path statement is defined.

9.9.3. The "require-instance" Statement ๐Ÿ™‹โ€โ™€๏ธ

The require-instance statement is a substatement to the type statement, and it MAY be present if the type is instance-identifier or leafref. It takes as an argument the string "true" or "false". If this statement is not present, it defaults to "true". the default is the strict era ๐Ÿ’–

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. no existence, no valid data ๐Ÿ’€

If require-instance is "false", it means the instance being referred to MAY exist in valid data. chill mode activated ๐Ÿ˜Œ

9.9.4. Lexical Representation

A leafref value is lexically represented the same way as the leaf it references represents its value. copy the vibe of your source โœจ

9.9.5. Canonical Form

The canonical form of a leafref is the same as the canonical form of the leaf it references. same outfit, same energy ๐Ÿ‘—

9.9.6. Usage Example ๐ŸŒท

Given the following 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;
    }
  }
}

the following leafref refers to an existing interface:

leaf mgmt-interface {
  type leafref {
    path "../interface/name";
  }
}

Here's an example of a corresponding XML snippet:

<interface>
  <name>eth0</name>
</interface>
<interface>
  <name>lo</name>
</interface>

<mgmt-interface>eth0</mgmt-interface>

The following leafrefs refer to an existing address of an interface:

container default-address {
  leaf ifname {
    type leafref {
      path "../../interface/name";
    }
  }
  leaf address {
    type leafref {
      path "../../interface[name = current()/../ifname]"
         + "/address/ip";
    }
  }
}

Here's an example of a corresponding XML snippet:

<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>

The following list uses a leafref for one of its keys. It's similar to a foreign key in a relational database, so if you know SQL you already get it ๐Ÿง 

list packet-filter {
  key "if-name filter-id";
  leaf if-name {
    type leafref {
      path "/interface/name";
    }
  }
  leaf filter-id {
    type uint32;
  }
  ...
}

Here's an example of a corresponding XML snippet:

<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>

The following notification defines two leafrefs that refer to an 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";
    }
  }
}

Here's an example of a corresponding XML notification:

<notification
  xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
  <eventTime>2008-04-01T00:01:00Z</eventTime>
  <link-failure xmlns="urn:example:system">
    <if-name>eth0</if-name>
    <admin-status>up</admin-status>
  </link-failure>
</notification>

9.10. The identityref Built-In Type ๐Ÿชช

The identityref built-in type is used to reference an existing identity (see Section 7.18). it's the ID badge of the YANG world ๐Ÿชชโœจ

9.10.1. Restrictions

An identityref cannot be restricted. no edits, it's already perfect ๐Ÿ’…

9.10.2. The identityref's "base" Statement

The base statement is a substatement to the type statement, and it MUST be present at least once if the type is identityref. The argument is the name of an identity, as defined by an identity statement. If a prefix is present on the identity name, it refers to an identity defined in the module that was imported with that prefix. Otherwise, an identity with the matching name MUST be defined in the current module or an included submodule. the name has to live somewhere real ๐Ÿ 

Valid values for an identityref are any identities derived from all of the identityref's base identities. On a particular server, the valid values are further restricted to the set of identities defined in the modules implemented by the server. so the list you can pick from depends on what the server actually runs ๐ŸŒธ

9.10.3. Lexical Representation ๐ŸŽ€

okay so an identityref gets written as the identity's qualified name, the way [XML-NAMES] defines it ๐Ÿซถ. If there's no prefix, the namespace is whatever default namespace is active on the element that holds the identityref value.

When you give an identityref a default with the "default" statement, the identity name in that default MAY have a prefix ๐Ÿ’…. If it does, the prefix points at an identity from the module imported under that prefix. If the identity lives in the current module or one of its submodules, the prefix is the current module's own prefix. If there's no prefix at all, an identity with that exact name MUST be defined in the current module or one of its submodules ๐Ÿ”ฅ.

In a "must" or "when" XPath expression, the string value of an identityref node is the referred identity's qualified name with the prefix included โœจ. If that identity comes from an imported module, the prefix is the one from the matching "import" statement. Otherwise, the prefix is the current module's prefix.

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 ๐Ÿงš

so, with the identity definitions in Section 7.18.3 and this module:

  module example-my-crypto {
    yang-version 1.1;
    namespace "urn:example:my-crypto";
    prefix mc;

    import "example-crypto-base" {
      prefix "crypto";
    }

    identity aes {
      base "crypto:crypto-alg";
    }

    leaf crypto {
      type identityref {
        base "crypto:crypto-alg";
      }
    }

    container aes-parameters {
      when "../crypto = 'mc:aes'";
      ...
    }
  }

here's how the leaf "crypto" can be encoded when its value is the "des3" identity defined in the "des" module ๐Ÿ’–:

  <crypto xmlns:des="urn:example:des">des:des3</crypto>

any prefixes in the encoding are local to each instance encoding ๐Ÿ‘‘. That means the same identityref can be encoded differently by different implementations. For example, this one encodes the same leaf as above:

  <crypto xmlns:x="urn:example:des">x:des3</crypto>

if the "crypto" leaf is "aes" from the "example-my-crypto" module instead, it can be encoded as:

  <crypto xmlns:mc="urn:example: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 is a leaf with no value at all ๐ŸŒˆ. It just tells you something by being there or not being there. 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 ๐ŸŽ€

with this leaf:

  leaf enable-qos {
    type empty;
  }

if the leaf exists, a valid encoding looks like this:

  <enable-qos/>

9.12. The union Built-In Type ๐Ÿ’œ

the union built-in type holds a value that matches one of its member types ๐ŸŒธ.

when the type is "union", the "type" statement (Section 7.4) MUST be present ๐Ÿ”ฅ. It gets used over and over, once for each member type of the union, and each one takes a string that names a member type โœจ.

a member type can be any built-in or derived type ๐Ÿงš.

when you generate an XML encoding, the value gets encoded using the rules of whichever member type it belongs to ๐Ÿซถ. When you interpret an XML encoding, the value gets checked against each member type in turn, in the order they appear in the "type" statement, until one matches ๐Ÿ‘‘. The type that matches becomes the value's type for the node that was checked, and the encoding gets read using that type's rules.

any default value or "units" property set on the member types does not get inherited by the union type ๐Ÿ™ˆ.

9.12.1. Restrictions ๐Ÿ’…

a union can't be restricted, but each member type can be restricted, following the rules in Section 9 ๐Ÿฆ‹.

9.12.2. Lexical Representation ๐ŸŒˆ

the lexical representation of a union is a value that matches the representation of any one of its member types ๐Ÿ’–.

9.12.3. Canonical Form ๐Ÿ“

the canonical form of a union value is the same as the canonical form of its member type ๐Ÿซถ.

9.12.4. Usage Example โญ

here's a union of an int32 and an enumeration:

  type union {
    type int32;
    type enumeration {
      enum "unbounded";
    }
  }
careful bestie ๐Ÿ˜ญ: when a member type is a leafref and its "require-instance" property (Section 9.9.3) is "true", you have to be extra careful. If a leaf of that type points at an existing instance, the leaf's value must be revalidated when that target instance gets deleted ๐Ÿ’€.

for example, say you have these definitions:

  list filter {
    key name;
    leaf name {
      type string;
    }
    ...
  }

  leaf outbound-filter {
    type union {
      type leafref {
        path "/filter/name";
      }
      type enumeration {
        enum default-filter;
      }
    }
  }

now assume there's a filter entry named "http", and the outbound-filter leaf holds this value:

  <filter>
    <name>http</name>
  </filter>
  <outbound-filter>http</outbound-filter>

if the "http" filter entry gets removed, the outbound-filter leaf's value no longer matches the leafref, so the next member type gets checked ๐Ÿ˜ฑ. The current value ("http") doesn't match the enumeration either, so the resulting configuration is invalid ๐Ÿฅ€.

if the second member type had been a "string" instead of an enumeration, the current value would have matched, and the resulting configuration would have been valid ๐ŸŽ‰.

9.13. The instance-identifier Built-In Type ๐Ÿ”ฎ

the instance-identifier built-in type is how you uniquely identify one particular instance node in the data tree โœจ.

the syntax for an instance-identifier is a subset of the XPath abbreviated syntax, formally defined by the rule "instance-identifier" in Section 14 ๐Ÿ“œ. It's used to uniquely identify a node in the data tree. Predicates only ever get used to specify the values of key nodes for list entries, a value of a leaf-list entry, or a positional index for a list without keys ๐Ÿงฉ. To identify list entries that have keys, each predicate is one equality test per key, and each key MUST have a corresponding predicate ๐Ÿ”‘. If a key is of type "empty", it gets represented as a zero-length string ("") ๐Ÿซง.

if the leaf with the instance-identifier type holds configuration data and the "require-instance" property (Section 9.9.3) is "true", the node it refers to MUST also represent configuration ๐Ÿ”’. This puts a constraint on what counts as valid data ๐Ÿ’…. All such leaf nodes MUST reference existing nodes, or leaf or leaf-list nodes with their default value in use (see Sections 7.6.1 and 7.7.2), for the data to be valid ๐ŸŒŸ. This constraint gets enforced according to the rules in Section 8 ๐Ÿ›ก๏ธ.

the "instance-identifier" XPath expression is conceptually evaluated in the following context, on top of the definition in Section 6.4.1 ๐ŸŒธ:

  • The context node is the root node in the accessible tree.

9.13.1. Restrictions ๐Ÿ’…

an instance-identifier can be restricted with the "require-instance" statement (Section 9.9.3) ๐Ÿฆ‹.

9.13.2. 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 these prefixes MUST be declared in the XML namespace scope in the instance-identifier's XML element ๐Ÿ“ฆ.

any prefixes used in the encoding are local to each instance encoding ๐Ÿซง. That means the same instance-identifier can be encoded differently by different implementations ๐ŸŒˆ.

9.13.3. 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.13.4. Usage Example ๐Ÿงš

here are some instance identifiers ๐Ÿ’–:

  /* 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 list entry where the second
     key ("enabled") is of type "empty" */
  /ex:system/ex:service[ex:name='foo'][ex:enabled='']

  /* 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. XPath Functions โœจ

this document defines two generic XPath functions and five YANG type-specific XPath functions ๐ŸŽ€. The function signatures are written in the syntax used in [XPATH] ๐Ÿ“.

10.1. Function for Node Sets ๐ŸŒธ

10.1.1. current() ๐Ÿ’…

  node-set current()

the current() function takes no input parameters and returns a node set with the initial context node as its only member ๐Ÿซถ.

10.1.1.1. Usage Example ๐Ÿงš

with this list:

  list interface {
    key "name";
    ...
    leaf enabled {
      type boolean;
    }
    ...
  }

the following leaf has a "must" expression that makes sure the referred interface is enabled ๐Ÿ”ฅ:

  leaf outgoing-interface {
    type leafref {
      path "/interface/name";
    }
    must '/interface[name=current()]/enabled = "true"';
  }

10.2. Function for Strings ๐Ÿฆ‹

10.2.1. re-match() ๐ŸŒˆ

  boolean re-match(string subject, string pattern)

the re-match() function returns "true" if the "subject" string matches the regular expression "pattern", and "false" otherwise ๐Ÿ’–.

the re-match() function checks whether a string matches a given regular expression. The regular expressions used are the XML Schema regular expressions [XSD-TYPES] ๐Ÿ“œ. Note that this includes implicit anchoring of the regular expression at the head and tail ๐Ÿ™ˆ.

10.2.1.1. Usage Example ๐Ÿ“

the expression:

  re-match("1.22.333", "\d{1,3}\.\d{1,3}\.\d{1,3}")

returns "true" ๐ŸŽ‰.

to count all logical interfaces called eth0.<number>, do:

  count(/interface[re-match(name, "eth0\.\d+")])

10.3. Function for the YANG Types "leafref" and "instance-identifier" ๐Ÿ‘‘

10.3.1. deref() ๐Ÿซถ

  node-set deref(node-set nodes)

the deref() function follows the reference defined by the first node in document order in the argument "nodes" and returns the nodes it refers to ๐Ÿ”.

if the first argument node is of type "instance-identifier", the function returns a node set containing the single node that the instance identifier refers to, if that node exists. If no such node exists, an empty node set is returned ๐Ÿซง.

if the first argument node is of type "leafref", the function returns a node set containing the nodes that the leafref refers to ๐Ÿงฉ. Specifically, this set contains the nodes selected by the leafref's "path" statement (Section 9.9.2) that have the same value as the first argument node ๐Ÿ’œ.

if the first argument node is of any other type, an empty node set is returned ๐Ÿฅบ.

10.3.1.1. Usage Example

Okay this example is literally the perfect little love story of leafrefs and deref() ๐Ÿ’•

list interface {
  key "name type";
  leaf name { ... }
  leaf type { ... }
  leaf enabled {
    type boolean;
  }
  ...
}

container mgmt-interface {
  leaf name {
    type leafref {
      path "/interface/name";
    }
  }
  leaf type {
    type leafref {
      path "/interface[name=current()/../name]/type";
    }
    must 'deref(.)/../enabled = "true"' {
      error-message
        "The management interface cannot be disabled.";
    }
  }
}

10.4. Functions for the YANG Type "identityref" ๐Ÿฆ‹

10.4.1. derived-from()

boolean derived-from(node-set nodes, string identity)

Okay so derived-from() is giving family tree vibes ๐ŸŒณ It returns "true" if any node in the argument "nodes" is a node of type "identityref" and its value is an identity that is derived from (see Section 7.18.2) the identity "identity". Otherwise, it returns "false" ๐Ÿ’…

The parameter "identity" is a string matching the rule "identifier-ref" in Section 14. If a prefix is present on the identity, 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. If no prefix is present, the identity refers to an identity defined in the current module or an included submodule. โœจ

10.4.1.1. Usage Example

module example-interface {
  yang-version 1.1;

  ...
  identity interface-type;

  identity ethernet {
    base interface-type;
  }

  identity fast-ethernet {
    base ethernet;
  }

  identity gigabit-ethernet {
    base ethernet;
  }

  list interface {
    key name;
    ...
    leaf type {
      type identityref {
        base interface-type;
      }
    }
    ...
  }

  augment "/interface" {
    when 'derived-from(type, "exif:ethernet")';
    // generic Ethernet definitions here
  }
  ...
}

10.4.2. derived-from-or-self()

boolean derived-from-or-self(node-set nodes, string identity)

derived-from-or-self() is the slightly more generous cousin ๐Ÿซถ It returns "true" if any node in the argument "nodes" is a node of type "identityref" and its value is an identity that is equal to or derived from (see Section 7.18.2) the identity "identity". Otherwise, it returns "false" ๐ŸŒธ

The parameter "identity" is a string matching the rule "identifier-ref" in Section 14. If a prefix is present on the identity, 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. If no prefix is present, the identity refers to an identity defined in the current module or an included submodule. ๐Ÿ’œ

10.4.2.1. Usage Example

The module defined in Section 10.4.1.1 might also have:

augment "/interface" {
  when 'derived-from-or-self(type, "exif:fast-ethernet");
  // Fast-Ethernet-specific definitions here
}

10.5. Function for the YANG Type "enumeration" ๐ŸŽ€

10.5.1. enum-value()

number enum-value(node-set nodes)

So enum-value() checks whether the first node in document order in the argument "nodes" is a node of type "enumeration", and if it is, it hands you back that enum's integer value ๐Ÿ”ข If the "nodes" node set is empty, or if the first node in "nodes" is not of type "enumeration", it returns NaN (not a number). Yikes, but make it make sense ๐Ÿ’€

10.5.1.1. Usage Example

With this data model:

list alarm {
  ...
  leaf severity {
    type enumeration {
      enum cleared {
        value 1;
      }
      enum indeterminate {
        value 2;
      }
      enum minor {
        value 3;
      }
      enum warning {
        value 4;
      }
      enum major {
        value 5;
      }
      enum critical {
        value 6;
      }
    }
  }
}

the following XPath expression selects only alarms that are of severity "major" or higher ๐Ÿšจ

/alarm[enum-value(severity) >= 5]

10.6. Function for the YANG Type "bits" ๐Ÿ“

10.6.1. bit-is-set()

boolean bit-is-set(node-set nodes, string bit-name)

bit-is-set() returns "true" if the first node in document order in the argument "nodes" is a node of type "bits" and its value has the bit "bit-name" set. Otherwise, it returns "false" ๐Ÿงš

10.6.1.1. Usage Example

If an interface has this leaf:

leaf flags {
  type bits {
    bit UP;
    bit PROMISCUOUS
    bit DISABLED;
  }
 }

the following XPath expression can be used to select all interfaces with the UP flag set ๐Ÿ™Œ

/interface[bit-is-set(flags, "UP")]

11. Updating a Module ๐Ÿ”„

As experience is gained with a module, it may be desirable to revise that module. However, changes to published modules are not allowed if they have any potential to cause interoperability problems between a client using an original specification and a server using an updated specification. So basically, glow up carefully ๐Ÿซ 

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 metadata statements, including the "organization" and "contact" statements (Sections 7.1.7 and 7.1.8). Paperwork, but make it cute ๐Ÿ“

Note that definitions contained in a module are available to be imported by any other module and are referenced in "import" statements via the module name. Thus, a module name MUST NOT be changed. Furthermore, the "namespace" statement MUST NOT be changed, since all XML elements are qualified by the namespace. ๐Ÿ™…โ€โ™€๏ธ

Obsolete definitions MUST NOT be removed from published modules, since their identifiers may still be referenced by other modules. Ghosting them is not an option, bestie ๐Ÿ‘ป

A definition in a published module may be revised in any of the following ways:

  • An "enumeration" type may have new enums added, provided the old enums's values do not change. Note that inserting a new enum before an existing enum or reordering existing enums will result in new values for the existing enums, unless they have explicit values assigned to them. ๐Ÿ”ข
  • A "bits" type may have new bits added, provided the old bit positions do not change. Note that inserting a new bit before an existing bit or reordering existing bits will result in new positions for the existing bits, unless they have explicit positions assigned to them. ๐ŸŽฏ
  • A "range", "length", or "pattern" statement may expand the allowed value space. ๐ŸŒˆ
  • A "default" statement may be added to a leaf that does not have a default value (either directly or indirectly through its type). ๐Ÿซง
  • A "units" statement may be added. ๐Ÿ“
  • A "reference" statement may be added or updated. ๐Ÿ“š
  • A "must" statement may be removed or its constraint relaxed. ๐Ÿ’…
  • A "when" statement may be removed or its constraint relaxed. ๐ŸŒธ
  • A "mandatory" statement may be removed or changed from "true" to "false". ๐Ÿ™
  • A "min-elements" statement may be removed, or changed to require fewer elements. ๐Ÿชถ
  • A "max-elements" statement may be removed, or changed to allow more elements. ๐ŸŽˆ
  • A "description" statement may be added or changed without changing the semantics of the definition. ๐Ÿ’ฌ
  • A "base" statement may be added to an "identity" statement. ๐Ÿงฉ
  • A "base" statement may be removed from an "identityref" type, provided there is at least one "base" statement left. ๐Ÿช„
  • 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) 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-feature" statement that refers to a new feature). ๐Ÿฐ
  • A new "case" statement may be added. ๐ŸŽญ
  • A node that represented state data may be changed to represent configuration, provided it is not mandatory (Section 3). ๐Ÿ”
  • An "if-feature" statement may be removed, provided its node is not mandatory (Section 3). ๐Ÿ—๏ธ
  • A "status" statement may be added, or changed from "current" to "deprecated" or "obsolete", or changed from "deprecated" to "obsolete". ๐ŸŒ…
  • A "type" statement may be replaced with another "type" statement 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 "uses" statement of 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 way other than those allowed here. โœ‚๏ธ
  • The "prefix" statement may be changed, provided all local uses of the prefix are also changed. ๐Ÿท๏ธ

Otherwise, if the semantics of any previous definition are changed (i.e., if a non-editorial change is made to any definition other than those specifically allowed above), then this MUST be achieved by a new definition with a new identifier. Big no-no zone ๐Ÿšซ

In statements that have any data definition statements as substatements, those data definition substatements MUST NOT be reordered. If new data definition statements are added, they can be added anywhere in the sequence of existing substatements. ๐Ÿซถ

12. Coexistence with YANG Version 1 ๐Ÿ‘ฏ

A YANG version 1.1 module MUST NOT include a YANG version 1 submodule, and a YANG version 1 module MUST NOT include a YANG version 1.1 submodule. Keep the versions in their own lanes ๐Ÿ›ฃ๏ธ

A YANG version 1 module or submodule MUST NOT import a YANG version 1.1 module by revision.

A YANG version 1.1 module or submodule MAY import a YANG version 1 module by revision.

If a YANG version 1 module A imports module B without revision and module B is updated to YANG version 1.1, a server MAY implement both of these modules (A and B) at the same time. In such cases, a NETCONF server MUST advertise both modules using the rules defined in Section 5.6.4, and SHOULD advertise module A and the latest revision of module B that is specified with YANG version 1 according to the rules defined in [RFC6020]. Two modules, one server, zero drama ๐Ÿค

This rule exists in order to allow implementations of existing YANG version 1 modules together with YANG version 1.1 modules. Without this rule, updating a single module to YANG version 1.1 would have a cascading effect on modules that import it, requiring all of them to also be updated to YANG version 1.1, and so on. It's giving domino effect, and nobody wants that ๐Ÿ‚

13. YIN ๐Ÿงต

A YANG module can be translated into an alternative XML-based syntax called YIN. The translated module is called a YIN module. This section describes bidirectional mapping rules between the two formats.

The YANG and YIN formats contain equivalent information using different notations. The YIN notation enables developers to represent YANG data models in XML and therefore use the rich set of XML-based tools for data filtering and validation, automated generation of code and documentation, and other tasks. Tools like XSLT or XML validators can be utilized. Main character energy for XML tooling ๐Ÿ’…โœจ

The mapping between YANG and YIN does not modify the information content of the model. Comments and whitespace are not preserved. ๐Ÿซง

13.1. Formal YIN Definition ๐Ÿ’–

okay bestie, YANG keywords and YIN elements have a one-to-one match ๐ŸŽ€ The local name of every YIN element is the exact same as its YANG keyword, so the root of a YIN document is always <module> or <submodule> ๐Ÿ‘‘

YIN elements for the YANG keywords live in the namespace whose URI is "urn:ietf:params:xml:ns:yang:yin:1" โœจ

YIN elements for extension keywords go in the namespace of the YANG module that declares the extension with the "extension" statement ๐Ÿฆ‹

The names of all YIN elements MUST be properly qualified with their namespaces (as specified above) using the standard mechanisms of [XML-NAMES], i.e., "xmlns" and "xmlns:xxx" attributes. No skipping this, it's giving strict ๐Ÿ’…

The argument of a YANG statement shows up in YIN as either an XML attribute or a subelement of the keyword element. Table 1 has the mapping for all the YANG keywords. For extensions, the argument mapping is set inside the "extension" statement (see Section 7.19). These rules apply to arguments:

  • If the argument is represented as an attribute, this attribute has no namespace.
  • If the argument is represented as an element, it is qualified by the same namespace as its parent keyword element.
  • If the argument is represented as an element, it MUST be the first child of the keyword element.

Substatements of a YANG statement are represented as (additional) children of the keyword element, and their relative order MUST be the same as the order of substatements in YANG. Order matters, bestie ๐Ÿงš

Comments in YANG MAY be mapped to XML comments. Optional but cute ๐ŸŒธ

           +------------------+---------------+-------------+
           | keyword          | argument name | yin-element |
           +------------------+---------------+-------------+
           | action           | name          | false       |
           | anydata          | name          | false       |
           | 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       |
           | modifier         | 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: Mapping of Arguments of the YANG Statements

13.1.1. Usage Example ๐Ÿ“

Here's a YANG module, the "example-foo" one:

     module example-foo {
       yang-version 1.1;
       namespace "urn:example:foo";
       prefix "foo";

       import example-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";
         }
       }
     }

Here, the "c-define" extension is defined in Section 7.19.3. Once it gets translated, the YIN version looks like this, and it's so pretty ๐Ÿ˜ญโœจ

     <module name="example-foo"
             xmlns="urn:ietf:params:xml:ns:yang:yin:1"
             xmlns:foo="urn:example:foo"
             xmlns:myext="urn:example:extensions">

       <namespace uri="urn:example:foo"/>
       <prefix value="foo"/>

       <import module="example-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>

14. YANG ABNF Grammar ๐Ÿ‘‘

Okay so in YANG, almost every statement is unordered ๐Ÿฆ‹ The ABNF grammar [RFC5234] [RFC7405] sets the canonical order. For easier-to-read modules, it is RECOMMENDED that clauses be entered in this order. Clean code is main character energy ๐Ÿ’…

Inside the ABNF grammar, the unordered statements are marked with comments.

This grammar assumes that the scanner swaps YANG comments out for a single space character. Little detail, big vibes ๐ŸŒˆ

   <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
                         namespace-stmt
                         prefix-stmt

   submodule-header-stmts =
                         ;; these stmts can appear in any order
                         yang-version-stmt
                         belongs-to-stmt

   meta-stmts          = ;; these stmts can appear in any order
                         [organization-stmt]
                         [contact-stmt]
                         [description-stmt]
                         [reference-stmt]

   linkage-stmts       = ;; these stmts can appear in any order
                         *import-stmt
                         *include-stmt

   revision-stmts      = *revision-stmt

   body-stmts          = *(extension-stmt /
                           feature-stmt /
                           identity-stmt /
                           typedef-stmt /
                           grouping-stmt /
                           data-def-stmt /
                           augment-stmt /
                           rpc-stmt /
                           notification-stmt /
                           deviation-stmt)

   data-def-stmt       = container-stmt /
                         leaf-stmt /
                         leaf-list-stmt /
                         list-stmt /
                         choice-stmt /
                         anydata-stmt /
                         anyxml-stmt /
                         uses-stmt

   yang-version-stmt   = yang-version-keyword sep yang-version-arg-str
                         stmtend

   yang-version-arg-str = < a string that matches the rule >
                          < yang-version-arg >

   yang-version-arg    = "1.1"

   import-stmt         = import-keyword sep identifier-arg-str optsep
                         "{" stmtsep
                             ;; these stmts can appear in any order
                             prefix-stmt
                             [revision-date-stmt]
                             [description-stmt]
                             [reference-stmt]
                         "}" stmtsep

   include-stmt        = include-keyword sep identifier-arg-str optsep
                         (";" /
                          "{" stmtsep
                              ;; these stmts can appear in any order
                              [revision-date-stmt]
                              [description-stmt]
                              [reference-stmt]
                          "}") stmtsep

   namespace-stmt      = namespace-keyword sep uri-str stmtend

   uri-str             = < a string that matches the rule >
                         < URI in RFC 3986 >

   prefix-stmt         = prefix-keyword sep prefix-arg-str stmtend

   belongs-to-stmt     = belongs-to-keyword sep identifier-arg-str
                         optsep
                         "{" stmtsep
                             prefix-stmt
                         "}" stmtsep

organization-stmt = organization-keyword sep string stmtend

contact-stmt = contact-keyword sep string stmtend

description-stmt = description-keyword sep string stmtend

reference-stmt = reference-keyword sep string stmtend

units-stmt = units-keyword sep string stmtend

revision-stmt = revision-keyword sep revision-date optsep (";" / "{" stmtsep ;; these stmts can appear in any order [description-stmt] [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] [status-stmt] [description-stmt] [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 *if-feature-stmt *base-stmt [status-stmt] [description-stmt] [reference-stmt] "}") stmtsep

base-stmt = base-keyword sep identifier-ref-arg-str stmtend

feature-stmt = feature-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order *if-feature-stmt [status-stmt] [description-stmt] [reference-stmt] "}") stmtsep

if-feature-stmt = if-feature-keyword sep if-feature-expr-str stmtend

if-feature-expr-str = < a string that matches the rule > < if-feature-expr >

if-feature-expr = if-feature-term [sep or-keyword sep if-feature-expr]

if-feature-term = if-feature-factor [sep and-keyword sep if-feature-term]

if-feature-factor = not-keyword sep if-feature-factor / "(" optsep if-feature-expr optsep ")" / identifier-ref-arg

typedef-stmt = typedef-keyword sep identifier-arg-str optsep "{" stmtsep ;; these stmts can appear in any order type-stmt [units-stmt] [default-stmt] [status-stmt] [description-stmt] [reference-stmt] "}" stmtsep

type-stmt = type-keyword sep identifier-ref-arg-str optsep (";" / "{" stmtsep [type-body-stmts] "}") stmtsep

type-body-stmts = numerical-restrictions / decimal64-specification / string-restrictions / enum-specification / leafref-specification / identityref-specification / instance-identifier-specification / bits-specification / union-specification / binary-specification

numerical-restrictions = [range-stmt]

range-stmt = range-keyword sep range-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [error-message-stmt] [error-app-tag-stmt] [description-stmt] [reference-stmt] "}") stmtsep

decimal64-specification = ;; these stmts can appear in any order fraction-digits-stmt [range-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] *pattern-stmt

length-stmt = length-keyword sep length-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [error-message-stmt] [error-app-tag-stmt] [description-stmt] [reference-stmt] "}") stmtsep

pattern-stmt = pattern-keyword sep string optsep (";" / "{" stmtsep ;; these stmts can appear in any order [modifier-stmt] [error-message-stmt] [error-app-tag-stmt] [description-stmt] [reference-stmt] "}") stmtsep

modifier-stmt = modifier-keyword sep modifier-arg-str stmtend

modifier-arg-str = < a string that matches the rule > < modifier-arg >

modifier-arg = invert-match-keyword

default-stmt = default-keyword sep string stmtend

enum-specification = 1*enum-stmt

enum-stmt = enum-keyword sep string optsep (";" / "{" stmtsep ;; these stmts can appear in any order *if-feature-stmt [value-stmt] [status-stmt] [description-stmt] [reference-stmt] "}") stmtsep

leafref-specification = ;; these stmts can appear in any order path-stmt [require-instance-stmt]

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]

identityref-specification = 1*base-stmt

union-specification = 1*type-stmt

binary-specification = [length-stmt]

bits-specification = 1*bit-stmt

bit-stmt = bit-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order *if-feature-stmt [position-stmt] [status-stmt] [description-stmt] [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] [error-app-tag-stmt] [description-stmt] [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-str stmtend

integer-value-str = < a string that matches the rule > < integer-value >

grouping-stmt = grouping-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [status-stmt] [description-stmt] [reference-stmt] *(typedef-stmt / grouping-stmt) *data-def-stmt *action-stmt *notification-stmt "}") stmtsep

container-stmt = container-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt *must-stmt [presence-stmt] [config-stmt] [status-stmt] [description-stmt] [reference-stmt] *(typedef-stmt / grouping-stmt) *data-def-stmt *action-stmt *notification-stmt "}") stmtsep

leaf-stmt = leaf-keyword sep identifier-arg-str optsep "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt type-stmt [units-stmt] *must-stmt [default-stmt] [config-stmt] [mandatory-stmt] [status-stmt] [description-stmt] [reference-stmt] "}" stmtsep

leaf-list-stmt = leaf-list-keyword sep identifier-arg-str optsep "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt type-stmt stmtsep [units-stmt] *must-stmt *default-stmt [config-stmt] [min-elements-stmt] [max-elements-stmt] [ordered-by-stmt] [status-stmt] [description-stmt] [reference-stmt] "}" stmtsep

list-stmt = list-keyword sep identifier-arg-str optsep "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt *must-stmt [key-stmt] *unique-stmt [config-stmt] [min-elements-stmt] [max-elements-stmt] [ordered-by-stmt] [status-stmt] [description-stmt] [reference-stmt] *(typedef-stmt / grouping-stmt) 1*data-def-stmt *action-stmt *notification-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] *if-feature-stmt [default-stmt] [config-stmt] [mandatory-stmt] [status-stmt] [description-stmt] [reference-stmt] *(short-case-stmt / case-stmt) "}") stmtsep

short-case-stmt = choice-stmt / container-stmt / leaf-stmt / leaf-list-stmt / list-stmt / anydata-stmt / anyxml-stmt

case-stmt = case-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt [status-stmt] [description-stmt] [reference-stmt] *data-def-stmt "}") stmtsep

anydata-stmt = anydata-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt *must-stmt [config-stmt] [mandatory-stmt] [status-stmt] [description-stmt] [reference-stmt] "}") stmtsep

anyxml-stmt = anyxml-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt *must-stmt [config-stmt] [mandatory-stmt] [status-stmt] [description-stmt] [reference-stmt] "}") stmtsep

uses-stmt = uses-keyword sep identifier-ref-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order [when-stmt] *if-feature-stmt [status-stmt] [description-stmt] [reference-stmt] *refine-stmt *uses-augment-stmt "}") stmtsep

refine-stmt = refine-keyword sep refine-arg-str optsep "{" stmtsep ;; these stmts can appear in any order *if-feature-stmt *must-stmt [presence-stmt] *default-stmt [config-stmt] [mandatory-stmt] [min-elements-stmt] [max-elements-stmt] [description-stmt] [reference-stmt] "}" stmtsep

refine-arg-str = < a string that matches the rule > < refine-arg >

refine-arg = descendant-schema-nodeid

uses-augment-stmt = augment-keyword sep uses-augment-arg-str optsep "{" stmtsep ;; these stmts can appear in any order [when-stmt] if-feature-stmt [status-stmt] [description-stmt] [reference-stmt] 1(data-def-stmt / case-stmt / action-stmt / notification-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] if-feature-stmt [status-stmt] [description-stmt] [reference-stmt] 1(data-def-stmt / case-stmt / action-stmt / notification-stmt) "}" stmtsep

augment-arg-str = < a string that matches the rule > < augment-arg >

augment-arg = absolute-schema-nodeid

when-stmt = when-keyword sep string optsep (";" / "{" stmtsep ;; these stmts can appear in any order [description-stmt] [reference-stmt] "}") stmtsep

rpc-stmt = rpc-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order *if-feature-stmt [status-stmt] [description-stmt] [reference-stmt] *(typedef-stmt / grouping-stmt) [input-stmt] [output-stmt] "}") stmtsep

action-stmt = action-keyword sep identifier-arg-str optsep (";" / "{" stmtsep ;; these stmts can appear in any order *if-feature-stmt [status-stmt] [description-stmt] [reference-stmt] *(typedef-stmt / grouping-stmt) [input-stmt] [output-stmt] "}") stmtsep

input-stmt = input-keyword optsep "{" stmtsep ;; these stmts can appear in any order *must-stmt *(typedef-stmt / grouping-stmt) 1*data-def-stmt "}" stmtsep

okay bestie, grammar is where the magic lives ๐Ÿงš these rules are the blueprint for every YANG statement, so here's the next chunk of the ABNF, copied exactly because parsers need every single character ๐Ÿ’…โœจ

   output-stmt         = output-keyword optsep
                         "{" stmtsep
                             ;; these stmts can appear in any order
                             *must-stmt
                             *(typedef-stmt / grouping-stmt)
                             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
                              *must-stmt
                              [status-stmt]
                              [description-stmt]
                              [reference-stmt]
                              *(typedef-stmt / grouping-stmt)
                              *data-def-stmt
                          "}") stmtsep

   deviation-stmt      = deviation-keyword sep
                         deviation-arg-str optsep
                         "{" stmtsep
                             ;; these stmts can appear in any order
                             [description-stmt]
                             [reference-stmt]
                             (deviate-not-supported-stmt /
                               1*(deviate-add-stmt /
                                  deviate-replace-stmt /
                                  deviate-delete-stmt))
                         "}" stmtsep

   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-str stmtend

   deviate-add-stmt    = deviate-keyword sep add-keyword-str optsep
                         (";" /
                          "{" stmtsep
                              ;; these stmts can appear in any order
                              [units-stmt]
                              *must-stmt
                              *unique-stmt
                              *default-stmt
                              [config-stmt]
                              [mandatory-stmt]
                              [min-elements-stmt]
                              [max-elements-stmt]
                          "}") stmtsep

   deviate-delete-stmt = deviate-keyword sep delete-keyword-str optsep
                         (";" /
                          "{" stmtsep
                              ;; these stmts can appear in any order
                              [units-stmt]
                              *must-stmt
                              *unique-stmt
                              *default-stmt
                          "}") stmtsep

   deviate-replace-stmt = deviate-keyword sep replace-keyword-str optsep
                         (";" /
                          "{" stmtsep
                              ;; these stmts can appear in any order
                              [type-stmt]
                              [units-stmt]
                              [default-stmt]
                              [config-stmt]
                              [mandatory-stmt]
                              [min-elements-stmt]
                              [max-elements-stmt]
                          "}") stmtsep

   not-supported-keyword-str = < a string that matches the rule >
                               < not-supported-keyword >

   add-keyword-str     = < a string that matches the rule >
                         < add-keyword >

   delete-keyword-str  = < a string that matches the rule >
                         < delete-keyword >

   replace-keyword-str = < a string that matches the rule >
                         < replace-keyword >

   ;; represents the usage of an extension
   unknown-statement   = prefix ":" identifier [sep string] optsep
                         (";" /
                          "{" optsep
                              *((yang-stmt / unknown-statement) optsep)
                           "}") stmtsep

   yang-stmt           = action-stmt /
                         anydata-stmt /
                         anyxml-stmt /
                         argument-stmt /
                         augment-stmt /
                         base-stmt /
                         belongs-to-stmt /
                         bit-stmt /
                         case-stmt /
                         choice-stmt /
                         config-stmt /
                         contact-stmt /
                         container-stmt /
                         default-stmt /
                         description-stmt /
                         deviate-add-stmt /
                         deviate-delete-stmt /
                         deviate-not-supported-stmt /
                         deviate-replace-stmt /
                         deviation-stmt /
                         enum-stmt /
                         error-app-tag-stmt /
                         error-message-stmt /
                         extension-stmt /
                         feature-stmt /
                         fraction-digits-stmt /
                         grouping-stmt /
                         identity-stmt /
                         if-feature-stmt /
                         import-stmt /
                         include-stmt /
                         input-stmt /
                         key-stmt /
                         leaf-list-stmt /
                         leaf-stmt /
                         length-stmt /
                         list-stmt /
                         mandatory-stmt /
                         max-elements-stmt /
                         min-elements-stmt /
                         modifier-stmt /
                         module-stmt /
                         must-stmt /
                         namespace-stmt /
                         notification-stmt /
                         ordered-by-stmt /
                         organization-stmt /
                         output-stmt /
                         path-stmt /
                         pattern-stmt /
                         position-stmt /
                         prefix-stmt /
                         presence-stmt /
                         range-stmt /
                         reference-stmt /
                         refine-stmt /
                         require-instance-stmt /
                         revision-date-stmt /
                         revision-stmt /
                         rpc-stmt /
                         status-stmt /
                         submodule-stmt /
                         typedef-stmt /
                         type-stmt /
                         unique-stmt /
                         units-stmt /
                         uses-augment-stmt /
                         uses-stmt /
                         value-stmt /
                         when-stmt /
                         yang-version-stmt /
                         yin-element-stmt

   ;; 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
                                 [1*key-predicate /
                                  leaf-list-predicate /
                                  pos]))

   key-predicate       = "[" *WSP key-predicate-expr *WSP "]"

   key-predicate-expr  = node-identifier *WSP "=" *WSP quoted-string

   leaf-list-predicate = "[" *WSP leaf-list-predicate-expr *WSP "]"

   leaf-list-predicate-expr = "." *WSP "=" *WSP quoted-string

   pos                 = "[" *WSP positive-integer-value *WSP "]"

   quoted-string       = (DQUOTE string DQUOTE) / (SQUOTE string SQUOTE)

   ;; 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 the syntax for case-sensitive strings (RFC 7405)

okay so there are a bunch of NETCONF error responses for when the data model gets broken ๐Ÿ˜ญ each one is for a specific YANG situation. If the relevant YANG statement has an "error-app-tag" substatement, that overrides the default value listed below ๐Ÿ’…

15.1. Error Message for Data That Violates a "unique" Statement

if a NETCONF operation would leave the configuration data with a "unique" constraint broken, the following error MUST be returned ๐Ÿ™ˆ

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").

15.2. Error Message for Data That Violates a "max-elements" Statement

if a NETCONF operation would leave a list or a leaf-list with too many entries, the following error MUST be returned ๐Ÿซถ

error-tag:      operation-failed
error-app-tag:  too-many-elements

this error only shows up once, with the error-path pointing at the list node, even if there's more than one extra child ๐Ÿฆ‹

15.3. Error Message for Data That Violates a "min-elements" Statement

if a NETCONF operation would leave a list or a leaf-list with too few entries, the following error MUST be returned ๐ŸŒธ

error-tag:      operation-failed
error-app-tag:  too-few-elements

same deal here: it's returned once, with the error-path pointing at the list node, even if more than one child is missing ๐Ÿงš

15.4. Error Message for Data That Violates a "must" Statement

if a NETCONF operation would leave configuration data where the restrictions from a "must" statement are violated, the following error MUST be returned, unless that "must" statement has its own "error-app-tag" substatement ๐Ÿ’€

error-tag:      operation-failed
error-app-tag:  must-violation

15.5. Error Message for Data That Violates a "require-instance" Statement

if a NETCONF operation would leave configuration data where a leaf of type "instance-identifier" or "leafref" marked with require-instance "true" points at an instance that doesn't exist, the following error MUST be returned ๐Ÿ“

error-tag:      data-missing
error-app-tag:  instance-required
error-path:     Path to the instance-identifier or leafref leaf.

15.6. Error Message for Data That Violates a Mandatory "choice" Statement

if a NETCONF operation would leave configuration data where a mandatory choice has no nodes at all, the following error MUST be returned ๐Ÿ‘‘

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").

15.7. 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 an instance that doesn't exist, the following error MUST be returned ๐ŸŽ€

error-tag:      bad-attribute
error-app-tag:  missing-instance

16. IANA Considerations ๐ŸŒˆ

this document registers one capability identifier URN from the "Network Configuration Protocol (NETCONF) Capability URNs" registry ๐Ÿฆ‹

Index           Capability Identifier
-------------   ---------------------------------------------------
:yang-library   urn:ietf:params:netconf:capability:yang-library:1.0

17. Security Considerations ๐Ÿ”’โœจ

okay bestie, so this document is just the language for writing and reading descriptions of management info. the language itself has zero security impact on the Internet ๐Ÿ’… no cap. the vibe is giving "we only did the grammar, the drama happens elsewhere" ๐Ÿซถ

the same security considerations apply as the ones for base NETCONF (see Section 9 in [RFC6241]). so go read that one too, it's the real main character of this topic ๐ŸŽ€

data modeled in YANG can have sensitive info in it ๐Ÿ˜ญ and RPCs or notifications defined in YANG can carry sensitive info around too. it's literally a whole thing.

the security issues come from how you use the data modeled in YANG. those issues shall be dealt with in the documents that describe the data models and in the docs about the interfaces you use to manipulate the data, like the NETCONF documents ๐Ÿ“œ so the security plan lives in those other docs, not here.

the safety of YANG data depends on a few things, periodt:

  • the security of the transmission infrastructure that sends the sensitive info ๐Ÿšš
  • the security of the apps that store or release that sensitive info ๐Ÿ—„๏ธ
  • adequate authentication and access control mechanisms, to restrict who can use the sensitive data ๐Ÿ”
โš ๏ธ Parser safety, this one's serious bestie: 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. so be careful what you feed your parser, fr fr ๐Ÿ™ˆ

18. References ๐Ÿ“š

18.1. Normative References

18.2. Informative References


Acknowledgements ๐Ÿซถ

The editor wishes to thank the following individuals, who all provided helpful comments on various draft versions of this document: Mehmet Ersue, Washam Fan, Joel Halpern, Per Hedeland, Leif Johansson, Ladislav Lhotka, Lionel Morand, Gerhard Muenz, Peyman Owladi, Tom Petch, Randy Presuhn, David Reid, Jernej Tuljak, Kent Watsen, Bert Wijnen, Robert Wilton, and Dale Worley. ๐Ÿ’– literally the whole squad.

Contributors ๐ŸŒˆ

The following people all contributed significantly to the initial YANG document:

  • Andy Bierman (YumaWorks) ๐Ÿ‘‘
  • Balazs Lengyel (Ericsson) ๐Ÿงš
  • David Partain (Ericsson) ๐ŸŒธ
  • Juergen Schoenwaelder (Jacobs University Bremen) ๐Ÿฆ‹
  • Phil Shafer (Juniper Networks) โญ

Author's Address ๐ŸŽ€

Martin Bjorklund (editor) Tail-f Systems

Email: mbj@tail-f.com

๐Ÿ“ฆ where this page comes from