Skip to main content

blockDiff

blockDiff(config: List<ConfigLine>, pattern: PatternBlocks<T>) : {data: T, blocks: MatchBlocks, diffCount: Integer}

Compares the block pattern pattern with the config lines in config and their children, and returns the closest match: the match with the fewest differences. Unlike blockMatches, blockDiff always returns exactly one record, even when the pattern does not fully match.

The record has the following fields:

  • data: T The data extracted by the pattern lines that matched, merged into one record.
  • blocks: MatchBlocks The differences between the pattern and the configuration. Empty if the pattern fully matches.
  • diffCount: Integer The number of differences. 0 if the pattern fully matches.

If several matches have the same number of differences, blockDiff returns one of them.

See Block Patterns for the block pattern syntax, and Details on Patterns for the syntax of each pattern line.

Matching rules​

blockDiff matches pattern lines to config lines with the same rules as blockMatches. See blockMatches matching rules.

Differences​

The blocks field contains each difference, along with the config lines of its parent blocks. Lines that fully match are omitted. Each difference counts toward diffCount and is one of the following:

DifferenceHow blocks shows itContribution to diffCount
A pattern line that matches no config lineThe pattern line, followed by **Missing**1 for the line and 1 for each pattern line nested under it
A config line that matches no pattern line, when unmatched config lines are forbiddenThe config line, followed by **Extraneous**1 for the line and 1 for each config line nested under it
A config line that matches a negated ({%-%}) pattern lineThe config line, followed by **Present**1

Data​

The type of data depends on the pattern:

  • If the pattern has named components, data is a record, and every field of the record is nullable. A field is null if the pattern line that contains it is missing. For example, a pattern of type PatternBlocks<{name: String}> produces data of type {name?: String}.
  • If the pattern does not produce a record, for example because it has a single unnamed component such as {string}, data itself is nullable and is null if the pattern line that contains the component is missing.

Null inputs​

If config, any item in config, or pattern is null, the query fails with the error First argument to “blockDiff” was null; consider adding an isPresent() condition. (or Second argument for pattern). The config field of a device's files is nullable. Use isPresent to exclude null values before calling blockDiff.

Examples​

Each example compares a pattern with the following configuration, which is the same configuration used in the blockMatches examples:

hostname r1
interface GigabitEthernet1
description uplink
ip address 10.0.0.1 255.255.255.0
ip address 10.0.1.1 255.255.255.0 secondary
interface GigabitEthernet2
ip address 10.0.2.1 255.255.255.0
shutdown
interface GigabitEthernet3
shutdown
router bgp 65000
neighbor 10.0.0.2 remote-as 65001

You can try out each of these examples in NQE by inserting the pattern into the pattern variable in the following template:

text =
"""
hostname r1
interface GigabitEthernet1
description uplink
ip address 10.0.0.1 255.255.255.0
ip address 10.0.1.1 255.255.255.0 secondary
interface GigabitEthernet2
ip address 10.0.2.1 255.255.255.0
shutdown
interface GigabitEthernet3
shutdown
router bgp 65000
neighbor 10.0.0.2 remote-as 65001
""";

pattern = ```
<your pattern here>
```;

match = blockDiff(parseConfigBlocks(OS.IOS, text), pattern);

[{ data: match.data, blocks: match.blocks, diffCount: match.diffCount }]

Full match​

With pattern

interface {name:string}
ip address {ip:ipv4Address}
shutdown

We get results

data blocks diffCount
{name: "GigabitEthernet2", ip: 10.0.2.1} Empty 0

GigabitEthernet2 contains every line of the pattern, so there are no differences.

Missing child line​

With pattern

interface GigabitEthernet3
shutdown
description uplink

We get results

data blocks diffCount
{}
interface GigabitEthernet3
description uplink **Missing**
1

GigabitEthernet3 has no description uplink line. The blocks field shows the missing line under the config line of its parent block. The shutdown line fully matches, so blocks omits it.

Closest of several blocks​

With pattern

interface {name:string}
description uplink
ip address 10.0.0.1 255.255.255.0
shutdown

We get results

data blocks diffCount
{name: "GigabitEthernet1"}
interface GigabitEthernet1
shutdown **Missing**
1

Every interface block matches the first pattern line, so blockDiff compares the pattern with each of them. GigabitEthernet1 is missing one line, while GigabitEthernet2 and GigabitEthernet3 are each missing two, so blockDiff returns the comparison with GigabitEthernet1.

Missing block​

With pattern

router ospf 1
router-id 1.1.1.1

We get results

data blocks diffCount
{}
router ospf 1 **Missing**
router-id 1.1.1.1 **Missing**
2

No config line matches router ospf 1, so that line and its child pattern line are both missing. Each missing line counts toward diffCount.

Missing line with a named component​

With pattern

hostname {host:string}
interface GigabitEthernet3
router ospf {id:number}

We get results

data blocks diffCount
{host: "r1", id: null}
router ospf {id:number} **Missing**
1

The hostname and interface GigabitEthernet3 lines fully match, so blocks omits them. The router ospf line is missing, so the id field of data is null.

Negated pattern line​

With pattern

interface GigabitEthernet2
{%-%} shutdown

We get results

data blocks diffCount
{}
interface GigabitEthernet2
shutdown **Present**
1

The pattern requires that GigabitEthernet2 has no shutdown line. The configuration has one, so blocks shows it as present.

Forbidding unmatched config lines​

With pattern

interface GigabitEthernet2
shutdown
{%unmatched-config:forbid%}

We get results

data blocks diffCount
{}
interface GigabitEthernet2
ip address 10.0.2.1 255.255.255.0 **Extraneous**
1

{%unmatched-config:forbid%} makes every child of the matched interface line that no pattern line matches a difference. The ip address line is extraneous.

Example query​

The following query checks each device for the required NTP and logging configuration, and lists the devices that differ from it, along with the differences.

pattern = ```
ntp server 10.0.0.1
logging host 10.0.0.2
```;

foreach device in network.devices
where isPresent(device.files.config)
let diff = blockDiff(device.files.config, pattern)
where diff.diffCount > 0
select {device: device.name, diffCount: diff.diffCount, differences: diff.blocks}

blockDiff returns a single record, so the query uses a let qualifier rather than a foreach qualifier. The where diff.diffCount > 0 condition keeps only the devices that differ from the pattern.

Deprecated Bag overload

blockDiff also accepts a Bag<ConfigLine> as its first argument. A Bag has no defined order, so this overload produces a warning. Use orderBy() or order() to convert the argument to a List<ConfigLine>. In a future release, the warning will become a type-checking error.

See also​

Guides​

Types​

Functions​

  • blockMatches: Gets every complete match of a block pattern against a list of config lines.
  • hasBlockMatch: Checks whether blockMatches returns at least one match.
  • blockPattern: Creates a block pattern from a string.
  • withOption: Changes how a block pattern handles unmatched config lines and partial line matches.