package org.batfish.question;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.batfish.common.Answerer;
import org.batfish.common.BatfishException;
import org.batfish.common.plugin.IBatfish;
import org.batfish.datamodel.Configuration;
import org.batfish.datamodel.ConnectedRoute;
import org.batfish.datamodel.Interface;
import org.batfish.datamodel.OspfExternalRoute;
import org.batfish.datamodel.OspfProcess;
import org.batfish.datamodel.Prefix;
import org.batfish.datamodel.Vrf;
import org.batfish.datamodel.answers.AnswerElement;
import org.batfish.datamodel.questions.Question;
import org.batfish.datamodel.routing_policy.RoutingPolicy;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
public class OspfLoopbacksQuestionPlugin extends QuestionPlugin {
public static class OspfLoopbacksAnswerElement implements AnswerElement {
private SortedMap<String, SortedSet<String>> _active;
private SortedMap<String, SortedSet<String>> _exported;
private SortedMap<String, SortedSet<String>> _missing;
private SortedMap<String, SortedSet<String>> _passive;
private SortedMap<String, SortedSet<String>> _running;
public OspfLoopbacksAnswerElement() {
_active = new TreeMap<>();
_exported = new TreeMap<>();
_missing = new TreeMap<>();
_passive = new TreeMap<>();
_running = new TreeMap<>();
}
public void add(SortedMap<String, SortedSet<String>> map, String hostname,
String interfaceName) {
SortedSet<String> interfacesByHostname = map.get(hostname);
if (interfacesByHostname == null) {
interfacesByHostname = new TreeSet<>();
map.put(hostname, interfacesByHostname);
}
interfacesByHostname.add(interfaceName);
}
@JsonIgnore
public SortedMap<String, SortedSet<String>> getActive() {
return _active;
}
@JsonIgnore
public SortedMap<String, SortedSet<String>> getExported() {
return _exported;
}
public SortedMap<String, SortedSet<String>> getMissing() {
return _missing;
}
@JsonIgnore
public SortedMap<String, SortedSet<String>> getPassive() {
return _passive;
}
@JsonIgnore
public SortedMap<String, SortedSet<String>> getRunning() {
return _running;
}
private Object interfacesToString(String indent, String header,
SortedMap<String, SortedSet<String>> interfaces) {
StringBuilder sb = new StringBuilder(indent + header + "\n");
for (String node : interfaces.keySet()) {
for (String iface : interfaces.get(node)) {
sb.append(indent + indent + node + " : " + iface + "\n");
}
}
return sb.toString();
}
@Override
public String prettyPrint() {
StringBuilder sb = new StringBuilder(
"Results for OSPF loopbacks check\n");
// if (_active.size() > 0) {
// sb.append(interfacesToString(" ", "Active loopbacks", _active));
// }
// if (_exported.size() > 0) {
// sb.append(
// interfacesToString(" ", "Exported loopbacks", _exported));
// }
if (_missing.size() > 0) {
sb.append(interfacesToString(" ", "Missing loopbacks", _missing));
}
// if (_passive.size() > 0) {
// sb.append(interfacesToString(" ", "Passive loopbacks", _passive));
// }
// if (_running.size() > 0) {
// sb.append(interfacesToString(" ", "Running loopbacks", _running));
// }
return sb.toString();
}
@JsonIgnore
public void setActive(SortedMap<String, SortedSet<String>> active) {
_active = active;
}
@JsonIgnore
public void setExported(SortedMap<String, SortedSet<String>> exported) {
_exported = exported;
}
public void setMissing(SortedMap<String, SortedSet<String>> missing) {
_missing = missing;
}
@JsonIgnore
public void setPassive(SortedMap<String, SortedSet<String>> passive) {
_passive = passive;
}
@JsonIgnore
public void setRunning(SortedMap<String, SortedSet<String>> running) {
_running = running;
}
}
public static class OspfLoopbacksAnswerer extends Answerer {
public OspfLoopbacksAnswerer(Question question, IBatfish batfish) {
super(question, batfish);
}
@Override
public AnswerElement answer() {
OspfLoopbacksQuestion question = (OspfLoopbacksQuestion) _question;
Pattern nodeRegex;
try {
nodeRegex = Pattern.compile(question.getNodeRegex());
}
catch (PatternSyntaxException e) {
throw new BatfishException(
"Supplied regex for nodes is not a valid java regex: \""
+ question.getNodeRegex() + "\"",
e);
}
OspfLoopbacksAnswerElement answerElement = new OspfLoopbacksAnswerElement();
_batfish.checkConfigurations();
Map<String, Configuration> configurations = _batfish
.loadConfigurations();
for (Entry<String, Configuration> e : configurations.entrySet()) {
String hostname = e.getKey();
if (!nodeRegex.matcher(hostname).matches()) {
continue;
}
Configuration c = e.getValue();
for (Vrf vrf : c.getVrfs().values()) {
for (Entry<String, Interface> e2 : vrf.getInterfaces()
.entrySet()) {
String interfaceName = e2.getKey();
Interface iface = e2.getValue();
if (iface.isLoopback(c.getConfigurationFormat())) {
if (iface.getOspfEnabled()) {
// ospf is running either passively or actively
answerElement.add(answerElement.getRunning(), hostname,
interfaceName);
if (iface.getOspfPassive()) {
answerElement.add(answerElement.getPassive(),
hostname, interfaceName);
}
else {
answerElement.add(answerElement.getActive(),
hostname, interfaceName);
}
}
else {
// check if exported as external ospf route
boolean exported = false;
OspfProcess proc = vrf.getOspfProcess();
if (proc != null) {
String exportPolicyName = proc.getExportPolicy();
if (exportPolicyName != null) {
RoutingPolicy exportPolicy = c
.getRoutingPolicies().get(exportPolicyName);
if (exportPolicy != null) {
for (Prefix prefix : iface.getAllPrefixes()) {
ConnectedRoute route = new ConnectedRoute(
prefix, interfaceName);
if (exportPolicy.process(route,
new OspfExternalRoute.Builder(), null,
vrf.getName())) {
exported = true;
}
}
}
}
if (exported) {
answerElement.add(answerElement.getExported(),
hostname, interfaceName);
}
else {
// not exported, so should be inactive
answerElement.add(answerElement.getMissing(),
hostname, interfaceName);
}
}
}
}
}
}
}
return answerElement;
}
}
// <question_page_comment>
/**
* Lists which loopbacks interfaces are being announced into OSPF.
* <p>
* When running OSPF, it is a good practice to announce loopbacks interface
* IPs into OSPF. This question produces the list of nodes for which such
* announcements are happening.
*
* @type OspfLoopbacks onefile
*
* @param nodeRegex
* Regular expression for names of nodes to include. Default value
* is '.*' (all nodes).
*
* @example bf_answer("OspfLoopbacks", nodeRegex='as2.*') Answers the
* question only for nodes whose names start with 'as2'.
*/
public static class OspfLoopbacksQuestion extends Question {
private static final String NODE_REGEX_VAR = "nodeRegex";
private String _nodeRegex;
public OspfLoopbacksQuestion() {
_nodeRegex = ".*";
}
@Override
public boolean getDataPlane() {
return false;
}
@Override
public String getName() {
return "ospfloopbacks";
}
@JsonProperty(NODE_REGEX_VAR)
public String getNodeRegex() {
return _nodeRegex;
}
@Override
public boolean getTraffic() {
return false;
}
@Override
public String prettyPrint() {
String retString = String.format("ospfLoopbacks %snodeRegex=\"%s\"",
prettyPrintBase(), _nodeRegex);
return retString;
}
@JsonProperty(NODE_REGEX_VAR)
public void setNodeRegex(String nodeRegex) {
_nodeRegex = nodeRegex;
}
}
@Override
protected Answerer createAnswerer(Question question, IBatfish batfish) {
return new OspfLoopbacksAnswerer(question, batfish);
}
@Override
protected Question createQuestion() {
return new OspfLoopbacksQuestion();
}
}