Skip to content

Commit

Permalink
VRRP question: Filtering by address
Browse files Browse the repository at this point in the history
  • Loading branch information
ratulm committed Jan 4, 2022
1 parent bb74c2a commit 9059813
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Multiset;
Expand All @@ -13,6 +14,7 @@
import org.batfish.common.plugin.IBatfish;
import org.batfish.datamodel.Configuration;
import org.batfish.datamodel.Interface;
import org.batfish.datamodel.IpSpace;
import org.batfish.datamodel.VrrpGroup;
import org.batfish.datamodel.answers.Schema;
import org.batfish.datamodel.collections.NodeInterfacePair;
Expand All @@ -23,6 +25,7 @@
import org.batfish.datamodel.table.TableAnswerElement;
import org.batfish.datamodel.table.TableMetadata;
import org.batfish.specifier.InterfaceSpecifier;
import org.batfish.specifier.IpSpaceSpecifier;
import org.batfish.specifier.NodeSpecifier;
import org.batfish.specifier.SpecifierContext;

Expand All @@ -35,6 +38,7 @@ public final class VrrpPropertiesAnswerer extends Answerer {
public static final String COL_SOURCE_ADDRESS = "Source_Address";
public static final String COL_PRIORITY = "Priority";
public static final String COL_PREEMPT = "Preempt";
public static final String COL_ACTIVE = "Active";

/** Creates {@link ColumnMetadata}s for the answer */
public static List<ColumnMetadata> createColumnMetadata() {
Expand All @@ -44,9 +48,20 @@ public static List<ColumnMetadata> createColumnMetadata() {
.add(
new ColumnMetadata(
COL_VIRTUAL_ADDRESSES, Schema.set(Schema.IP), "Virtual Addresses", false, true))
.add(new ColumnMetadata(COL_SOURCE_ADDRESS, Schema.STRING, "Source Address", false, true))
.add(new ColumnMetadata(COL_PRIORITY, Schema.INTEGER, "Priority", false, true))
.add(new ColumnMetadata(COL_PREEMPT, Schema.BOOLEAN, "Preempt", false, true))
.add(
new ColumnMetadata(
COL_SOURCE_ADDRESS,
Schema.STRING,
"Source Address used for VRRP messages",
false,
true))
.add(new ColumnMetadata(COL_PRIORITY, Schema.INTEGER, "VRRP router priority", false, true))
.add(
new ColumnMetadata(
COL_PREEMPT, Schema.BOOLEAN, "Whether preemption is allowed", false, true))
.add(
new ColumnMetadata(
COL_ACTIVE, Schema.BOOLEAN, "Whether the interface is active", false, true))
.build();
}

Expand Down Expand Up @@ -77,6 +92,7 @@ public TableAnswerElement answer(NetworkSnapshot snapshot) {
_batfish.specifierContext(snapshot),
question.getNodesSpecifier(),
question.getInterfacesSpecifier(),
question.getVirtualAddressSpecifier(),
question.getExcludeShutInterfaces(),
tableMetadata.toColumnMap());
answer.postProcessAnswer(question, propertyRows);
Expand All @@ -94,10 +110,12 @@ static Multiset<Row> getProperties(
SpecifierContext ctxt,
NodeSpecifier nodeSpecifier,
InterfaceSpecifier interfaceSpecifier,
IpSpaceSpecifier virtualAddressSpecifier,
boolean excludeShutInterfaces,
Map<String, ColumnMetadata> columns) {
Multiset<Row> rows = HashMultiset.create();
Map<String, Configuration> configs = ctxt.getConfigs();
IpSpace virtualAddressSpace = virtualAddressSpecifier.resolve(ctxt);

for (String nodeName : nodeSpecifier.resolve(ctxt)) {
for (NodeInterfacePair ifaceId :
Expand All @@ -112,21 +130,28 @@ static Multiset<Row> getProperties(
}
RowBuilder row =
Row.builder(columns)
.put(COL_INTERFACE, NodeInterfacePair.of(nodeName, iface.getName()));
iface.getVrrpGroups().forEach((id, group) -> populateRow(row, id, group));
rows.add(row.build());
.put(COL_INTERFACE, NodeInterfacePair.of(nodeName, iface.getName()))
.put(COL_ACTIVE, iface.getActive());
iface.getVrrpGroups().entrySet().stream()
.filter(
e ->
e.getValue().getVirtualAddresses().stream()
.anyMatch(
address -> virtualAddressSpace.containsIp(address, ImmutableMap.of())))
.forEach(e -> rows.add(populateRow(row, e.getKey(), e.getValue()).build()));
}
}
return rows;
}

@VisibleForTesting
static void populateRow(RowBuilder row, Integer id, VrrpGroup group) {
static RowBuilder populateRow(RowBuilder row, Integer id, VrrpGroup group) {
row.put(COL_GROUP_ID, id)
.put(COL_VIRTUAL_ADDRESSES, ImmutableSortedSet.copyOf(group.getVirtualAddresses()))
.put(COL_SOURCE_ADDRESS, group.getSourceAddress())
.put(COL_PRIORITY, group.getPriority())
.put(COL_PREEMPT, group.getPreempt());
return row;
}

public VrrpPropertiesAnswerer(VrrpPropertiesQuestion question, IBatfish batfish) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.batfish.datamodel.UniverseIpSpace;
import org.batfish.datamodel.questions.Question;
import org.batfish.specifier.AllInterfacesInterfaceSpecifier;
import org.batfish.specifier.AllNodesNodeSpecifier;
import org.batfish.specifier.ConstantIpSpaceSpecifier;
import org.batfish.specifier.InterfaceSpecifier;
import org.batfish.specifier.IpSpaceSpecifier;
import org.batfish.specifier.NodeSpecifier;
import org.batfish.specifier.SpecifierFactories;

/** A question that returns a table with VRRP groups on interfaces and their properties. */
@ParametersAreNonnullByDefault
public final class VrrpPropertiesQuestion extends Question {
private static final String PROP_NODES = "nodes";
private static final String PROP_INTERFACES = "interfaces";
private static final String PROP_VIRTUAL_ADDRESSES = "virtualAddresses";
private static final String PROP_EXCLUDE_SHUT_INTERFACES = "excludeShutInterfaces";

@Nullable private final String _nodes;
@Nullable private final String _interfaces;
@Nullable private final String _virtualAddresses;
private final boolean _excludeShutInterfaces;

@Override
Expand All @@ -41,15 +44,20 @@ public String getName() {
private static @Nonnull VrrpPropertiesQuestion create(
@Nullable @JsonProperty(PROP_NODES) String nodes,
@Nullable @JsonProperty(PROP_INTERFACES) String interfaces,
@Nullable @JsonProperty(PROP_VIRTUAL_ADDRESSES) String virtualAddresses,
@Nullable @JsonProperty(PROP_EXCLUDE_SHUT_INTERFACES) Boolean excludeShutInterfaces) {
return new VrrpPropertiesQuestion(
nodes, interfaces, firstNonNull(excludeShutInterfaces, false));
nodes, interfaces, virtualAddresses, firstNonNull(excludeShutInterfaces, false));
}

public VrrpPropertiesQuestion(
@Nullable String nodes, @Nullable String interfaces, boolean excludeShutInterfaces) {
@Nullable String nodes,
@Nullable String interfaces,
@Nullable String virtualAddresses,
boolean excludeShutInterfaces) {
_nodes = nodes;
_interfaces = interfaces;
_virtualAddresses = virtualAddresses;
_excludeShutInterfaces = excludeShutInterfaces;
}

Expand All @@ -63,6 +71,11 @@ public VrrpPropertiesQuestion(
return _interfaces;
}

@JsonProperty(PROP_VIRTUAL_ADDRESSES)
public @Nullable String getVirtualAddresses() {
return _virtualAddresses;
}

@JsonProperty(PROP_EXCLUDE_SHUT_INTERFACES)
public boolean getExcludeShutInterfaces() {
return _excludeShutInterfaces;
Expand All @@ -79,6 +92,12 @@ public boolean getExcludeShutInterfaces() {
_interfaces, AllInterfacesInterfaceSpecifier.INSTANCE);
}

@JsonIgnore
public @Nonnull IpSpaceSpecifier getVirtualAddressSpecifier() {
return SpecifierFactories.getIpSpaceSpecifierOrDefault(
_interfaces, new ConstantIpSpaceSpecifier(UniverseIpSpace.INSTANCE));
}

@Override
public boolean equals(@Nullable Object o) {
if (!(o instanceof VrrpPropertiesQuestion)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ protected Answerer createAnswerer(Question question, IBatfish batfish) {

@Override
protected Question createQuestion() {
return new VrrpPropertiesQuestion(null, null, false);
return new VrrpPropertiesQuestion(null, null, null, false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@ParametersAreNonnullByDefault
package org.batfish.question.vrrpproperties;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.batfish.question.vrrpproperties;

import static org.batfish.question.vrrpproperties.VrrpPropertiesAnswerer.COL_ACTIVE;
import static org.batfish.question.vrrpproperties.VrrpPropertiesAnswerer.COL_GROUP_ID;
import static org.batfish.question.vrrpproperties.VrrpPropertiesAnswerer.COL_INTERFACE;
import static org.batfish.question.vrrpproperties.VrrpPropertiesAnswerer.COL_PREEMPT;
Expand All @@ -11,6 +12,7 @@
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
Expand All @@ -22,11 +24,15 @@
import org.batfish.datamodel.ConfigurationFormat;
import org.batfish.datamodel.Interface;
import org.batfish.datamodel.Ip;
import org.batfish.datamodel.UniverseIpSpace;
import org.batfish.datamodel.VrrpGroup;
import org.batfish.datamodel.collections.NodeInterfacePair;
import org.batfish.datamodel.table.ColumnMetadata;
import org.batfish.datamodel.table.Row;
import org.batfish.datamodel.table.Row.RowBuilder;
import org.batfish.specifier.AllInterfacesInterfaceSpecifier;
import org.batfish.specifier.AllNodesNodeSpecifier;
import org.batfish.specifier.ConstantIpSpaceSpecifier;
import org.batfish.specifier.MockSpecifierContext;
import org.batfish.specifier.NameNodeSpecifier;
import org.batfish.specifier.NameRegexInterfaceSpecifier;
Expand All @@ -36,9 +42,16 @@
public final class VrrpPropertiesAnswererTest {

private static final Map<String, ColumnMetadata> _columnMap =
VrrpPropertiesAnswerer.createTableMetadata(new VrrpPropertiesQuestion(null, null, false))
VrrpPropertiesAnswerer.createTableMetadata(
new VrrpPropertiesQuestion(null, null, null, false))
.toColumnMap();

private static RowBuilder createRowBuilder(String hostname, Interface iface) {
return Row.builder()
.put(COL_INTERFACE, NodeInterfacePair.of(hostname, iface.getName()))
.put(COL_ACTIVE, iface.getActive());
}

@Test
public void testGetProperties() {
Configuration conf1 = new Configuration("node1", ConfigurationFormat.CISCO_IOS);
Expand Down Expand Up @@ -79,21 +92,15 @@ public void testGetProperties() {
MockSpecifierContext ctxt =
MockSpecifierContext.builder().setConfigs(ImmutableMap.of("node1", conf1)).build();

RowBuilder expectedRow1Builder =
Row.builder().put(COL_INTERFACE, NodeInterfacePair.of("node1", "iface1"));
populateRow(expectedRow1Builder, 0, group);
Row expectedRow1 = expectedRow1Builder.build();

RowBuilder expectedRow2Builder =
Row.builder().put(COL_INTERFACE, NodeInterfacePair.of("node1", "iface2"));
populateRow(expectedRow2Builder, 0, group);
Row expectedRow2 = expectedRow2Builder.build();
Row expectedRow1 = populateRow(createRowBuilder("node1", iface1), 0, group).build();
Row expectedRow2 = populateRow(createRowBuilder("node1", iface2), 0, group).build();

assertThat(
getProperties(
ctxt,
new NameNodeSpecifier("node1"),
new NameRegexInterfaceSpecifier(Pattern.compile(".*")),
new ConstantIpSpaceSpecifier(UniverseIpSpace.INSTANCE),
false,
_columnMap),
equalTo(ImmutableMultiset.of(expectedRow1, expectedRow2)));
Expand All @@ -102,11 +109,74 @@ public void testGetProperties() {
ctxt,
new NameNodeSpecifier("node1"),
new NameRegexInterfaceSpecifier(Pattern.compile(".*")),
new ConstantIpSpaceSpecifier(UniverseIpSpace.INSTANCE),
true,
_columnMap),
equalTo(ImmutableMultiset.of(expectedRow1)));
}

@Test
public void testGetProperties_virtualAddressFiltering() {
Configuration conf = new Configuration("node", ConfigurationFormat.CISCO_IOS);

VrrpGroup group =
VrrpGroup.builder()
.setVirtualAddresses(ImmutableList.of(Ip.parse("1.1.1.1"), Ip.parse("2.2.2.2")))
.setSourceAddress(ConcreteInterfaceAddress.create(Ip.parse("1.1.1.2"), 29))
.setPriority(23)
.setPreempt(true)
.build();

Interface iface =
Interface.builder()
.setName("iface")
.setOwner(conf)
.setVrrpGroups(ImmutableSortedMap.of(0, group))
.setActive(true)
.build();

conf.getAllInterfaces().putAll(ImmutableMap.of(iface.getName(), iface));
MockSpecifierContext ctxt =
MockSpecifierContext.builder()
.setConfigs(ImmutableMap.of(conf.getHostname(), conf))
.build();

Row expectedRow = populateRow(createRowBuilder(conf.getHostname(), iface), 0, group).build();

// row is included when there is no address filtering
assertThat(
getProperties(
ctxt,
AllNodesNodeSpecifier.INSTANCE,
AllInterfacesInterfaceSpecifier.INSTANCE,
new ConstantIpSpaceSpecifier(UniverseIpSpace.INSTANCE),
false,
_columnMap),
equalTo(ImmutableMultiset.of(expectedRow)));

// row is included when one address matches
assertThat(
getProperties(
ctxt,
AllNodesNodeSpecifier.INSTANCE,
AllInterfacesInterfaceSpecifier.INSTANCE,
new ConstantIpSpaceSpecifier(Ip.parse("1.1.1.1").toIpSpace()),
false,
_columnMap),
equalTo(ImmutableMultiset.of(expectedRow)));

// row is not included no address matches
assertThat(
getProperties(
ctxt,
AllNodesNodeSpecifier.INSTANCE,
AllInterfacesInterfaceSpecifier.INSTANCE,
new ConstantIpSpaceSpecifier(Ip.parse("3.3.3.3").toIpSpace()),
false,
_columnMap),
equalTo(ImmutableMultiset.of()));
}

@Test
public void testPopulateRow() {
RowBuilder row = Row.builder(_columnMap);
Expand Down
8 changes: 8 additions & 0 deletions questions/experimental/vrrpProperties.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"excludeShutInterfaces": "${excludeShutInterfaces}",
"interfaces": "${interfaces}",
"nodes": "${nodes}",
"virtualAddresses": "${virtualAddresses}",
"instance": {
"description": "Returns configuration settings of VRRP groups.",
"instanceName": "vrrpProperties",
"longDescription": "Lists information about VRRP groups on interfaces.",
"orderedVariableNames": [
"nodes",
"interfaces",
"virtualAddresses",
"excludeShutInterfaces"
],
"tags": [
Expand All @@ -35,6 +37,12 @@
"type": "nodeSpec",
"optional": true,
"displayName": "Nodes"
},
"virtualAddresses": {
"description": "Include only groups with at least one virtual address matching this specifier",
"type": "ipSpaceSpec",
"optional": true,
"displayName": "Virtual Addresses"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/questions/experimental/commands
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ test -raw tests/questions/experimental/traceroute.ref validate-template tracerou
test -raw tests/questions/experimental/viModel.ref validate-template viModel nodes="n1"

# validate vrrpProperties
test -raw tests/questions/experimental/vrrpProperties.ref validate-template vrrpProperties excludeShutInterfaces=true, interfaces="i1", nodes="n1"
test -raw tests/questions/experimental/vrrpProperties.ref validate-template vrrpProperties excludeShutInterfaces=true, interfaces="i1", nodes="n1", virtualAddresses="1.1.1.1"

# validate vxlanVniProperties
test -raw tests/questions/experimental/vxlanVniProperties.ref validate-template vxlanVniProperties nodes="nodename", properties="VLAN"

0 comments on commit 9059813

Please sign in to comment.