skip to content

A NETCONF <edit-config> meant only to change an interface's MTU also erased that interface's description; what in the request explains it?

level: middleimportance: must knowfreq 15%

answer

  1. the default is not the problem
  2. an attribute on the list entry
  3. omitted children are not kept
  4. default-operation can widen the damage

basics

~20 s

The request used replace: an operation="replace" attribute on the interface entry, or default-operation replace. Replace makes the sent data the whole entry, so the omitted description is deleted; merge, the default, would have changed only the MTU.

solid answer

~40 s

In an `<edit-config>`, each element can carry a NETCONF `operation` attribute, and where none is given the request's `<default-operation>` applies, which itself defaults to `merge`. With `merge`, only the leaves sent are changed, so sending the interface's key and a new `<mtu>` leaves the description alone. The job instead sent `operation="replace"` on the `<interface>` entry: RFC 6241 says the sent data *replaces* the related configuration, so the entry now holds exactly the name and MTU and the description is gone. If the job had set `<default-operation>replace</default-operation>`, the damage would be wider: the whole `<config>` replaces the whole target datastore, deleting every other interface too. The fix is to drop the attribute and let merge apply, or to send a replace with every leaf the entry should keep.

code

xml · 14 lines
xml
<rpc message-id="102"
     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <edit-config>
    <target><running/></target>
    <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
      <top xmlns="http://example.com/schema/1.2/config">
        <interface xc:operation="replace">
          <name>uplink-1</name>
          <mtu>9000</mtu>
        </interface>
      </top>
    </config>
  </edit-config>
</rpc>

go deeper

for a junior

Recall that an edit-config merges by default and that replace, create, delete and remove must be asked for, either per element or through default-operation.

for a middle

Explain replace precisely: the entry carrying the attribute becomes exactly what was sent, so unsent children are deleted. Contrast it with default-operation replace and with copy-config.

for a senior

Diagnose from the symptom: one entry lost leaves, so look for a replace attribute; several entries gone points at default-operation replace or copy-config. Then fix the template, not just the device.

for a principal

Treat merge versus replace as an ownership statement: replace only where the pipeline owns the whole subtree, and decide who is allowed to own descriptions and other hand-set leaves.

## What `<edit-config>` actually does In NETCONF (RFC 6241), `<edit-config>` loads all or part of a configuration into a **target datastore**, such as `<running/>` on a device that advertises `:writable-running`, or `<candidate/>` on one that advertises `:candidate`. It is not a file upload. The device compares the data in the `<config>` parameter with what the target already holds and applies **operations** node by node. Two inputs decide which operation each node gets: - the **`operation` attribute**, which any element inside `<config>` may carry, in the NETCONF namespace `urn:ietf:params:xml:ns:netconf:base:1.0`; - the **`<default-operation>`** parameter, which applies wherever no attribute is given. Its own default is `merge`. ## The operation values | Value | Node exists in target | Node absent from target | |---|---|---| | `merge` | merged at that level; unsent children kept | added | | `replace` | replaced by exactly what was sent; unsent children deleted | created | | `create` | `<rpc-error>`, error-tag `data-exists` | created | | `delete` | deleted | `<rpc-error>`, error-tag `data-missing` | | `remove` | deleted | silently ignored | RFC 6241 also notes that a `replace` attribute, unlike `<copy-config>`, affects "only the configuration actually present in the `<config>` parameter". That phrase is about **scope**: the replace applies to the entry carrying the attribute, not to the whole datastore. Inside that entry, every child that was not sent disappears. ## Tracing the incident The automation job wanted one change, a new MTU on one interface. A request shaped like the example below explains the outcome: 1. The `<interface>` entry carries `operation="replace"`. 2. The device looks up the existing entry by its key, the `<name>`. 3. It replaces that entry with the sent content: a name and an MTU. 4. The description, which the device held but the request did not mention, is not part of the new entry, so it is deleted. 5. The reply is `<ok/>`: nothing failed, the device did what was asked. The same symptom has two wider-blast-radius cousins worth ruling out in a post-incident review: - **`<default-operation>replace</default-operation>`** with no attributes. RFC 6241 says the configuration in `<config>` then "completely replaces the configuration in the target datastore". Every other interface, and everything else not in the request, would be gone too. - **`<copy-config>`** with an inline `<config>` holding one interface. `<copy-config>` creates or replaces an *entire* datastore with a *complete* configuration, so a partial source wipes the rest. If only the description vanished and the other interfaces survived, the attribute on the entry is the cause. ## Fixing the job - **Use merge for a point change.** Send the key and the new MTU with no `operation` attribute and no `<default-operation>`. Unsent leaves are kept. - **If replace is required**, for instance because the job owns the whole entry and wants stale leaves cleaned out, read the entry first with `<get-config>` and send every leaf it should keep. A concurrent writer could change the entry between the read and the write; closing that window is what datastore locking is for. - **Preview before writing.** On a device that advertises `:validate:1.1`, `<test-option>test-only</test-option>` validates the edit without applying it. Validation catches invalid values; it does not warn that a valid replace will delete a description. - **Verify the result, not the reply.** An `<ok/>` means the device did what the request said, not what the job intended. Reading the touched entry back with `<get-config>` and diffing it against the pre-change snapshot is what catches an unintended deletion before someone else does. - **Stage it.** On a device with a candidate datastore, editing the candidate and reviewing it before a commit turns this mistake into a visible difference rather than a live outage. ## Why the protocol works this way Replace exists for callers that hold the complete intended state of a subtree and want the device to match it exactly, including removing leaves that should no longer exist. Merge exists for callers that know only the change. The protocol cannot tell which kind of caller you are; the attribute is how you say so. Incidents of this shape often come from a library or template that sets replace as a habit, and from callers who read "replace the MTU" as an instruction about a leaf when the attribute sat on its parent entry.

  • How would you rewrite the job so an MTU change cannot disturb any other leaf?
    Send the interface's key and the new `<mtu>` with no `operation` attribute, so the default merge applies. For extra safety set `<default-operation>none</default-operation>` and put `operation="merge"` on the `<mtu>` leaf alone: the ancestors then only locate the node, and if the interface does not exist the server returns `data-missing` instead of creating it.
  • What would the blast radius be if the replace attribute sat on the top-level container instead of the interface?
    Everything under that container would be replaced by what was sent. If the container holds all interfaces, every interface not in the request is deleted, and the one that was sent keeps only its name and MTU. The attribute's position sets the scope of the replace.

Merge is correcting one box on a form already on file; replace is handing in a fresh form for that entry, where every box you leave blank is now blank.

saying these in an interview costs you the question

  • Replace on an interface entry changes only the leaves included in the request.
  • Merge is risky because it overwrites every leaf of the entry it touches.
  • default-operation replace affects only the elements the request carries.
  • NETCONF requires every leaf of an entry to be sent in each edit.
  • <copy-config> with one interface adds that interface to the existing configuration.