Implementation Cisco rest driver for Nexus 900
Change-Id: Ic0eed72e4126ac2937336a6e357ca99bb445ae9a
(cherry picked from commit a7d7bd95a1cbb16311dac72b08fedc557850d2bb)
(cherry picked from commit c7d9077049a783f23f03e615bb7b517675a394a1)
(cherry picked from commit 386bd345717017b3ce66ee3c5a6222e0cd3bbc7e)
diff --git a/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/LinkDiscoveryCisco9kImpl.java b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/LinkDiscoveryCisco9kImpl.java
new file mode 100644
index 0000000..4359378
--- /dev/null
+++ b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/LinkDiscoveryCisco9kImpl.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.cisco.rest;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.Tools;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LinkDiscovery;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkDescription;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.protocol.rest.RestSBController;
+import org.slf4j.Logger;
+
+import javax.ws.rs.core.MediaType;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Discovers the links from a Cisco Nexus 9K REST device.
+ */
+public class LinkDiscoveryCisco9kImpl extends AbstractHandlerBehaviour implements LinkDiscovery {
+
+ private static final String SHOW_LLDP_NEIGHBOR_DETAIL_CMD = "show lldp neighbor detail";
+ private static final String UNKNOWN = "unknown";
+ private static final String JSON_RESULT = "result";
+ private static final String TABLE_NBOR_DETAIL = "TABLE_nbor_detail";
+ private static final String ROW_NBOR_DETAIL = "ROW_nbor_detail";
+ private static final String CHASSIS_ID = "chassis_id";
+ private static final String PORT_ID = "port_id";
+ private static final String PORT_DESC = "port_desc";
+ private static final String SYS_NAME = "sys_name";
+ private static final String LOCAL_PORT_ID = "l_port_id";
+ private static final String LLDP = "lldp:";
+
+ private final Logger log = getLogger(getClass());
+
+ @Override
+ public Set<LinkDescription> getLinks() {
+ String response = retrieveResponse(SHOW_LLDP_NEIGHBOR_DETAIL_CMD);
+ DeviceId localDeviceId = this.handler().data().deviceId();
+ DeviceService deviceService = this.handler().get(DeviceService.class);
+ Set<LinkDescription> linkDescriptions = Sets.newHashSet();
+ List<Port> ports = deviceService.getPorts(localDeviceId);
+
+ if (ports.size() == 0 || Objects.isNull(response)) {
+ return linkDescriptions;
+ }
+ try {
+ ObjectMapper om = new ObjectMapper();
+ JsonNode json = om.readTree(response);
+ if (json == null) {
+ return linkDescriptions;
+ }
+
+ JsonNode res = json.at("/" + JSON_RESULT);
+ if (res.isMissingNode()) {
+ return linkDescriptions;
+ }
+
+ JsonNode lldpNeighborsRow = res.at("/" + TABLE_NBOR_DETAIL);
+ if (lldpNeighborsRow.isMissingNode()) {
+ return linkDescriptions;
+ }
+
+ JsonNode lldpNeighbors = lldpNeighborsRow.at("/" + ROW_NBOR_DETAIL);
+ if (lldpNeighbors.isMissingNode()) {
+ return linkDescriptions;
+ }
+
+ Iterator<JsonNode> iterator = lldpNeighbors.elements();
+
+ while (iterator.hasNext()) {
+ JsonNode neighbors = iterator.next();
+ String remoteChassisId = neighbors.get(CHASSIS_ID).asText();
+ String remotePortName = neighbors.get(PORT_ID).asText();
+ String remotePortDesc = neighbors.get(PORT_DESC).asText();
+ String lldpLocalPort = neighbors.get(LOCAL_PORT_ID).asText()
+ .replaceAll("(Eth.{0,5})(.\\d{0,5}/\\d{0,5})", "Ethernet$2");
+
+ Port localPort = findLocalPortByName(ports, lldpLocalPort);
+ if (localPort == null) {
+ log.warn("local port not found. LldpLocalPort value: {}", lldpLocalPort);
+ continue;
+ }
+
+ Device remoteDevice = findRemoteDeviceByChassisId(deviceService, remoteChassisId);
+ Port remotePort = findDestinationPortByName(remotePortName,
+ remotePortDesc,
+ deviceService,
+ remoteDevice);
+
+ if (!localPort.isEnabled() || !remotePort.isEnabled()) {
+ log.debug("Ports are disabled. Cannot create a link between {}/{} and {}/{}",
+ localDeviceId, localPort, remoteDevice.id(), remotePort);
+ continue;
+ }
+
+ linkDescriptions.addAll(buildLinkPair(localDeviceId, localPort, remoteDevice.id(), remotePort));
+ }
+ } catch (IOException e) {
+ log.error("Failed to get links ", e);
+ }
+
+ log.debug("Returning linkDescriptions: {}", linkDescriptions);
+ return linkDescriptions;
+
+ }
+
+ private Port findLocalPortByName(List<Port> ports, String lldpLocalPort) {
+ Optional<Port> localPort = ports.stream()
+ .filter(port -> lldpLocalPort.equalsIgnoreCase(port.annotations().value(PORT_NAME))).findAny();
+ if (!localPort.isPresent()) {
+ return null;
+ }
+ return localPort.get();
+ }
+
+ private Device findRemoteDeviceByChassisId(DeviceService deviceService, String remoteChassisIdString) {
+ String forMacTmp = remoteChassisIdString.replace(".", "").replaceAll("(.{2})", "$1:").trim().substring(0, 17);
+ MacAddress mac = MacAddress.valueOf(forMacTmp);
+ ChassisId remoteChassisId = new ChassisId(mac.toLong());
+ Optional<Device> remoteDeviceOptional;
+ Supplier<Stream<Device>> deviceStream = () ->
+ StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false);
+ remoteDeviceOptional = deviceStream.get()
+ .filter(device -> device.chassisId() != null
+ && MacAddress.valueOf(device.chassisId().value()).equals(mac))
+ .findAny();
+
+ if (remoteDeviceOptional.isPresent()) {
+ return remoteDeviceOptional.get();
+ } else {
+ remoteDeviceOptional = deviceStream.get().filter(device ->
+ Tools.stream(deviceService.getPorts(device.id())).anyMatch(port ->
+ port.annotations().keys().contains(AnnotationKeys.PORT_MAC)
+ && MacAddress.valueOf(port.annotations().value(AnnotationKeys.PORT_MAC))
+ .equals(mac))).findAny();
+ if (remoteDeviceOptional.isPresent()) {
+ return remoteDeviceOptional.get();
+ } else {
+ log.debug("remote device not found. remoteChassisId value: {}", remoteChassisId);
+ return new DefaultDevice(ProviderId.NONE,
+ DeviceId.deviceId(LLDP + mac.toString()),
+ Device.Type.SWITCH,
+ UNKNOWN,
+ UNKNOWN,
+ UNKNOWN,
+ UNKNOWN,
+ remoteChassisId,
+ DefaultAnnotations.EMPTY);
+ }
+ }
+ }
+
+ private Port findDestinationPortByName(String remotePortName,
+ String remotePortDesc,
+ DeviceService deviceService,
+ Device remoteDevice) {
+ Optional<Port> remotePort = deviceService.getPorts(remoteDevice.id())
+ .stream().filter(port -> remotePortName.equals(port.annotations().value(PORT_NAME))).findAny();
+ if (remotePort.isPresent()) {
+ return remotePort.get();
+ } else {
+ Optional<Port> remotePortByDesc = deviceService.getPorts(remoteDevice.id())
+ .stream().filter(port -> remotePortDesc.equals(port.annotations().value(PORT_NAME))).findAny();
+ if (remotePortByDesc.isPresent()) {
+ return remotePortByDesc.get();
+ } else {
+ int portNumber = Integer.valueOf(remotePortName.replaceAll("\\D+", ""));
+ DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+ .set(AnnotationKeys.PORT_NAME, remotePortName);
+ return new DefaultPort(remoteDevice, PortNumber.portNumber(portNumber),
+ true,
+ annotations.build());
+ }
+ }
+ }
+
+ private String retrieveResponse(String command) {
+ DriverHandler handler = handler();
+ RestSBController controller = checkNotNull(handler.get(RestSBController.class));
+ DeviceId deviceId = handler.data().deviceId();
+
+ String req = NxApiRequest.generate(Lists.newArrayList(command), NxApiRequest.CommandType.CLI);
+ log.debug("request :" + req);
+
+ InputStream stream = new ByteArrayInputStream(req.getBytes(StandardCharsets.UTF_8));
+ return controller.post(deviceId, "/ins", stream, MediaType.valueOf("application/json-rpc"), String.class);
+ }
+
+ private static Set<LinkDescription> buildLinkPair(DeviceId localDevId,
+ Port localPort,
+ DeviceId remoteDevId,
+ Port remotePort) {
+
+ Set<LinkDescription> linkDescriptions = Sets.newHashSet();
+ ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
+ ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
+ DefaultAnnotations annotations = DefaultAnnotations.builder()
+ .set("layer", "ETHERNET")
+ .build();
+ linkDescriptions.add(new DefaultLinkDescription(
+ remote, local, Link.Type.DIRECT, true, annotations));
+
+ return linkDescriptions;
+ }
+}