YANG: the schema behind every modern network API
A reference guide to YANG data models — the modeling language that defines what NETCONF, RESTCONF, and gNMI actually transport, and how to work with it.
Contents
NETCONF, RESTCONF, and gNMI move structured data between a client and a network device. None of them define the shape of that data. YANG does. It describes what fields exist on an interface, what parameters a BGP neighbor takes, what values are legal for an admin-status. The protocols are the transport. YANG is the schema.
RFC 6020 introduced YANG in 2010. RFC 7950 updated it to version 1.1 in 2016. It is not a protocol. It defines the structure, types, and constraints of data — the data itself gets encoded as XML (NETCONF) or JSON (RFC 7951). YANG doesn’t care about the wire format.
The closest analogies: JSON Schema defines the shape of JSON documents. Protobuf .proto files define the shape of serialized messages. YANG defines the shape of network configuration and state data.
Core constructs
| Construct | What it is | Example |
|---|---|---|
| module | The file. Defines a namespace and prefix. One module = one .yang file. | ietf-interfaces |
| container | A grouping node (JSON object). No value itself. | interfaces, statistics |
| list | A keyed table. Each entry is a container. | interface* [name] |
| leaf | Atomic typed value. Where actual data lives. | name, enabled, in-octets |
| leaf-list | Array of scalar values. | DNS servers, tag sets |
| typedef | Custom type built on a primitive. | ipv4-address (string + regex) |
| grouping / uses | Reusable template. Define once, embed anywhere. | Shared address block |
| augment | Extend another module’s model. | Cisco augmenting IETF interfaces |
| choice / case | Mutually exclusive branches. | ethernet or loopback, not both |
| identity / identityref | Named constants with inheritance. | iana-if-type:ethernetCsmacd |
| deviation | ”My device doesn’t fully implement this.” | Vendor marks unsupported leaves |
Reading pyang tree output
pyang -f tree is the standard way to visualize a YANG model. Here’s ietf-interfaces — the base interface data structure every vendor implements:
module: ietf-interfaces
+--rw interfaces
+--rw interface* [name]
+--rw name string
+--rw description? string
+--rw type identityref
+--rw enabled? boolean
+--rw link-up-down-trap-enable? enumeration
+--ro admin-status enumeration
+--ro oper-status enumeration
+--ro last-change? yang:date-and-time
+--ro if-index int32
+--ro phys-address? yang:phys-address
+--ro higher-layer-if* interface-ref
+--ro lower-layer-if* interface-ref
+--ro speed? yang:gauge64
+--ro statistics
+--ro discontinuity-time yang:date-and-time
+--ro in-octets? yang:counter64
+--ro in-unicast-pkts? yang:counter64
+--ro in-broadcast-pkts? yang:counter64
+--ro in-multicast-pkts? yang:counter64
+--ro in-discards? yang:counter32
+--ro in-errors? yang:counter32
+--ro out-octets? yang:counter64
+--ro out-unicast-pkts? yang:counter64
+--ro out-broadcast-pkts? yang:counter64
+--ro out-multicast-pkts? yang:counter64
+--ro out-discards? yang:counter32
+--ro out-errors? yang:counter32
Tree notation
| Symbol | Meaning |
|---|---|
rw | Read-write. Configuration data. Set via edit-config or PUT/PATCH. |
ro | Read-only. Operational state. Counters, oper-status, speed. |
+-- | Child node indicator. |
? | Optional leaf. Device may or may not return it. |
* | List (zero or more entries) or leaf-list (multiple values). |
[name] | List key. Uniquely identifies each entry. |
The rw vs ro distinction is the most common NETCONF debugging issue. get_config() returns only rw nodes (what was configured). get() returns both rw and ro (configuration plus operational state). If you need counters or oper-status, you must use get().
Python tools
| Tool | What it does | When to use it |
|---|---|---|
| pyang | Validates YANG models, generates tree output (pyang -f tree), UML, DSDL, sample XML. Foundation for code generation plugins. | First tool you install. Daily use for tree output. |
| ncclient | NETCONF client. SSH to port 830, exchanges capabilities, sends XML RPCs. get_config(), get(), edit_config(). | Direct NETCONF interaction. Low-level, no magic. |
| pyangbind | Generates Python classes from YANG models via pyang plugin. Containers become objects, leaves become typed attributes. | Type-safe config building. Serialize to JSON. |
| yangson | Validates JSON instance data against YANG schemas. | Verifying RESTCONF responses conform to the model. |
ydk-py (archived) — Cisco’s YANG Development Kit generated full Python SDKs from YANG models — complete class hierarchies with CRUD methods. Now archived. The generated code was correct but maintaining it across hundreds of models and firmware versions was unsustainable.
Three model families
The same physical interface — GigabitEthernet1 on a Cisco IOS XE device — returns different JSON depending on which YANG model family you query. Three families coexist on the same device:
{
"ietf-interfaces:interface": {
"name": "GigabitEthernet1",
"type": "iana-if-type:ethernetCsmacd",
"enabled": true,
"ietf-ip:ipv4": {
"address": [
{
"ip": "10.10.20.48",
"netmask": "255.255.255.0"
}
]
}
}
} {
"openconfig-interfaces:interface": {
"name": "GigabitEthernet1",
"config": {
"name": "GigabitEthernet1",
"type": "iana-if-type:ethernetCsmacd",
"enabled": true
},
"subinterfaces": {
"subinterface": [
{
"index": 0,
"openconfig-if-ip:ipv4": {
"addresses": {
"address": [
{
"ip": "10.10.20.48",
"config": {
"ip": "10.10.20.48",
"prefix-length": 24
}
}
]
}
}
}
]
},
"openconfig-if-ethernet:ethernet": {
"config": {
"auto-negotiate": true
}
}
}
} {
"Cisco-IOS-XE-native:GigabitEthernet": {
"name": "1",
"ip": {
"address": {
"primary": {
"address": "10.10.20.48",
"mask": "255.255.255.0"
}
}
},
"mop": {
"enabled": false
},
"Cisco-IOS-XE-ethernet:negotiation": {
"auto": true
}
}
} Same device. Same physical interface. Three different JSON structures.
| Family | Maintainer | Strengths | Limitations |
|---|---|---|---|
| IETF | IETF working groups | Simplest, cleanest, most stable. RFC 8343 base + augmentations like ietf-ip. | Limited scope — no QoS, VRF, or platform features. |
| OpenConfig | Operators (Google, Microsoft, AT&T) | Vendor-neutral. Config and state in the same container. Most portable for multi-vendor automation. | Written in YANG 1.0. Vendor augmentations can break portability. |
| Cisco Native | Cisco | Mirrors CLI structure. Most comprehensive. If you know the CLI, you can guess the path. | Zero portability across vendors. |
Selection order: OpenConfig first for multi-vendor consistency. IETF second where OpenConfig lacks coverage. Vendor native as fallback for platform-specific features.
The vendor deviation problem
No vendor implements a standard model completely. Cisco publishes YANG deviations — separate .yang files that declare which leaves from the IETF or OpenConfig models are “not-supported” or “replace.” These deviations vary by platform and firmware version. An IOS XE 17.6 device may support a leaf that 17.3 marks as deviated. OpenConfig models get augmented with vendor-specific extensions that break the portability promise.
The hardest rule to learn: never mix model families for the same feature. If you configure an interface via openconfig-interfaces, read it back via openconfig-interfaces. Querying the same data through ietf-interfaces or the native model may return stale or mismatched state.
NETCONF vs RESTCONF
Two protocols, same YANG models underneath.
| Aspect | NETCONF | RESTCONF |
|---|---|---|
| Transport | SSH (port 830) | HTTPS |
| Encoding | XML | XML or JSON |
| Statefulness | Session-based | Stateless |
| Transactions | Candidate config, commit/rollback | Per-request atomic |
| Config vs state | get_config() for config, get() for both | content=config or content=nonconfig query param |
| Python library | ncclient | requests |
| Best for | Bulk config, transactions, rollback | Quick reads, CI/CD, modern tooling |
NETCONF has proper transactional semantics — candidate datastore, commit, rollback — but requires SSH sessions and XML. RESTCONF is stateless HTTPS with JSON support, works with any HTTP client, but lacks native multi-operation transactions.
from ncclient import manager
import xmltodict
m = manager.connect(
host='sandbox-iosxe-latest-1.cisco.com',
port=830,
username='developer',
password='C1sco12345',
hostkey_verify=False
)
# Fetch interface config using IETF model
intf_filter = """
<filter>
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>GigabitEthernet1</name>
</interface>
</interfaces>
</filter>
"""
# get_config() = configuration only (rw nodes)
config = m.get_config(source='running', filter=intf_filter)
# get() = configuration + operational state (rw + ro)
state = m.get(filter=intf_filter)
parsed = xmltodict.parse(state.xml)
print(parsed['rpc-reply']['data']['interfaces']['interface']['name']) import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
BASE = "https://sandbox-iosxe-latest-1.cisco.com/restconf/data"
HEADERS = {
"Accept": "application/yang-data+json",
"Content-Type": "application/yang-data+json"
}
AUTH = ("developer", "C1sco12345")
# Fetch interface data using IETF model
r = requests.get(
f"{BASE}/ietf-interfaces:interfaces/interface=GigabitEthernet1",
headers=HEADERS,
auth=AUTH,
verify=False
)
interface = r.json()
print(interface["ietf-interfaces:interface"]["name"]) YANG to JSON Schema (and MCP)
YANG’s type system maps to JSON Schema:
| YANG construct | JSON Schema equivalent |
|---|---|
| container | object with properties |
| list | array of objects |
| leaf | primitive (string, integer, boolean) |
| leaf-list | array of primitives |
| enumeration | enum |
| leafref | No direct equivalent (pointer to another data tree node) |
| when/must | Partial — XPath constraints exceed JSON Schema’s conditional support |
This means YANG models can be compiled into MCP tool inputSchema definitions — giving an LLM agent a machine-readable description of what parameters a network operation accepts. The IETF draft on MCP applicability describes this: “private YANG is compiled to JSON-Schema in the controller (milliseconds); tool registers via /tools/register and is immediately callable.”
The concept is sound. The hard parts: leafref has no JSON Schema equivalent, augment requires flattening across modules, deviation means per-device schema variations, and when/must constraints use XPath expressions that JSON Schema can’t fully represent. At scale — hundreds of models across multiple vendors — this translation layer is where the real engineering work lives.
The open-source vendor-agnostic MCP server project, covering architecture and roadmap.
A deep analysis of the IETF draft constellation — architecture, companion specs, and gap analysis.
Quick reference
| What you need | Where to look |
|---|---|
| YANG 1.1 spec | RFC 7950 |
| JSON encoding rules | RFC 7951 |
| Base interface model | RFC 8343 (ietf-interfaces) |
| OpenConfig models | github.com/openconfig/public |
| Cisco YANG models | github.com/YangModels/yang |
| Validate a model | pyang --strict <model>.yang |
| View tree output | pyang -f tree <model>.yang |
| Generate Python bindings | pyang --plugindir $(pyangbind) -f pybind <model>.yang |
YANG is a schema language. The protocols change, the encodings change, the clients change. The data model doesn’t.