blob: 0827d09bdcee9189f8cca997759cfd550a672c8c [file] [log] [blame]
/*
* Copyright 2019-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.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.google.common.collect.Lists;
import org.onlab.packet.ChassisId;
import org.onlab.packet.MacAddress;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.StreamSupport;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Discovers the device, ports information from a Cisco Nexus 9K REST device.
*/
public class DeviceDescriptionDiscoveryCisco9kImpl extends AbstractHandlerBehaviour
implements DeviceDescriptionDiscovery {
private final Logger log = getLogger(getClass());
private static final String SHOW_INTERFACES_CMD = "show interface";
private static final String SHOW_VERSION_CMD = "show version";
private static final String SHOW_MODULE_CMD = "show module";
private static final String JSON_BODY = "body";
private static final String JSON_RESULT = "result";
private static final String JSON_INTERFACE = "interface";
private static final String JSON_ROW_INTERFACE = "ROW_interface";
private static final String JSON_ROW_MODULE = "ROW_modinfo";
private static final String JSON_ROW_MODULE_MAC = "ROW_modmacinfo";
private static final String MANUFACTURER = "manufacturer";
private static final String CHASSIS_ID = "chassis_id";
private static final String CISCO_SERIAL_BOARD = "proc_board_id";
private static final String KICKSTART_VER = "kickstart_ver_str";
private static final String INTERFACE_STATE = "state";
private static final String INTERFACE_ADMIN_STATE = "admin_state";
private static final String INTERFACE_ENABLED = "enabled";
private static final String INTERFACE_DISABLED = "disabled";
private static final String STATE_UP = "up";
private static final String MODULE_MODEL = "model";
private static final String MODULE_INTERFACE = "modinf";
private static final String MODULE_MAC = "modmac";
private static final String MODULE_SERIAL = "serialnum";
private static final String INTERFACE_ETHERNET = "Ethernet";
private static final String INTERFACE_PORTCHANNEL = "port-channel";
private static final String INTERFACE_BW = "eth_bw";
private static final String INTERFACE_MAC = "eth_bia_addr";
private static final String BREAKOUT = "breakout";
private static final String UNKNOWN = "unknown";
private static final String MODULE_ANNOTATION_FORMAT = "%s:%s:%s";
private static final String MODULE_BRACKET_FORMAT = "[%s]";
@Override
public DeviceDescription discoverDeviceDetails() {
DeviceId deviceId = handler().data().deviceId();
ArrayList<String> cmd = new ArrayList<>();
cmd.add(SHOW_VERSION_CMD);
cmd.add(SHOW_MODULE_CMD);
String response = NxApiRequest.postClis(handler(), cmd);
String mrf = UNKNOWN;
String hwVer = UNKNOWN;
String swVer = UNKNOWN;
String serialNum = UNKNOWN;
String module = UNKNOWN;
try {
ObjectMapper om = new ObjectMapper();
JsonNode json = om.readTree(response);
JsonNode body = json.at("/0/body");
if (!body.isMissingNode()) {
mrf = body.at("/" + MANUFACTURER).asText();
hwVer = body.at("/" + CHASSIS_ID).asText();
swVer = body.at("/" + KICKSTART_VER).asText();
serialNum = body.at("/" + CISCO_SERIAL_BOARD).asText();
}
JsonNode modInfo = json.at("/1/" + JSON_ROW_MODULE);
JsonNode modMacInfo = json.at("/1/" + JSON_ROW_MODULE_MAC);
if (!modInfo.isMissingNode()) {
List<String> modulesAnn = prepareModuleAnnotation(modInfo, modMacInfo);
module = String.format(MODULE_BRACKET_FORMAT, String.join(",", modulesAnn));
}
} catch (JsonParseException e) {
log.error("Failed to parse Json", e);
} catch (JsonMappingException e) {
log.error("Failed to map Json", e);
} catch (JsonProcessingException e) {
log.error("Failed to processing Json", e);
} catch (IOException e) {
log.error("Failed to retrieve Device Information", e);
}
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
Device device = deviceService.getDevice(deviceId);
if (device != null) {
annotations.putAll(device.annotations());
}
return new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
mrf, hwVer, swVer, serialNum,
new ChassisId(), annotations.build());
}
@Override
public List<PortDescription> discoverPortDetails() {
List<PortDescription> ports = Lists.newArrayList();
try {
String response;
try {
response = NxApiRequest.postCli(handler(), SHOW_INTERFACES_CMD);
} catch (NullPointerException e) {
log.error("Failed to perform {} command on the device {}",
SHOW_INTERFACES_CMD, handler().data().deviceId());
return ports;
}
ObjectMapper om = new ObjectMapper();
JsonNode json = om.readTree(response);
JsonNode res = json.get(JSON_RESULT);
JsonNode interfaces = res.findValue(JSON_ROW_INTERFACE);
Iterator<JsonNode> iter = interfaces.elements();
Integer ifCount = 1;
while (iter.hasNext()) {
JsonNode ifs = iter.next();
String ifName = ifs.get(JSON_INTERFACE).asText();
if (isPortValid(ifName)) {
Port.Type portType = Port.Type.VIRTUAL;
long portSpeed = ifs.get(INTERFACE_BW).asLong() / 1000; //Mbps
String portMac = ifs.get(INTERFACE_MAC).asText();
MacAddress mac = MacAddress.valueOf(
portMac.replace(".", "").replaceAll("(.{2})", "$1:").trim().substring(0, 17));
boolean state = STATE_UP.equals(ifs.get(INTERFACE_STATE).asText());
String adminState = STATE_UP.equals(ifs.get(INTERFACE_ADMIN_STATE).asText())
? INTERFACE_ENABLED : INTERFACE_DISABLED;
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
.set(AnnotationKeys.PORT_NAME, ifName)
.set(AnnotationKeys.PORT_MAC, mac.toString())
.set(AnnotationKeys.ADMIN_STATE, adminState);
if (isValidPhysicalPort(ifName)) {
String interfaceNumber = ifName.replace(INTERFACE_ETHERNET, "");
String[] interfaceLocation = interfaceNumber.split("/");
portType = Port.Type.FIBER;
if (interfaceLocation.length == 3) {
String breakout = ifName.substring(0, ifName.lastIndexOf("/"));
annotations.set(BREAKOUT, breakout);
}
}
PortDescription desc = DefaultPortDescription.builder()
.withPortNumber(PortNumber.portNumber(ifCount))
.isEnabled(state)
.type(portType).portSpeed(portSpeed).annotations(annotations.build())
.build();
ports.add(desc);
ifCount++;
}
}
} catch (Exception e) {
log.error("Exception occurred because of ", e);
}
return ports;
}
private boolean isValidPhysicalPort(String portName) {
return portName.startsWith(INTERFACE_ETHERNET);
}
private boolean isValidVirtualPort(String portName) {
return portName.startsWith(INTERFACE_PORTCHANNEL);
}
private boolean isPortValid(String portName) {
return isValidPhysicalPort(portName) || isValidVirtualPort(portName);
}
private List<String> prepareModuleAnnotation(JsonNode modules, JsonNode macs) {
List<String> modulesInfo = new ArrayList<>();
if (modules.getNodeType() == JsonNodeType.ARRAY) {
modules.forEach(module -> modulesInfo.add(getModuleInfo(module, macs)));
} else if (modules.getNodeType() == JsonNodeType.OBJECT) {
modulesInfo.add(getModuleInfo(modules, macs));
}
return modulesInfo;
}
private String getModuleInfo(JsonNode module, JsonNode moduleMac) {
int moduleId = module.get(MODULE_INTERFACE).asInt();
String moduleModel = module.get(MODULE_MODEL).asText();
String moduleSerial = getModuleSerial(moduleId, moduleMac);
return String.format(MODULE_ANNOTATION_FORMAT, moduleId, moduleModel, moduleSerial);
}
private String getModuleSerial(int moduleId, JsonNode macs) {
if (macs.getNodeType() == JsonNodeType.ARRAY) {
Optional<JsonNode> serial = StreamSupport.stream(macs.spliterator(), false)
.filter(mac -> mac.get(MODULE_MAC).asInt() == moduleId)
.findAny();
if (serial.isPresent()) {
return serial.get().get(MODULE_SERIAL).asText();
}
} else if (macs.getNodeType() == JsonNodeType.OBJECT) {
if (macs.get(MODULE_MAC).asInt() == moduleId) {
return macs.get(MODULE_SERIAL).asText();
}
}
return UNKNOWN;
}
}