1.1 The Four-Layer NETCONF Architecture
NETCONF (RFC 6241) is built on a clean four-layer model that separates concerns cleanly. The transport layer (SSH) handles encryption and authentication. The message layer wraps every operation in a consistent <rpc> envelope with a unique message-id for correlation. The operations layer defines a small, precise set of verbs. The content layer is where YANG lives — the device accepts any valid XML document conforming to its loaded YANG models.
| Layer | Name | Responsibility | Example |
| 4 | Content | What data is being exchanged | YANG-modeled configuration XML |
| 3 | Operations | How the data is manipulated | <get-config>, <edit-config>, <commit> |
| 2 | Messages | How operations are framed | <rpc> / <rpc-reply> XML envelopes |
| 1 | Transport | How bytes are delivered | SSH (TCP port 830) |
graph TD
L4["Layer 4: Content\nYANG-modeled configuration XML\n(What data is exchanged)"]
L3["Layer 3: Operations\nget-config, edit-config, commit\n(How data is manipulated)"]
L2["Layer 2: Messages\nrpc / rpc-reply XML envelopes\nwith message-id correlation\n(How operations are framed)"]
L1["Layer 1: Transport\nSSH — TCP port 830\nEncryption + Authentication\n(How bytes are delivered)"]
L4 --> L3
L3 --> L2
L2 --> L1
style L4 fill:#d4edda,stroke:#28a745,color:#000
style L3 fill:#cce5ff,stroke:#004085,color:#000
style L2 fill:#fff3cd,stroke:#856404,color:#000
style L1 fill:#f8d7da,stroke:#721c24,color:#000
1.2 Capabilities Exchange and Datastores
After SSH is established, both sides simultaneously send a <hello> message listing URNs for every supported feature. This includes NETCONF base versions, capability URNs (candidate, confirmed-commit, validate), and a namespace URI + revision date for every loaded YANG module — effectively a machine-readable bill of materials for the device's management API.
RFC 6241 defines three standard datastores:
| Datastore | Description | Always Present? |
<running> | Active configuration controlling the device | Yes |
<startup> | Configuration loaded at boot (NVRAM equivalent) | Device-dependent |
<candidate> | Staging area isolated from running until committed | Requires capability |
On Cisco IOS XE, enable the candidate datastore with:
netconf-yang
netconf-yang feature candidate-datastore
graph TD
subgraph Device["Cisco IOS XE Device"]
STARTUP["startup Datastore\nBoot configuration\n(NVRAM equivalent)"]
RUNNING["running Datastore\nActive configuration\n(controls device behavior)"]
CANDIDATE["candidate Datastore\nStaging area for changes\n(requires capability)"]
end
CLIENT["Automation Client\n(Python / Ansible / YANG Suite)"]
CLIENT -- "edit-config" --> CANDIDATE
CLIENT -- "edit-config (direct)" --> RUNNING
CANDIDATE -- "commit / confirmed-commit" --> RUNNING
CANDIDATE -- "discard-changes" --> RUNNING
RUNNING -- "copy-config" --> STARTUP
STARTUP -- "loaded at boot" --> RUNNING
style RUNNING fill:#cce5ff,stroke:#004085,color:#000
style CANDIDATE fill:#fff3cd,stroke:#856404,color:#000
style STARTUP fill:#f8d7da,stroke:#721c24,color:#000
style CLIENT fill:#d4edda,stroke:#28a745,color:#000
1.3 Core RPC Operations
| Operation | Target | Description |
<get> | N/A | Retrieves running config AND operational state data |
<get-config> | running / startup / candidate | Retrieves config from a specific datastore; supports subtree and XPath filtering |
<edit-config> | running or candidate | Modifies a datastore; operation attribute: merge, replace, create, delete, remove |
<commit> | candidate → running | Atomically copies candidate to running |
<confirmed-commit> | candidate → running | Commit with auto-rollback if confirming commit not received in time |
<discard-changes> | candidate | Reverts candidate to match current running config |
<lock> / <unlock> | Any datastore | Prevents / releases exclusive write access |
<validate> | candidate | Validates configuration without applying it |
<copy-config> | Source → Target | Copies entire datastore to another |
<close-session> | N/A | Gracefully terminates the session |
<kill-session> | N/A | Forcefully terminates another session by session ID |
1.4 Confirmed Commit Workflow
The confirmed commit (capability URN: urn:ietf:params:netconf:capability:confirmed-commit:1.1) is the critical safety net for production changes. When a confirmed commit with <confirm-timeout> is issued:
- The candidate config is committed to running (change takes effect immediately).
- A countdown timer starts (default: 600 seconds).
- If a confirming
<commit> arrives before timeout, the change is permanent.
- If the timer expires without confirmation, the device automatically rolls back to the pre-commit configuration.
Key use case: if the change breaks the management path and your session drops, you cannot send a confirming commit. Ten minutes later the device self-recovers without any console intervention.
sequenceDiagram
participant Client as Automation Client
participant Device as Cisco IOS XE Device
Note over Client,Device: Normal Path
Client->>Device: confirmed-commit (timeout=600)
Device-->>Client: rpc-reply OK
Note over Device: Change applied, timer starts
Client->>Device: commit (confirming)
Device-->>Client: rpc-reply OK
Note over Device: Permanent — timer cancelled
Note over Client,Device: Rollback Path
Client->>Device: confirmed-commit (timeout=600)
Device-->>Client: rpc-reply OK
Note over Device: Change applied, timer starts
Note over Client: Session drops (mgmt path broken)
Note over Device: Timer expires after 600s
Note over Device: Automatic rollback
Note over Client: Management access restored
1.5 Best-Practice Candidate Datastore Workflow
The 7-Step Safe Change Sequence
1
<lock> <running> — Prevent other sessions from changing running
2
<lock> <candidate> — Prevent conflicting staged changes
3
<edit-config> → <candidate> — Stage your changes (repeat as needed)
4
<validate> <candidate> — Confirm syntactic validity before commit
5
<commit> or <confirmed-commit> — Atomically apply to running
6
<unlock> <candidate> — Release candidate lock
7
<unlock> <running> — Release running lock
If anything fails between steps 3 and 5, issue <discard-changes> to reset the candidate before unlocking.
1.6 Complete edit-config Example
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target><candidate/></target>
<config>
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
<interface>
<GigabitEthernet>
<name>1</name>
<description>Uplink to Core</description>
<ip>
<address>
<primary>
<address>192.168.1.1</address>
<mask>255.255.255.0</mask>
</primary>
</address>
</ip>
</GigabitEthernet>
</interface>
</native>
</config>
</edit-config>
</rpc>
2.1 What is RESTCONF?
RESTCONF (RFC 8040) exposes the same YANG data models as NETCONF through a conventional HTTP/HTTPS REST API. Any developer who has consumed a REST API can apply those same skills to a Cisco device. RESTCONF is explicitly a subset of NETCONF: it omits datastores, explicit locking, and confirmed commits in exchange for universal accessibility via HTTPS and native JSON support.
2.2 Enabling RESTCONF on Cisco IOS XE
netconf-yang
restconf
ip http secure-server
netconf-yang must be configured first — RESTCONF reuses the YANG model infrastructure that NETCONF initializes. The API root is discoverable via GET https://{device}/.well-known/host-meta; on IOS XE this points to /restconf. Data resources live under /restconf/data/; operations (RPCs/actions) under /restconf/operations/.
2.3 URI Construction
URI construction is one of the highest-frequency exam topics. The pattern is:
https://{device}/restconf/data/{module-name}:{top-container}/{child}/{list}={key}
Concrete example — GigabitEthernet 1 via the native model:
https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1
graph TD
URI["https://192.168.1.1/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1"]
HOST["Host\n192.168.1.1"]
ROOT["API Root\n/restconf/data/\n(Fixed prefix)"]
MODULE["Module + Container\nCisco-IOS-XE-native:native\n(module name : top-level container)"]
PATH["Intermediate Path\n/interface"]
LIST["List + Key\n/GigabitEthernet=1"]
URI --> HOST
URI --> ROOT
URI --> MODULE
URI --> PATH
URI --> LIST
style HOST fill:#f8d7da,stroke:#721c24,color:#000
style ROOT fill:#d4edda,stroke:#28a745,color:#000
style MODULE fill:#cce5ff,stroke:#004085,color:#000
style PATH fill:#fff3cd,stroke:#856404,color:#000
style LIST fill:#e2d9f3,stroke:#6f42c1,color:#000
Additional rules: module boundary augmentations require the augmenting module prefix (e.g. openconfig-if-ip:ipv4); multiple list keys are comma-separated (/BGPNeighbor={address},{vrf}); special characters in key values must be percent-encoded.
2.4 HTTP Methods and Their NETCONF Equivalents
HTTP Method Quick Reference
GET
get-config / get
POST
edit-config create
PUT
edit-config replace
PATCH
edit-config merge
DELETE
edit-config delete
| HTTP Method | NETCONF Equivalent | Description |
| GET | <get-config> / <get> | Retrieve a resource (config or state) |
| POST | <edit-config> (create) | Create a new resource; 409 Conflict if already exists |
| PUT | <edit-config> (replace) | Create or replace a resource entirely |
| PATCH | <edit-config> (merge) | Merge updates into an existing resource |
| DELETE | <edit-config> (delete) | Delete a resource |
2.5 Content Negotiation and Query Parameters
| Header | JSON Value | XML Value |
Content-Type | application/yang-data+json | application/yang-data+xml |
Accept | application/yang-data+json | application/yang-data+xml |
| Query Parameter | Example | Effect |
depth | depth=2 | Limit response to N levels deep |
content=config | ?content=config | Return only configuration nodes |
content=nonconfig | ?content=nonconfig | Return only state/operational nodes |
fields | fields=name;description | Return only specified leaf fields |
with-defaults | with-defaults=report-all | Include nodes set to their default values |
3.1 RFC 7951 JSON Encoding Rules
Every failed RESTCONF call fails for the same root cause: the payload does not match the YANG model. RFC 7951 defines the encoding rules:
- Module name as namespace prefix — at every module boundary, prefix the key with the module name and a colon:
"Cisco-IOS-XE-native:native".
- Lists become JSON arrays — each list entry is a JSON object; the key field is a regular property within that object.
- Containers become JSON objects — standard key-value maps.
- Leaf-lists become JSON arrays of primitives — e.g., an array of strings.
- Empty-type leaves — represented as
[null].
3.2 Mapping a YANG Tree to JSON
Using pyang to view the relevant YANG tree section:
pyang -f tree --tree-path /native/interface/GigabitEthernet Cisco-IOS-XE-native.yang
module: Cisco-IOS-XE-native
+--rw native
+--rw interface
+--rw GigabitEthernet* [name] <-- list, key=name
+--rw name string
+--rw description? string <-- optional leaf (?)
+--rw ip
+--rw address
+--rw primary
+--rw address inet:ipv4-address
+--rw mask inet:ipv4-address
Translated to JSON for a full PATCH targeting the native container:
{
"Cisco-IOS-XE-native:native": {
"interface": {
"GigabitEthernet": [
{
"name": "1",
"description": "Uplink to Core",
"ip": {
"address": {
"primary": {
"address": "192.168.1.1",
"mask": "255.255.255.0"
}
}
}
}
]
}
}
}
3.3 pyang Workflows for JSON Payload Construction
| Step | Command | Purpose |
| 1 | pyang -f tree Cisco-IOS-XE-native.yang | Visualize the YANG tree structure |
| 2 | pyang -f sample-xml-skeleton ... > skeleton.xml | Generate XML template with placeholder values |
| 3 | pyang -f jsonxsl -o native.xsl ... then xsltproc native.xsl config.xml | Convert XML instance to RFC 7951 JSON |
| 4 | pyang -f jtox -o native.jtox ... then python json2xml.py ... | Convert JSON back to XML (for NETCONF use) |
3.4 Cisco YANG Suite GUI Workflow
YANG Suite (available as a Docker container at http://localhost:8480) provides a point-and-click workflow: create a YANG Set, navigate the visual tree, check nodes and enter values, select RESTCONF + JSON encoding, and review the generated URI and payload. Click "Run RPC" to execute directly, or copy the payload for use in Python/Ansible/Postman.
Validate any JSON payload before sending to a device using yanglint:
yanglint --format json Cisco-IOS-XE-native.yang my_payload.json
4.1 XML Namespace Rules for NETCONF Payloads
XML is the exclusive data format for NETCONF. Every top-level container element in a <config> block must carry an xmlns attribute declaring the namespace URI of the YANG module. Getting this wrong is the most common cause of NETCONF payload failures.
| YANG Module | XML Namespace |
Cisco-IOS-XE-native | http://cisco.com/ns/yang/Cisco-IOS-XE-native |
Cisco-IOS-XE-bgp | http://cisco.com/ns/yang/Cisco-IOS-XE-bgp |
ietf-interfaces | urn:ietf:params:xml:ns:yang:ietf-interfaces |
openconfig-interfaces | http://openconfig.net/yang/interfaces |
openconfig-bgp | http://openconfig.net/yang/bgp |
The namespace appears once on the top-level container element and is inherited by all children within the same module. Child elements at a module boundary (augmentations) must declare their own xmlns.
To delete a specific element, add the operation attribute to the target node:
<description nc:operation="delete"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"/>
4.2 Subtree and XPath Filters
Subtree filter — uses XML element matching; empty elements select all content beneath:
<filter type="subtree">
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
<interface>
<GigabitEthernet><name>1</name></GigabitEthernet>
</interface>
</native>
</filter>
XPath filter — uses an XPath expression string; requires the XPath capability to be advertised:
<filter type="xpath"
select="/native/interface/GigabitEthernet[name='1']"
xmlns:ios="http://cisco.com/ns/yang/Cisco-IOS-XE-native"/>
4.3 NETCONF vs. RESTCONF: Protocol Comparison
flowchart TD
START([New automation task])
Q1{Multi-step config\nchanges needed?}
Q2{Automatic rollback\nrequired?}
Q3{Candidate staging\nor locking needed?}
Q4{HTTP-native team\nor REST tooling?}
Q5{Read-only or\nlightweight update?}
NETCONF(["Use NETCONF\nRFC 6241 / SSH port 830\nCandidate + confirmed commit\nncclient / netconf_config"])
RESTCONF(["Use RESTCONF\nRFC 8040 / HTTPS port 443\nStateless HTTP, JSON preferred\nrequests / curl / Ansible uri"])
EITHER(["Either protocol\n(RESTCONF simpler for\nsingle-resource ops)"])
START --> Q1
Q1 -- Yes --> Q2
Q1 -- No --> Q4
Q2 -- Yes --> NETCONF
Q2 -- No --> Q3
Q3 -- Yes --> NETCONF
Q3 -- No --> Q4
Q4 -- Yes --> RESTCONF
Q4 -- No --> Q5
Q5 -- Yes --> RESTCONF
Q5 -- No --> EITHER
style NETCONF fill:#cce5ff,stroke:#004085,color:#000
style RESTCONF fill:#d4edda,stroke:#28a745,color:#000
style EITHER fill:#fff3cd,stroke:#856404,color:#000
| Attribute | NETCONF | RESTCONF |
| RFC | RFC 6241 | RFC 8040 |
| Transport | SSH | HTTPS |
| Default Port | 830 | 443 |
| Data Format | XML only | XML and JSON |
| Session Model | Stateful (persistent) | Stateless (per-request) |
| Datastores | running, startup, candidate | Conceptual single (running) |
| Candidate Datastore | Yes | No |
| Locking | Explicit lock/unlock | No locking |
| Confirmed Commit | Yes (auto-rollback) | No |
| Transactions | Full ACID-like | None (immediate apply) |
| Tooling | ncclient, Ansible netconf_config | requests, curl, Postman, Ansible uri |
The most exam-tested distinction: RESTCONF has no candidate datastore, no locking, and no confirmed commit. For any scenario requiring transactional safety or automatic rollback, NETCONF is the answer.