package org.batfish.question; import java.util.LinkedHashSet; 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.Set; 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.PrefixSpace; import org.batfish.datamodel.Route; import org.batfish.datamodel.RoutingProtocol; import org.batfish.datamodel.Vrf; import org.batfish.datamodel.answers.AnswerElement; import org.batfish.datamodel.collections.RoutesByVrf; import org.batfish.datamodel.questions.Question; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; public class RoutesQuestionPlugin extends QuestionPlugin { public static class RoutesAnswerElement implements AnswerElement { private transient boolean _diff; private SortedMap<String, RoutesByVrf> _routesByHostname; @JsonCreator public RoutesAnswerElement() { _routesByHostname = new TreeMap<>(); } public RoutesAnswerElement(Map<String, Configuration> configurations, Pattern nodeRegex, Set<RoutingProtocol> protocols, PrefixSpace prefixSpace) { _routesByHostname = new TreeMap<>(); for (Entry<String, Configuration> e : configurations.entrySet()) { String hostname = e.getKey(); if (!nodeRegex.matcher(hostname).matches()) { continue; } Configuration c = e.getValue(); RoutesByVrf routesByVrf = new RoutesByVrf(); _routesByHostname.put(hostname, routesByVrf); for (Entry<String, Vrf> e2 : c.getVrfs().entrySet()) { String vrfName = e2.getKey(); Vrf vrf = e2.getValue(); SortedSet<Route> routes = vrf.getRoutes(); SortedSet<Route> filteredRoutes; if (protocols.isEmpty() && prefixSpace.isEmpty()) { filteredRoutes = routes; } else { filteredRoutes = new TreeSet<>(); for (Route route : routes) { boolean matchProtocol = protocols.isEmpty() || protocols.contains(route.getProtocol()); boolean matchPrefixSpace = prefixSpace.isEmpty() || prefixSpace.containsPrefix(route.getNetwork()); if (matchProtocol && matchPrefixSpace) { filteredRoutes.add(route); } } } routesByVrf.put(vrfName, filteredRoutes); } } } public RoutesAnswerElement(RoutesAnswerElement base, RoutesAnswerElement delta) { _diff = true; _routesByHostname = new TreeMap<>(); Set<String> hosts = new LinkedHashSet<>(); hosts.addAll(base.getRoutesByHostname().keySet()); hosts.addAll(delta.getRoutesByHostname().keySet()); for (String host : hosts) { RoutesByVrf routesByVrf = new RoutesByVrf(); _routesByHostname.put(host, routesByVrf); RoutesByVrf baseRoutesByVrf = base._routesByHostname.get(host); RoutesByVrf deltaRoutesByVrf = delta._routesByHostname.get(host); if (baseRoutesByVrf == null) { for (Entry<String, SortedSet<Route>> e : deltaRoutesByVrf .entrySet()) { String vrfName = e.getKey(); SortedSet<Route> deltaRoutes = e.getValue(); SortedSet<Route> routes = new TreeSet<>(); routesByVrf.put(vrfName, routes); for (Route deltaRoute : deltaRoutes) { deltaRoute.setDiffSymbol("+"); routes.add(deltaRoute); } } } else if (deltaRoutesByVrf == null) { for (Entry<String, SortedSet<Route>> e : baseRoutesByVrf .entrySet()) { String vrfName = e.getKey(); SortedSet<Route> baseRoutes = e.getValue(); SortedSet<Route> routes = new TreeSet<>(); routesByVrf.put(vrfName, routes); for (Route baseRoute : baseRoutes) { baseRoute.setDiffSymbol("-"); routes.add(baseRoute); } } } else { Set<String> vrfNames = new LinkedHashSet<>(); vrfNames.addAll(baseRoutesByVrf.keySet()); vrfNames.addAll(deltaRoutesByVrf.keySet()); for (String vrfName : vrfNames) { SortedSet<Route> routes = new TreeSet<>(); routesByVrf.put(vrfName, routes); SortedSet<Route> baseRoutes = baseRoutesByVrf.get(vrfName); SortedSet<Route> deltaRoutes = deltaRoutesByVrf.get(vrfName); if (baseRoutes == null) { for (Route deltaRoute : deltaRoutes) { deltaRoute.setDiffSymbol("+"); routes.add(deltaRoute); } } else if (deltaRoutes == null) { for (Route baseRoute : baseRoutes) { baseRoute.setDiffSymbol("-"); routes.add(baseRoute); } } else { Set<Route> tmpBaseRoutes = new LinkedHashSet<>(baseRoutes); baseRoutes.removeAll(deltaRoutes); deltaRoutes.removeAll(tmpBaseRoutes); for (Route baseRoute : baseRoutes) { baseRoute.setDiffSymbol("-"); routes.add(baseRoute); } for (Route deltaRoute : deltaRoutes) { deltaRoute.setDiffSymbol("+"); routes.add(deltaRoute); } } } } } } public RoutesAnswerElement( SortedMap<String, RoutesByVrf> environmentRoutesByHostname, Pattern nodeRegex, Set<RoutingProtocol> protocols) { _routesByHostname = new TreeMap<>(); for (Entry<String, RoutesByVrf> e : environmentRoutesByHostname .entrySet()) { String hostname = e.getKey(); if (!nodeRegex.matcher(hostname).matches()) { continue; } RoutesByVrf environmentRoutesByVrf = e.getValue(); RoutesByVrf routesByVrf = new RoutesByVrf(); _routesByHostname.put(hostname, routesByVrf); for (Entry<String, SortedSet<Route>> e2 : environmentRoutesByVrf .entrySet()) { String vrfName = e2.getKey(); SortedSet<Route> routes = e2.getValue(); if (!protocols.isEmpty()) { SortedSet<Route> filteredRoutes = new TreeSet<>(); for (Route environmentRoute : routes) { if (protocols.contains(environmentRoute.getProtocol())) { filteredRoutes.add(environmentRoute); } } routes = filteredRoutes; } routesByVrf.put(vrfName, routes); } } } public SortedMap<String, RoutesByVrf> getRoutesByHostname() { return _routesByHostname; } @Override public String prettyPrint() { StringBuilder sb = new StringBuilder(); for (Entry<String, RoutesByVrf> e : _routesByHostname.entrySet()) { RoutesByVrf routesByVrf = e.getValue(); for (SortedSet<Route> routes : routesByVrf.values()) { for (Route route : routes) { String routeStr = route.prettyPrint(_diff); sb.append(routeStr); } } } return sb.toString(); } public void setRoutesByHostname( SortedMap<String, RoutesByVrf> routesByHostname) { _routesByHostname = routesByHostname; } } public static class RoutesAnswerer extends Answerer { public RoutesAnswerer(Question question, IBatfish batfish) { super(question, batfish); } @Override public RoutesAnswerElement answer() { RoutesQuestion question = (RoutesQuestion) _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); } RoutesAnswerElement answerElement; if (question._fromEnvironment) { SortedMap<String, RoutesByVrf> environmentRoutes = _batfish .loadEnvironmentRoutingTables(); answerElement = new RoutesAnswerElement(environmentRoutes, nodeRegex, question._protocols); } else { _batfish.checkDataPlaneQuestionDependencies(); Map<String, Configuration> configurations = _batfish .loadConfigurations(); _batfish.initRoutes(configurations); answerElement = new RoutesAnswerElement(configurations, nodeRegex, question._protocols, question._prefixSpace); } return answerElement; } @Override public AnswerElement answerDiff() { _batfish.pushBaseEnvironment(); RoutesAnswerElement base = answer(); _batfish.popEnvironment(); _batfish.pushDeltaEnvironment(); RoutesAnswerElement delta = answer(); _batfish.popEnvironment(); return new RoutesAnswerElement(base, delta); } } // <question_page_comment> /** * Outputs all routes (RIB) at nodes in the network. * <p> * It produces routes from all protocols (e.g., BGP, OSPF, static, and * connected). * * @type Routes dataplane * * @param nodeRegex * Regular expression for names of nodes to include. Default value * is '.*' (all nodes). * * @example bf_answer("Nodes", nodeRegex="as1.*") Outputs the routes for all * nodes whose names begin with "as1". */ public static class RoutesQuestion extends Question { private static final String FROM_ENVIRONMENT_VAR = "fromEnvironment"; private static final String NODE_REGEX_VAR = "nodeRegex"; private static final String PREFIX_SPACE_VAR = "prefixSpace"; private static final String PROTOCOLS_VAR = "protocols"; private boolean _fromEnvironment; private String _nodeRegex; private PrefixSpace _prefixSpace; private SortedSet<RoutingProtocol> _protocols; public RoutesQuestion() { _nodeRegex = ".*"; _prefixSpace = new PrefixSpace(); _protocols = new TreeSet<>(); } @Override public boolean getDataPlane() { return !_fromEnvironment; } @JsonProperty(FROM_ENVIRONMENT_VAR) public boolean getFromEnvironment() { return _fromEnvironment; } @Override public String getName() { return "routes"; } @JsonProperty(NODE_REGEX_VAR) public String getNodeRegex() { return _nodeRegex; } @JsonProperty(PREFIX_SPACE_VAR) public PrefixSpace getPrefixSpace() { return _prefixSpace; } @JsonProperty(PROTOCOLS_VAR) public SortedSet<RoutingProtocol> getProtocols() { return _protocols; } @Override public boolean getTraffic() { return false; } @JsonProperty(FROM_ENVIRONMENT_VAR) public void setFromEnvironment(boolean fromEnvironment) { _fromEnvironment = fromEnvironment; } @JsonProperty(NODE_REGEX_VAR) public void setNodeRegex(String nodeRegex) { _nodeRegex = nodeRegex; } @JsonProperty(PREFIX_SPACE_VAR) public void setPrefixSpace(PrefixSpace prefixSpace) { _prefixSpace = prefixSpace; } @JsonProperty(PROTOCOLS_VAR) public void setProtocols(SortedSet<RoutingProtocol> protocols) { _protocols = protocols; } } @Override protected Answerer createAnswerer(Question question, IBatfish batfish) { return new RoutesAnswerer(question, batfish); } @Override protected Question createQuestion() { return new RoutesQuestion(); } }