ONOS-6323 Revise Juniper driver
- use snmp-index as port number, which seems to be unique.
duplicates were observed for local-index
- physical-interface Port's enabled state is admin-status & oper-status (was only admin-status)
- logical-interface Port's enabled state is above & if-config-flags.contains(iff-up)
Note: local loopback also falls into this category
- logical-interface Port's portSpeed is inherited from parent physical interface speed.
Change-Id: Ie70d07589db01f6b394c90cb1e599b5eb2f8ec91
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 efd5f50..2aeea4f 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
@@ -16,17 +16,18 @@
package org.onosproject.drivers.juniper;
-import com.google.common.collect.Lists;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.onlab.packet.ChassisId;
import org.onlab.packet.MacAddress;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultAnnotations.Builder;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.Port.Type;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
@@ -35,19 +36,21 @@
import org.onosproject.net.link.LinkDescription;
import org.slf4j.Logger;
+import com.google.common.base.Strings;
+
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static java.lang.Integer.parseInt;
-import static org.onosproject.net.DefaultAnnotations.Builder;
import static org.onosproject.net.Device.Type.ROUTER;
-import static org.onosproject.net.Port.Type.COPPER;
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
@@ -77,21 +80,14 @@
private static final String SER_NUM = "serial-number";
private static final String IF_INFO = "interface-information";
private static final String IF_PHY = "physical-interface";
+
private static final String IF_TYPE = "if-type";
private static final String SPEED = "speed";
- private static final String ETH = "Ethernet";
- private static final String MBPS = "mbps";
private static final String NAME = "name";
- private static final String IF_LO_ENCAP = "logical-interface.encapsulation";
- private static final String IF_LO_NAME = "logical-interface.name";
- private static final String IF_LO_ADD =
- "logical-interface.address-family.interface-address.ifa-local";
- private static final String LO_INDEX = "local-index";
- private static final String STATUS = "admin-status";
+
+ // seems to be unique index within device
private static final String SNMP_INDEX = "snmp-index";
- private static final String IF_LO_INDEX = "logical-interface.local-index";
- private static final String IF_LO_STATUS =
- "logical-interface.if-config-flags.iff-up";
+
private static final String LLDP_LO_PORT = "lldp-local-port-id";
private static final String LLDP_REM_CHASS = "lldp-remote-chassis-id";
private static final String LLDP_REM_PORT = "lldp-remote-port-id";
@@ -102,6 +98,50 @@
private static final String JUNIPER = "JUNIPER";
private static final String UNKNOWN = "UNKNOWN";
+
+ /**
+ * Annotation key for interface type.
+ */
+ static final String AK_IF_TYPE = "ifType";
+
+ /**
+ * Annotation key for Logical link-layer encapsulation.
+ */
+ static final String AK_ENCAPSULATION = "encapsulation";
+
+ /**
+ * Annotation key for interface description.
+ */
+ static final String AK_DESCRIPTION = "description";
+
+ /**
+ * Annotation key for interface admin status. "up"/"down"
+ */
+ static final String AK_ADMIN_STATUS = "adminStatus";
+
+ /**
+ * Annotation key for interface operational status. "up"/"down"
+ */
+ static final String AK_OPER_STATUS = "operStatus";
+
+ /**
+ * Annotation key for logical-interfaces parent physical interface name.
+ */
+ static final String AK_PHYSICAL_PORT_NAME = "physicalPortName";
+
+
+ private static final String NUMERIC_SPEED_REGEXP = "(\\d+)([GM])bps";
+
+ /**
+ * {@value #NUMERIC_SPEED_REGEXP} as {@link Pattern}.
+ * Case insensitive
+ */
+ private static final Pattern SPEED_PATTERN =
+ Pattern.compile(NUMERIC_SPEED_REGEXP, Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Default port speed {@value} Mbps.
+ */
private static final long DEFAULT_PORT_SPEED = 1000;
@@ -164,104 +204,187 @@
public static List<PortDescription> parseJuniperPorts(HierarchicalConfiguration cfg) {
//This methods ignores some internal ports
- List<PortDescription> portDescriptions = Lists.newArrayList();
+ List<PortDescription> portDescriptions = new ArrayList<>();
List<HierarchicalConfiguration> subtrees =
cfg.configurationsAt(IF_INFO);
for (HierarchicalConfiguration interfInfo : subtrees) {
List<HierarchicalConfiguration> interfaceTree =
interfInfo.configurationsAt(IF_PHY);
- for (HierarchicalConfiguration interf : interfaceTree) {
- if (interf != null) {
- if (interf.getString(IF_TYPE) != null &&
- interf.getString(SPEED) != null) {
- if (interf.getString(IF_TYPE).contains(ETH) &&
- interf.getString(SPEED).contains(MBPS)) {
- portDescriptions.add(parseDefaultPort(interf));
- } else {
- log.debug("Ignoring default port candidate {}",
- interf.getString(NAME));
- }
- } else if (interf.getString(IF_LO_ENCAP) != null &&
- !interf.getString(NAME).contains("pfe") &&
- interf.getString(IF_LO_ENCAP).contains("ENET2")) {
- portDescriptions.add(parseLogicalPort(interf));
- } else if (interf.getString(NAME).contains("lo")) {
- portDescriptions.add(parseLoopback(interf));
- } else {
- log.debug("Ignoring unknown port {}",
- interf.getString(NAME));
- }
+ for (HierarchicalConfiguration phyIntf : interfaceTree) {
+ if (phyIntf == null) {
+ continue;
}
+ // parse physical Interface
+ parsePhysicalInterface(portDescriptions, phyIntf);
}
}
return portDescriptions;
}
- private static PortDescription parseLoopback(HierarchicalConfiguration cfg) {
- String name = cfg.getString(IF_LO_NAME).trim();
- PortNumber portNumber = portNumber(name.replace("lo0.", ""));
-
- Builder annotationsBuilder = DefaultAnnotations.builder()
- .set(AnnotationKeys.PORT_NAME, name);
- String ip = cfg.getString(IF_LO_ADD);
- if (ip != null) {
- annotationsBuilder.set("ip", ip);
+ /**
+ * Parses {@literal physical-interface} tree.
+ *
+ * @param portDescriptions list to populate Ports found parsing configuration
+ * @param phyIntf physical-interface
+ */
+ private static void parsePhysicalInterface(List<PortDescription> portDescriptions,
+ HierarchicalConfiguration phyIntf) {
+ Builder annotations = DefaultAnnotations.builder();
+ PortNumber portNumber = portNumber(phyIntf.getString(SNMP_INDEX));
+ String phyPortName = phyIntf.getString(NAME);
+ if (portNumber == null) {
+ log.debug("Skipping physical-interface {}, no PortNumer",
+ phyPortName);
+ log.trace(" {}", phyIntf);
+ return;
}
- return new DefaultPortDescription(portNumber,
- true,
- COPPER,
- DEFAULT_PORT_SPEED,
- annotationsBuilder.build());
- }
+ setIfNonNull(annotations,
+ AnnotationKeys.PORT_NAME,
+ phyPortName);
- private static DefaultPortDescription parseDefaultPort(HierarchicalConfiguration cfg) {
- PortNumber portNumber = portNumber(cfg.getString(LO_INDEX));
- boolean enabled = "up".equals(cfg.getString(STATUS));
- int speed = parseInt(cfg.getString(SPEED).replaceAll(MBPS, ""));
+ setIfNonNull(annotations,
+ AnnotationKeys.PORT_MAC,
+ phyIntf.getString("current-physical-address"));
+ setIfNonNull(annotations,
+ AK_IF_TYPE,
+ phyIntf.getString(IF_TYPE));
- Builder annotationsBuilder = DefaultAnnotations.builder()
- .set(AnnotationKeys.PORT_NAME, cfg.getString(NAME).trim());
- setIpIfPresent(cfg, annotationsBuilder);
+ setIfNonNull(annotations,
+ AK_DESCRIPTION,
+ phyIntf.getString("description"));
- return new DefaultPortDescription(portNumber,
- enabled,
- COPPER,
- speed,
- annotationsBuilder.build());
- }
+ boolean opUp = phyIntf.getString(AK_OPER_STATUS, "down").equals("up");
+ annotations.set("oper-status", toUpDown(opUp));
- private static DefaultPortDescription parseLogicalPort(HierarchicalConfiguration cfg) {
+ boolean admUp = phyIntf.getString(AK_ADMIN_STATUS, "down").equals("up");
+ annotations.set("admin-status", toUpDown(admUp));
- String name = cfg.getString(NAME).trim();
- String index = cfg.getString(SNMP_INDEX).trim();
- Builder annotationsBuilder = DefaultAnnotations.builder()
- .set(AnnotationKeys.PORT_NAME, name)
- .set("index", index);
- setIpIfPresent(cfg, annotationsBuilder);
+ long portSpeed = toMbps(phyIntf.getString(SPEED));
- PortNumber portNumber = PortNumber.portNumber(index);
+ portDescriptions.add(new DefaultPortDescription(portNumber,
+ admUp & opUp,
+ Type.COPPER,
+ portSpeed,
+ annotations.build()));
- boolean enabled = false;
- if (cfg.getString(IF_LO_STATUS) != null) {
- enabled = true;
+ // parse each logical Interface
+ for (HierarchicalConfiguration logIntf : phyIntf.configurationsAt("logical-interface")) {
+ if (logIntf == null) {
+ continue;
+ }
+ PortNumber lPortNumber = safePortNumber(logIntf.getString(SNMP_INDEX));
+ if (lPortNumber == null) {
+ log.debug("Skipping logical-interface {} under {}, no PortNumer",
+ logIntf.getString(NAME), phyPortName);
+ log.trace(" {}", logIntf);
+ continue;
+ }
+
+ Builder lannotations = DefaultAnnotations.builder();
+ setIfNonNull(lannotations,
+ AnnotationKeys.PORT_NAME,
+ logIntf.getString(NAME));
+ setIfNonNull(lannotations,
+ AK_PHYSICAL_PORT_NAME,
+ phyPortName);
+
+ String afName = logIntf.getString("address-family.address-family-name");
+ String address = logIntf.getString("address-family.interface-address.ifa-local");
+ if (afName != null && address != null) {
+ // e.g., inet : IPV4, inet6 : IPV6
+ setIfNonNull(lannotations, afName, address);
+ }
+
+ // preserving former behavior
+ setIfNonNull(lannotations,
+ "ip",
+ logIntf.getString("address-family.interface-address.ifa-local"));
+
+ setIfNonNull(lannotations,
+ AK_ENCAPSULATION, logIntf.getString("encapsulation"));
+
+ // TODO confirm if this is correct.
+ // Looking at sample data,
+ // it seemed all logical loop-back interfaces were down
+ boolean lEnabled = logIntf.getString("if-config-flags.iff-up") != null;
+
+ portDescriptions.add(new DefaultPortDescription(lPortNumber,
+ admUp & opUp & lEnabled,
+ Type.COPPER,
+ portSpeed,
+ lannotations.build()));
}
- //FIXME: port speed should be exposed
- return new DefaultPortDescription(
- portNumber,
- enabled,
- COPPER,
- DEFAULT_PORT_SPEED,
- annotationsBuilder.build());
}
- private static void setIpIfPresent(HierarchicalConfiguration cfg,
- Builder annotationsBuilder) {
- String ip = cfg.getString(IF_LO_ADD);
- if (ip != null) {
- annotationsBuilder.set("ip", ip);
+ /**
+ * Port status as "up"/"down".
+ *
+ * @param portStatus port status
+ * @return "up" if {@code portStats} is {@literal true}, "down" otherwise
+ */
+ static String toUpDown(boolean portStatus) {
+ return portStatus ? "up" : "down";
+ }
+
+ /**
+ * Translate interface {@literal speed} value as Mbps value.
+ *
+ * Note: {@literal Unlimited} and unrecognizable string will be treated as
+ * {@value #DEFAULT_PORT_SPEED} Mbps.
+ *
+ * @param speed in String
+ * @return Mbps
+ */
+ static long toMbps(String speed) {
+ String s = Strings.nullToEmpty(speed).trim().toLowerCase();
+ Matcher matcher = SPEED_PATTERN.matcher(s);
+ if (matcher.matches()) {
+ // numeric
+ int n = Integer.parseInt(matcher.group(1));
+ String unit = matcher.group(2);
+ if ("m".equalsIgnoreCase(unit)) {
+ // Mbps
+ return n;
+ } else {
+ // assume Gbps
+ return 1000 * n;
+ }
}
+ log.trace("Treating unknown speed value {} as default", speed);
+ // Unlimited or unrecognizable
+ return DEFAULT_PORT_SPEED;
+ }
+
+ /**
+ * Sets annotation entry if {@literal value} was not {@literal null}.
+ *
+ * @param builder Annotation Builder
+ * @param key Annotation key
+ * @param value Annotation value (can be {@literal null})
+ */
+ static void setIfNonNull(Builder builder, String key, String value) {
+ if (value != null) {
+ builder.set(key, value.trim());
+ }
+ }
+
+ /**
+ * Creates PortNumber instance from String.
+ *
+ * Instead for throwing Exception, it will return null on format error.
+ *
+ * @param s port number as string
+ * @return PortNumber instance or null on error
+ */
+ static PortNumber safePortNumber(String s) {
+ try {
+ return portNumber(s);
+ } catch (RuntimeException e) {
+ log.trace("Failed parsing PortNumber {}", s, e);
+ }
+ return null;
}
/**