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/ControllerConfigCiscoImpl.java b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/ControllerConfigCiscoImpl.java
index 437c80a..ad2121e 100644
--- a/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/ControllerConfigCiscoImpl.java
+++ b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/ControllerConfigCiscoImpl.java
@@ -177,7 +177,7 @@
 
     public boolean checkSwitchN7K(DriverHandler handler) {
 
-        String response = NxApiRequest.postClis(handler, SHOW_VERSION);
+        String response = NxApiRequest.postCli(handler, SHOW_VERSION);
 
         String msg = "";
 
diff --git a/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/DeviceDescriptionDiscoveryCisco9kImpl.java b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/DeviceDescriptionDiscoveryCisco9kImpl.java
new file mode 100644
index 0000000..03a118e
--- /dev/null
+++ b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/DeviceDescriptionDiscoveryCisco9kImpl.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2016-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;
+    }
+
+}
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;
+    }
+}
diff --git a/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/NxApiRequest.java b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/NxApiRequest.java
index 201ff02..544ec2e 100644
--- a/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/NxApiRequest.java
+++ b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/NxApiRequest.java
@@ -22,6 +22,7 @@
 import java.util.List;
 import java.util.stream.IntStream;
 
+import com.google.common.collect.Lists;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.driver.DriverHandler;
 import org.onosproject.protocol.rest.RestSBController;
@@ -31,7 +32,6 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import javax.ws.rs.core.MediaType;
-import com.google.common.collect.Lists;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
@@ -128,6 +128,21 @@
     /**
      * Sends NX-API request message to the device.
      * @param handler device's driver handler
+     * @param cmds NX-API list of command strings
+     * @return the response string
+     */
+    public static String postClis(DriverHandler handler, List<String> cmds) {
+        RestSBController controller = checkNotNull(handler.get(RestSBController.class));
+        DeviceId deviceId = handler.data().deviceId();
+
+        String request = generate(cmds, CommandType.CLI);
+        InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8));
+        return controller.post(deviceId, API_URI, stream, MediaType.valueOf(APP_JSON_RPC), String.class);
+    }
+
+    /**
+     * Sends NX-API request message to the device.
+     * @param handler device's driver handler
      * @param command NX-API command string
      * @param type response message format
      * @return the response string
@@ -139,33 +154,14 @@
         String request = generate(command, type);
         return post(controller, deviceId, request);
     }
-    /**
-     * Sends NX-API request message to the device.
-     * @param handler device's driver handler
-     * @param cmd NX-API command string
-     * @return the response string
-     */
-    static String postClis(DriverHandler handler, String cmd) {
-        RestSBController controller = checkNotNull(handler.get(RestSBController.class));
-        DeviceId deviceId = handler.data().deviceId();
-
-        String request = generate(cmd, CommandType.CLI);
-        InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8));
-        return controller.post(deviceId, API_URI, stream, MediaType.valueOf(APP_JSON_RPC), String.class);
-    }
 
     /**
-     * Sends NX-API request message to the device.
-     * @param handler device's driver handler
-     * @param cmds NX-API list of command strings
+     * Sends NX-API request message to the device with CLI command.
+     * @param handler device handler
+     * @param command NX-API command string
      * @return the response string
      */
-    static String postClis(DriverHandler handler, List<String> cmds) {
-        RestSBController controller = checkNotNull(handler.get(RestSBController.class));
-        DeviceId deviceId = handler.data().deviceId();
-
-        String request = generate(cmds, CommandType.CLI);
-        InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8));
-        return controller.post(deviceId, API_URI, stream, MediaType.valueOf(APP_JSON_RPC), String.class);
+    public static String postCli(DriverHandler handler, String command) {
+        return post(handler, command, CommandType.CLI);
     }
 }
diff --git a/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/PortDiscoveryCiscoImpl.java b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/PortDiscoveryCiscoImpl.java
new file mode 100644
index 0000000..cf6af3b
--- /dev/null
+++ b/drivers/cisco/rest/src/main/java/org/onosproject/drivers/cisco/rest/PortDiscoveryCiscoImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016-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 org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import java.util.List;
+
+/**
+ * Discovers the ports from a Cisco Nexus 9K REST device.
+ */
+@Deprecated
+public class PortDiscoveryCiscoImpl extends AbstractHandlerBehaviour {
+
+    @Deprecated
+    public List<PortDescription> getPorts() {
+        return new DeviceDescriptionDiscoveryCisco9kImpl().discoverPortDetails();
+    }
+}
diff --git a/drivers/cisco/rest/src/main/resources/cisco-rest-drivers.xml b/drivers/cisco/rest/src/main/resources/cisco-rest-drivers.xml
index 04188fb..9b788f4 100644
--- a/drivers/cisco/rest/src/main/resources/cisco-rest-drivers.xml
+++ b/drivers/cisco/rest/src/main/resources/cisco-rest-drivers.xml
@@ -23,4 +23,11 @@
         <behaviour api="org.onosproject.net.behaviour.ControllerConfig"
                    impl="org.onosproject.drivers.cisco.rest.ControllerConfigCiscoImpl"/>
     </driver>
+
+    <driver name="cisco-rest" manufacturer="Cisco" hwVersion="Nexus 9000" swVersion="NXOS">
+        <behaviour api="org.onosproject.net.device.PortStatisticsDiscovery"
+                   impl="org.onosproject.drivers.cisco.rest.CiscoNxosPortStatistics"/>
+        <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.cisco.rest.DeviceDescriptionDiscoveryCisco9kImpl"/>
+    </driver>
 </drivers>