In a YANG list, how does a unique statement differ from the list's key, and which entries does unique leave out of the check?
answer
- identity versus an extra rule
- combined values across entries
- defaults count, missing leafs skip
- data-not-unique with non-unique pointers
basics
~20 sA YANG list key identifies each entry and every key leaf must be set; unique adds a rule that the combined values of named leafs differ across entries. Entries missing any referenced leaf, with no default in use, are skipped.
solid answer
~40 sThe `key` names the leafs whose combined values identify an entry; every key leaf is set when the entry is created, and a configuration list MUST have one. `unique` takes a space-separated list of descendant leafs and requires their **combined** values, defaults included, to differ across all entries in which every referenced leaf exists or has a default — an entry missing one is simply not compared. A missing key leaf is caught as the payload is parsed (`missing-element`); `unique` is checked during validation and fails with `operation-failed`, `error-app-tag` `data-not-unique`, and a `<non-unique>` instance identifier for each offending leaf. In a BGP neighbour list keyed by `address`, `unique "name"` stops two neighbours sharing a display name, while unnamed neighbours are left alone; add `mandatory true` if every neighbour needs a name.
code
yang · 12 linescontainer bgp {
list neighbor {
key "address";
unique "name";
leaf address { type inet:ip-address; }
leaf name { type string; }
leaf peer-as {
type inet:as-number;
mandatory true;
}
}
}go deeper
Recall that the key identifies an entry and must always be set, while unique only stops value combinations repeating.
Explain the comparison: combined values, defaults counted, entries missing a referenced leaf skipped, and the data-not-unique reply with non-unique pointers.
Show when to reach for which: identity in the key, renameable labels under unique, mandatory where a value is required, and unique rather than a counting must.
Consider the contract a list's identity creates for every client, and how adding a default to a unique leaf can invalidate configuration already deployed.
## Key and unique answer different questions Every configuration list in YANG MUST have a `key` (RFC 7950 §7.8.2). The key names the leafs whose combined values identify one entry: a NETCONF or RESTCONF request addresses an entry by its key, so two entries with the same key would be the same entry. `unique` (§7.8.3) is an extra rule over other leafs of the list. | | `key` | `unique` | |---|---|---| | Purpose | identifies an entry | forbids repeated value combinations across entries | | Leafs present? | every key leaf, given a value when the entry is created | optional; an entry missing one is skipped | | Defaults | ignored on key leafs | a default in use counts as a value | | Checked | as the payload is parsed | during validation | | Failure | `missing-element` when a key leaf is absent | `operation-failed` with `data-not-unique` | ## What unique compares The argument is a space-separated list of schema node identifiers in descendant form, each of which MUST name a leaf below the list. The rule works in three steps: 1. For each list entry, take the values of all referenced leafs, using the default value where one is in use. 2. Leave out any entry in which a referenced leaf neither exists nor has a default. 3. Across the remaining entries, the **combined** values MUST be unique. So `unique "ip port"` rejects two entries that both have `ip 192.0.2.1` and `port 25`, accepts two entries that share an address but use different ports, and accepts any number of entries that set `ip` and leave out `port` — RFC 7950's own example in §7.8.3.1. If one referenced leaf represents configuration, all of them MUST. Two details of the definition are easy to miss: - **Descendant form.** A referenced leaf may sit below a child container of the entry, written as a relative path such as `timers/hold-time`; it is still compared per entry. - **"Exist or have default values".** A leaf counts as present for the comparison either because the client set it or because its default is in use; only a leaf that is absent with no default takes the entry out of the check. ## The BGP neighbour model In the code example, the `neighbor` list is keyed by `address` and carries `unique "name"`: a neighbour may have a human-readable name, and no two neighbours may share one. Trace a configuration: 1. Neighbour 192.0.2.1 named `transit-a` — accepted. 2. Neighbour 198.51.100.7, also named `transit-a` — validation fails, because the value of `name` repeats. 3. Neighbour 203.0.113.9 with no name — accepted; it takes no part in the comparison. If every neighbour must have a name, `unique` cannot say so; add `mandatory true` to `name`. The key would be a poor home for the name: every request addresses the entry by its key, while a display name is something an operator expects to be able to change. ## What the client sees RFC 7950 §15.1 fixes the NETCONF reply: - `error-tag` `operation-failed` and `error-app-tag` `data-not-unique`; - in `<error-info>`, one `<non-unique>` element for each offending leaf, holding an instance identifier that points at it, in the YANG namespace `urn:ietf:params:xml:ns:yang:1`. A client can therefore point the operator at the exact duplicate rather than at "invalid configuration". If the statement carried its own `error-app-tag`, that would override the default — but `unique` has no such substatement, so `data-not-unique` is what clients see. Because `unique` is a validation constraint, a NETCONF server checks it at the end of an `<edit-config>` or `<copy-config>` on the running or startup datastore, and at `<commit>` or `<validate>` on the candidate. A missing key leaf, by contrast, is caught as the payload is parsed, whatever the target datastore. ## Design notes - **Prefer `unique` to a counting `must`.** An expression such as `count(../neighbor[name = current()/name]) = 1` on `name` states a similar rule, but RFC 9907's performance guidance says `must` statements are generally more expensive than `unique`, and the standard `data-not-unique` reply with its `<non-unique>` pointers is lost. - **Remember the combination.** `unique "local-address peer-as"` forbids two entries sharing *both* values; each leaf may still repeat on its own. - **Leaf-lists need no `unique`.** RFC 7950 requires the values in a configuration leaf-list to be distinct already, and among data definitions only `list` takes a `unique` statement. - **Defaults participate.** Giving a referenced leaf a `default` brings every entry into the comparison, which can turn a previously valid configuration into a set of duplicates. - **Read the reply, not the message.** Tooling that turns `data-not-unique` and its `<non-unique>` paths into "neighbour 198.51.100.7 reuses the name transit-a" saves an operator a search through the whole list.
- Why not express the same rule with a YANG must that counts neighbours with the same name?It can be written, but RFC 9907's performance guidance says `must` statements are generally more expensive to evaluate than `unique`, the expression is easy to get wrong, and the client loses the standard reply: `operation-failed` with `data-not-unique` and a `<non-unique>` instance identifier for each offending leaf.
- Can a YANG unique statement make the name leaf required?No. RFC 7950 compares only entries in which every referenced leaf exists or has a default, so a neighbour without a name is skipped rather than rejected. Requiring the leaf is a separate rule: `mandatory true` on `name`, which applies in every list entry.
saying these in an interview costs you the question
- unique is just a second way to declare the list key.
- An entry that leaves out one of the unique leafs violates the constraint.
- unique ignores leafs that only hold their default value.
- unique checks each listed leaf on its own, not their combination.