blob: 43593783e05d22d88ea9ec66764529c66c763240 [file] [log] [blame]
/*
* 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;
}
}