From ce25649ff6cdbcb9dd14090764f13fa453ef93fd Mon Sep 17 00:00:00 2001 From: Michele Santuari Date: Fri, 23 Jun 2017 14:44:01 +0200 Subject: [PATCH] From FlowRule to static routes for Juniper Conversion of FlowRules into static routes and retrieve of installed static routes as FlowRules. Change-Id: If960e4d15e431ae8f5ea75eff83913056a51c852 --- .../drivers/juniper/FlowRuleJuniperImpl.java | 425 ++++++++++++++++++ .../drivers/juniper/JuniperUtils.java | 148 ++++++ .../drivers/juniper/StaticRoute.java | 137 ++++++ .../src/main/resources/juniper-drivers.xml | 2 + 4 files changed, 712 insertions(+) create mode 100644 drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java create mode 100644 drivers/juniper/src/main/java/org/onosproject/drivers/juniper/StaticRoute.java diff --git a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java new file mode 100644 index 0000000000..ae9d7e91df --- /dev/null +++ b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java @@ -0,0 +1,425 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.drivers.juniper; + +import com.google.common.annotations.Beta; +import com.google.common.base.Strings; +import org.apache.commons.lang.StringUtils; +import org.onlab.packet.Ip4Address; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Link; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleProgrammable; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.criteria.IPCriterion; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.link.LinkService; +import org.onosproject.netconf.DatastoreId; +import org.onosproject.netconf.NetconfController; +import org.onosproject.netconf.NetconfException; +import org.onosproject.netconf.NetconfSession; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.onosproject.drivers.juniper.JuniperUtils.OperationType; +import static org.onosproject.drivers.juniper.JuniperUtils.OperationType.ADD; +import static org.onosproject.drivers.juniper.JuniperUtils.OperationType.REMOVE; +import static org.onosproject.drivers.juniper.JuniperUtils.commitBuilder; +import static org.onosproject.drivers.juniper.JuniperUtils.rollbackBuilder; +import static org.onosproject.drivers.juniper.JuniperUtils.routeAddBuilder; +import static org.onosproject.drivers.juniper.JuniperUtils.routeDeleteBuilder; +import static org.onosproject.drivers.utilities.XmlConfigParser.loadXmlString; +import static org.onosproject.net.flow.FlowEntry.FlowEntryState.PENDING_REMOVE; +import static org.onosproject.net.flow.FlowEntry.FlowEntryState.REMOVED; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Conversion of FlowRules into static routes and retrieve of installed + * static routes as FlowRules. + * The selector of the FlowRule must contains the IPv4 address + * {@link org.onosproject.net.flow.TrafficSelector.Builder#matchIPDst(org.onlab.packet.IpPrefix)} + * of the host to connect and the treatment must include an + * output port {@link org.onosproject.net.flow.TrafficTreatment.Builder#setOutput(PortNumber)} + * All other instructions in the selector and treatment are ignored. + *

+ * This implementation requires an IP adjacency + * (e.g., IP link discovered by {@link LinkDiscoveryJuniperImpl}) between the routers + * to find the next hop IP address. + */ +@Beta +public class FlowRuleJuniperImpl extends AbstractHandlerBehaviour + implements FlowRuleProgrammable { + + private static final String OK = ""; + public static final String IP_STRING = "ip"; + private final org.slf4j.Logger log = getLogger(getClass()); + + @Override + public Collection getFlowEntries() { + + DeviceId devId = checkNotNull(this.data().deviceId()); + NetconfController controller = checkNotNull(handler().get(NetconfController.class)); + NetconfSession session = controller.getDevicesMap().get(devId).getSession(); + if (session == null) { + log.warn("Device {} is not registered in netconf", devId); + return Collections.EMPTY_LIST; + } + + //Installed static routes + String reply; + try { + reply = session.get(routingTableBuilder()); + } catch (IOException e) { + throw new RuntimeException(new NetconfException("Failed to retrieve configuration.", + e)); + } + Collection devicesStaticRoutes = + JuniperUtils.parseRoutingTable(loadXmlString(reply)); + + //Expected FlowEntries installed + FlowRuleService flowRuleService = this.handler().get(FlowRuleService.class); + Iterable flowEntries = flowRuleService.getFlowEntries(devId); + + Collection installedRules = new HashSet<>(); + flowEntries.forEach(flowEntry -> { + Optional ipCriterion = getIpCriterion(flowEntry); + if (!ipCriterion.isPresent()) { + return; + } + + Optional output = getOutput(flowEntry); + if (!output.isPresent()) { + return; + } + //convert FlowRule into static route + getStaticRoute(devId, ipCriterion.get(), output.get(), flowEntry.priority()).ifPresent(staticRoute -> { + //Two type of FlowRules: + //1. FlowRules to forward to a remote subnet: they are translated into static route + // configuration. So a removal request will be processed. + //2. FlowRules to forward on a subnet directly attached to the router (Generally speaking called local): + // those routes do not require any configuration because the router is already able to forward on + // directly attached subnet. In this case, when the driver receive the request to remove, + // it will report as removed. + + if (staticRoute.isLocalRoute()) { + //if the FlowRule is in PENDING_REMOVE or REMOVED, it is not reported. + if (flowEntry.state() == PENDING_REMOVE || flowEntry.state() == REMOVED) { + devicesStaticRoutes.remove(staticRoute); + } else { + //FlowRule is reported installed + installedRules.add(flowEntry); + devicesStaticRoutes.remove(staticRoute); + } + + } else { + //if the route is found in the device, the FlowRule is reported installed. + if (devicesStaticRoutes.contains(staticRoute)) { + installedRules.add(flowEntry); + devicesStaticRoutes.remove(staticRoute); + } + } + }); + }); + + if (!devicesStaticRoutes.isEmpty()) { + log.info("Found static routes on device {} not installed by ONOS: {}", + devId, devicesStaticRoutes); +// FIXME: enable configuration to purge already installed flows. +// It cannot be allowed by default because it may remove needed management routes +// log.warn("Removing from device {} the FlowEntries not expected {}", deviceId, devicesStaticRoutes); +// devicesStaticRoutes.forEach(staticRoute -> editRoute(session, REMOVE, staticRoute)); + } + return installedRules; + } + + @Override + public Collection applyFlowRules(Collection rules) { + return manageRules(rules, ADD); + } + + @Override + public Collection removeFlowRules(Collection rules) { + return manageRules(rules, REMOVE); + } + + private Collection manageRules(Collection rules, OperationType type) { + + DeviceId deviceId = this.data().deviceId(); + + log.debug("{} flow entries to NETCONF device {}", type, deviceId); + NetconfController controller = checkNotNull(handler() + .get(NetconfController.class)); + NetconfSession session = controller.getDevicesMap().get(deviceId) + .getSession(); + Collection managedRules = new HashSet<>(); + + for (FlowRule flowRule : rules) { + + Optional ipCriterion = getIpCriterion(flowRule); + if (!ipCriterion.isPresent()) { + log.error("Currently not supported: IPv4 destination match must be used"); + continue; + } + + Optional output = getOutput(flowRule); + if (!output.isPresent()) { + log.error("Currently not supported: the output action is needed"); + continue; + } + + getStaticRoute(deviceId, ipCriterion.get(), output.get(), flowRule.priority()).ifPresent( + staticRoute -> { + //If the static route is not local, the driver tries to install + if (!staticRoute.isLocalRoute()) { + //Only if the installation is successful, the driver report the + // FlowRule as installed. + if (editRoute(session, type, staticRoute)) { + managedRules.add(flowRule); + } + //If static route are local, they are not installed + // because are not required. Directly connected routes + //are automatically forwarded. + } else { + managedRules.add(flowRule); + } + } + ); + } + return rules; + } + + private boolean editRoute(NetconfSession session, OperationType type, + StaticRoute staticRoute) { + try { + boolean reply = false; + if (type == ADD) { + reply = session + .editConfig(DatastoreId.CANDIDATE, "merge", + routeAddBuilder(staticRoute)); + } else if (type == REMOVE) { + reply = session + .editConfig(DatastoreId.CANDIDATE, "none", routeDeleteBuilder(staticRoute)); + } + if (reply && commit()) { + return true; + } else { + if (!rollback()) { + log.error("Something went wrong in the configuration and impossible to rollback"); + } else { + log.error("Something went wrong in the configuration: a static route has not been {} {}", + type == ADD ? "added" : "removed", staticRoute); + } + } + } catch (IOException e) { + throw new RuntimeException(new NetconfException("Failed to retrieve configuration.", + e)); + } + return false; + } + + /** + * Helper method to convert FlowRule into an abstraction of static route + * {@link StaticRoute}. + * + * @param devId the device id + * @param criteria the IP destination criteria + * @param output the output instruction + * @return optional of Static Route + */ + private Optional getStaticRoute(DeviceId devId, + IPCriterion criteria, + OutputInstruction output, + int priority) { + + DeviceService deviceService = this.handler().get(DeviceService.class); + Collection ports = deviceService.getPorts(devId); + Optional port = ports.stream().filter(x -> x.number().equals(output.port())).findAny(); + if (!port.isPresent()) { + log.error("The port {} does not exist in the device", + output.port()); + return Optional.empty(); + } + + //Find if the route refers to a local interface. + Optional local = deviceService.getPorts(devId).stream().filter(this::isIp) + .filter(p -> criteria.ip().getIp4Prefix().contains( + Ip4Address.valueOf(p.annotations().value(IP_STRING)))).findAny(); + + if (local.isPresent()) { + return Optional.of(new StaticRoute(criteria.ip().getIp4Prefix(), + criteria.ip().getIp4Prefix().address(), true, priority)); + } + + Optional nextHop = findIpDst(devId, port.get()); + if (nextHop.isPresent()) { + return Optional.of( + new StaticRoute(criteria.ip().getIp4Prefix(), nextHop.get(), false, priority)); + } else { + log.error("The destination interface has not an IP {}", port.get()); + return Optional.empty(); + } + + } + + /** + * Helper method to get the IP destination criterion given a flow rule. + * + * @param flowRule the flow rule + * @return optional of IP destination criterion + */ + private Optional getIpCriterion(FlowRule flowRule) { + + Criterion ip = flowRule.selector().getCriterion(Criterion.Type.IPV4_DST); + return Optional.ofNullable((IPCriterion) ip); + } + + /** + * Helper method to get the output instruction given a flow rule. + * + * @param flowRule the flow rule + * @return the output instruction + */ + private Optional getOutput(FlowRule flowRule) { + Optional output = flowRule + .treatment().allInstructions().stream() + .filter(instruction -> instruction + .type() == Instruction.Type.OUTPUT) + .map(x -> (OutputInstruction) x).findFirst(); + return output; + } + + private String routingTableBuilder() { + StringBuilder rpc = new StringBuilder(""); + rpc.append(""); + rpc.append(""); + return rpc.toString(); + } + + private boolean commit() { + NetconfController controller = checkNotNull(handler() + .get(NetconfController.class)); + NetconfSession session = controller.getDevicesMap() + .get(handler().data().deviceId()).getSession(); + + String replay; + try { + replay = session.get(commitBuilder()); + } catch (IOException e) { + throw new RuntimeException(new NetconfException("Failed to retrieve configuration.", + e)); + } + + if (replay != null && replay.indexOf(OK) >= 0) { + return true; + } + return false; + } + + private boolean rollback() { + NetconfController controller = checkNotNull(handler() + .get(NetconfController.class)); + NetconfSession session = controller.getDevicesMap() + .get(handler().data().deviceId()).getSession(); + + String replay; + try { + replay = session.get(rollbackBuilder(0)); + } catch (IOException e) { + throw new RuntimeException(new NetconfException("Failed to retrieve configuration.", + e)); + } + + if (replay != null && replay.indexOf(OK) >= 0) { + return true; + } + return false; + } + + /** + * Helper method to find the next hop IP address. + * The logic is to check if the destination ports have an IP address + * by checking the logical interface (e.g., for port physical ge-2/0/1, + * a logical interface may be ge-2/0/1.0 + * + * @param deviceId the device id of the flow rule to be installed + * @param port output port of the flow rule treatment + * @return optional IPv4 address of a next hop + */ + private Optional findIpDst(DeviceId deviceId, Port port) { + LinkService linkService = this.handler().get(LinkService.class); + Set links = linkService.getEgressLinks(new ConnectPoint(deviceId, port.number())); + DeviceService deviceService = this.handler().get(DeviceService.class); + //Using only links with adjacency discovered by the LLDP protocol (see LinkDiscoveryJuniperImpl) + Map dstPorts = links.stream().filter(l -> + IP_STRING.toUpperCase().equals(l.annotations().value("layer"))) + .collect(Collectors.toMap( + l -> l.dst().deviceId(), + l -> deviceService.getPort(l.dst().deviceId(), l.dst().port()))); + for (Map.Entry entry : dstPorts.entrySet()) { + String portName = entry.getValue().annotations().value(AnnotationKeys.PORT_NAME); + + Optional childPort = deviceService.getPorts(entry.getKey()).stream() + .filter(p -> Strings.nullToEmpty( + p.annotations().value(AnnotationKeys.PORT_NAME)).contains(portName.trim())) + .filter(p -> isIp(p)) + .findAny(); + if (childPort.isPresent()) { + return Optional.ofNullable(Ip4Address.valueOf(childPort.get().annotations().value("ip"))); + } + } + + return Optional.empty(); + } + + /** + * Helper method to find if an interface has an IP address. + * It will check the annotations of the port. + * + * @param port the port + * @return true if the IP address is present. Otherwise false. + */ + private boolean isIp(Port port) { + String ip4 = port.annotations().value(IP_STRING); + if (StringUtils.isEmpty(ip4)) { + return false; + } + try { + + Ip4Address.valueOf(port.annotations().value(IP_STRING)); + } catch (IllegalArgumentException e) { + return false; + } + return true; + } +} diff --git a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java index 3dc94df6a2..a67c2f64d8 100644 --- a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java +++ b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java @@ -17,7 +17,10 @@ package org.onosproject.drivers.juniper; import org.apache.commons.configuration.HierarchicalConfiguration; +import org.apache.commons.lang.StringUtils; import org.onlab.packet.ChassisId; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; import org.onlab.packet.MacAddress; import org.onosproject.net.AnnotationKeys; import org.onosproject.net.ConnectPoint; @@ -39,18 +42,23 @@ import org.slf4j.Logger; import com.google.common.base.Strings; import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.onosproject.drivers.juniper.StaticRoute.DEFAULT_METRIC_STATIC_ROUTE; +import static org.onosproject.drivers.juniper.StaticRoute.toFlowRulePriority; import static org.onosproject.net.Device.Type.ROUTER; import static org.onosproject.net.PortNumber.portNumber; import static org.slf4j.LoggerFactory.getLogger; // Ref: Junos YANG: // https://github.com/Juniper/yang + /** * Utility class for Netconf XML for Juniper. * Tested with MX240 junos 14.2 @@ -96,6 +104,8 @@ public final class JuniperUtils { private static final Pattern ADD_PATTERN = Pattern.compile(REGEX_ADD, Pattern.DOTALL); + public static final String PROTOCOL_NAME = "protocol-name"; + private static final String JUNIPER = "JUNIPER"; private static final String UNKNOWN = "UNKNOWN"; @@ -160,6 +170,80 @@ public final class JuniperUtils { request + RPC_CLOSE_TAG; } + /** + * Helper method to commit a config. + * + * @return string contains the result of the commit + */ + public static String commitBuilder() { + return RPC_TAG_NETCONF_BASE + + "" + RPC_CLOSE_TAG; + } + + /** + * Helper method to build the schema for returning to a previously + * committed configuration. + * + * @param versionToReturn Configuration to return to. The range of values is from 0 through 49. + * The most recently saved configuration is number 0, + * and the oldest saved configuration is number 49. + * @return string containing the XML schema + */ + public static String rollbackBuilder(int versionToReturn) { + return RPC_TAG_NETCONF_BASE + + "" + + "" + versionToReturn + "" + + "" + + RPC_CLOSE_TAG; + } + + + /** + * Helper method to build an XML schema to configure a static route + * given a {@link StaticRoute}. + * + * @param staticRoute the static route to be configured + * @return string contains the result of the configuration + */ + public static String routeAddBuilder(StaticRoute staticRoute) { + StringBuilder rpc = new StringBuilder("\n"); + rpc.append("\n"); + rpc.append("\n"); + rpc.append("\n"); + rpc.append("" + staticRoute.ipv4Dst().toString() + "\n"); + rpc.append("" + staticRoute.nextHop() + "\n"); + + if (staticRoute.getMetric() != DEFAULT_METRIC_STATIC_ROUTE) { + rpc.append("" + staticRoute.getMetric() + ""); + } + + rpc.append("\n"); + rpc.append("\n"); + rpc.append("\n"); + rpc.append("\n"); + + return rpc.toString(); + } + + /** + * Helper method to build a XML schema to delete a static route + * given a {@link StaticRoute}. + * @param staticRoute the static route to be deleted + * @return string contains the result of the configuratio + */ + public static String routeDeleteBuilder(StaticRoute staticRoute) { + return "\n" + + "\n" + + "\n" + + "\n" + + "" + staticRoute.ipv4Dst().toString() + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + } + + /** * Parses device configuration and returns the device description. * @@ -458,4 +542,68 @@ public final class JuniperUtils { this.remotePortIndex = pIndex; } } + + protected enum OperationType { + ADD, + REMOVE, + } + + /** + * Parses {@literal route-information} tree. + * This implementation supports only static routes. + * + * @param cfg route-information + * @return a collection of static routes + */ + public static Collection parseRoutingTable(HierarchicalConfiguration cfg) { + + Collection staticRoutes = new HashSet<>(); + HierarchicalConfiguration routeInfo = + cfg.configurationAt("route-information"); + List routeTables = routeInfo.configurationsAt("route-table"); + for (HierarchicalConfiguration routeTable : routeTables) { + List routes = routeTable.configurationsAt("rt"); + for (HierarchicalConfiguration route : routes) { + if (route != null) { + HierarchicalConfiguration rtEntry = route.configurationAt("rt-entry"); + if (rtEntry.getString(PROTOCOL_NAME) != null && + rtEntry.getString(PROTOCOL_NAME).contains("Static")) { + parseStaticRoute(rtEntry, + route.getString("rt-destination"), + rtEntry.getString("metric")) + .ifPresent(x -> staticRoutes.add(x)); + + } + } + } + } + return staticRoutes; + } + + /** + * Parse the {@literal rt-entry} for static routes. + * + * @param rtEntry rt-entry filtered by {@literal protocol-name} equals to Static + * @param destination rt-destination + * @return optional of static route + */ + private static Optional parseStaticRoute(HierarchicalConfiguration rtEntry, + String destination, String metric) { + + Ip4Prefix ipDst = Ip4Prefix.valueOf(destination); + + HierarchicalConfiguration nextHop = rtEntry.configurationAt("nh"); + String to = nextHop.getString("to"); + if (StringUtils.isEmpty(to)) { + return Optional.empty(); + } + Ip4Address nextHopIp = Ip4Address.valueOf(to); + + if (metric == null) { + return Optional.of(new StaticRoute(ipDst, nextHopIp, false)); + } else { + return Optional.of(new StaticRoute(ipDst, nextHopIp, false, + toFlowRulePriority(Integer.parseInt(metric)))); + } + } } diff --git a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/StaticRoute.java b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/StaticRoute.java new file mode 100644 index 0000000000..f83d0bb175 --- /dev/null +++ b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/StaticRoute.java @@ -0,0 +1,137 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.drivers.juniper; + +import com.google.common.annotations.Beta; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static org.onosproject.net.intent.Intent.DEFAULT_INTENT_PRIORITY; + + +/** + * Definition of a static route for the Juniper. + */ +@Beta +public class StaticRoute { + + /** + * Maximum metric value for the Juniper. + */ + private static final int BEST_STATIC_METRIC = 1; + + /** + * Max FlowRule priority that corresponds to the BEST_STATIC_METRIC of the Juniper. + * DEFAULT_INTENT_PRIORITY + DEFAULT_METRIC_STATIC_ROUTE - BEST_STATIC_METRIC + */ + private static final int MAX_FLOWRULE_PRIORITY_ACCEPTED = 104; + + /** + * Default static route metric. + */ + public static final int DEFAULT_METRIC_STATIC_ROUTE = 5; + + private final Ip4Prefix ipv4Dest; + private final Ip4Address nextHop; + private final int priority; + private final boolean localRoute; + + public StaticRoute(Ip4Prefix ipv4Dest, Ip4Address nextHop, boolean localRoute, int priority) { + this.ipv4Dest = ipv4Dest; + this.nextHop = nextHop; + this.priority = priority; + this.localRoute = localRoute; + } + + public StaticRoute(Ip4Prefix ipv4Dest, Ip4Address nextHop, boolean localRoute) { + this(ipv4Dest, nextHop, localRoute, DEFAULT_INTENT_PRIORITY); + } + + public Ip4Prefix ipv4Dst() { + return ipv4Dest; + } + + public Ip4Address nextHop() { + return nextHop; + } + + /** + * Method to covert the priority into the Juniper metric. + * See https://goo.gl/ACo952. + * @return the metric for the Juniper + */ + public int getMetric() { + if (priority > MAX_FLOWRULE_PRIORITY_ACCEPTED) { + return BEST_STATIC_METRIC; + } + return DEFAULT_INTENT_PRIORITY + DEFAULT_METRIC_STATIC_ROUTE - priority; + } + + /** + * Method to convert the Juniper metric into a priority. + * + * @param metric the metric to be converted + * @return the priority + */ + public static int toFlowRulePriority(int metric) { + return DEFAULT_INTENT_PRIORITY + DEFAULT_METRIC_STATIC_ROUTE - metric; + } + + /** + * Directly attached routes are called local route. Such local routes are directly discovered by + * the router itself. + * + * @return true if it is a local route + */ + public boolean isLocalRoute() { + return localRoute; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof StaticRoute) { + StaticRoute that = (StaticRoute) obj; + return Objects.equals(ipv4Dest, that.ipv4Dest) && + Objects.equals(nextHop, that.nextHop) && + Objects.equals(priority, that.priority) && + Objects.equals(localRoute, that.localRoute); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(ipv4Dest, nextHop, priority, localRoute); + } + + @Override + public String toString() { + return toStringHelper(this) + .omitNullValues() + .addValue(localRoute ? "Local route" : null) + .add("IP address destination", ipv4Dest) + .add("Next Hop IP address", nextHop) + .add("Priority", priority) + .toString(); + } +} diff --git a/drivers/juniper/src/main/resources/juniper-drivers.xml b/drivers/juniper/src/main/resources/juniper-drivers.xml index a4b1150b3d..f5d3e0e3c6 100644 --- a/drivers/juniper/src/main/resources/juniper-drivers.xml +++ b/drivers/juniper/src/main/resources/juniper-drivers.xml @@ -19,6 +19,8 @@ hwVersion="" swVersion="JunOS"> +