Dell Rest Driver

Change-Id: I79eee0540766c56694f35d18f3d5be06fb59aea5
diff --git a/drivers/dellrest/BUILD b/drivers/dellrest/BUILD
new file mode 100644
index 0000000..2d53a22
--- /dev/null
+++ b/drivers/dellrest/BUILD
@@ -0,0 +1,32 @@
+COMPILE_DEPS = CORE_DEPS + JACKSON + [
+    "@javax_ws_rs_api//jar",
+    "//utils/rest:onlab-rest",
+    "//drivers/utilities:onos-drivers-utilities",
+    "//protocols/rest/api:onos-protocols-rest-api",
+]
+
+TEST_DEPS = TEST_ADAPTERS + [
+    "//core/api:onos-api-tests",
+]
+
+BUNDLES = [
+    ":onos-drivers-dellrest",
+    "//drivers/utilities:onos-drivers-utilities",
+]
+
+osgi_jar_with_tests(
+    resources = glob(["src/main/resources/**"]),
+    resources_root = "src/main/resources",
+    test_deps = TEST_DEPS,
+    deps = COMPILE_DEPS,
+)
+
+onos_app(
+    app_name = "org.onosproject.drivers.dellrest",
+    category = "Drivers",
+    description = "Adds support for Dell Networks devices using REST.",
+    included_bundles = BUNDLES,
+    required_apps = ["org.onosproject.restsb"],
+    title = "Dell Networks REST Drivers",
+    url = "http://onosproject.org",
+)
diff --git a/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestBridgeConfig.java b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestBridgeConfig.java
new file mode 100644
index 0000000..f885b53
--- /dev/null
+++ b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestBridgeConfig.java
@@ -0,0 +1,145 @@
+/*
+ * 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.dellrest;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.protocol.rest.RestSBController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.MediaType;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class DellRestBridgeConfig extends AbstractHandlerBehaviour
+        implements BridgeConfig {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String CLI_REQUEST = "/running/dell/_operations/cli";
+    private static final String CONFIG_COMMANDS = "<input>" +
+            "<config-commands>" +
+            "openflow of-instance 1\\r\\n" +
+            "shutdown\\r\\n" +
+            "exit\\r\\n" +
+            "%s" + // interface commands
+            "openflow of-instance 1\\r\\n" +
+            "no shutdown" +
+            "</config-commands>" +
+            "</input>";
+    private static final String PORT_ADD_COMMANDS = "interface %s\\r\\n" + // interface name
+            "of-instance 1\\r\\n" +
+            "no shutdown\\r\\n" +
+            "exit\\r\\n";
+    private static final String PORT_REMOVE_COMMANDS = "interface %s\\r\\n" + // interface name
+            "no of-instance\\r\\n" +
+            "exit\\r\\n";
+    private static final String TENGINTERFACE = "TenGigabitEthernet ";
+    private static final String FORTYGINTERFACE = "FortyGigE ";
+    private static final String SLASH = "/";
+    private static final String DASH = "-";
+
+    @Override
+    public boolean addBridge(BridgeDescription bridgeDescription) {
+        return false;
+    }
+
+    @Override
+    public void deleteBridge(BridgeName bridgeName) {
+
+    }
+
+    @Override
+    public Collection<BridgeDescription> getBridges() {
+        return null;
+    }
+
+    @Override
+    public void addPort(BridgeName bridgeName, String portName) {
+        // bridgeName is not used
+        checkNotNull(portName);
+        portName = portNameForCli(portName);
+        String portAddCommands = String.format(PORT_ADD_COMMANDS, portName);
+
+        DriverHandler handler = handler();
+        RestSBController controller = checkNotNull(handler.get(RestSBController.class));
+        DeviceId deviceId = handler.data().deviceId();
+
+        InputStream payload = new StringBufferInputStream(String.format(CONFIG_COMMANDS, portAddCommands));
+        String resp = controller.post(deviceId, CLI_REQUEST, payload, MediaType.valueOf("*/*"), String.class);
+        //TODO Parse resp and if error, return false
+        log.info("{}", resp);
+        return;
+    }
+
+    @Override
+    public void deletePort(BridgeName bridgeName, String portName) {
+        // bridgeName is not used
+        checkNotNull(portName);
+        portName = portNameForCli(portName);
+        String portRemoveCommands = String.format(PORT_REMOVE_COMMANDS, portName);
+
+        DriverHandler handler = handler();
+        RestSBController controller = checkNotNull(handler.get(RestSBController.class));
+        DeviceId deviceId = handler.data().deviceId();
+
+        InputStream payload = new StringBufferInputStream(String.format(CONFIG_COMMANDS, portRemoveCommands));
+        String resp = controller.post(deviceId, CLI_REQUEST, payload, MediaType.valueOf("*/*"), String.class);
+        log.info("{}", resp);
+    }
+
+    @Override
+    public Collection<PortDescription> getPorts() {
+        return null;
+    }
+
+    @Override
+    public Set<PortNumber> getPortNumbers() {
+        return null;
+    }
+
+    @Override
+    public List<PortNumber> getLocalPorts(Iterable<String> ifaceIds) {
+        return null;
+    }
+
+    private String portNameForCli(String portName) {
+        if (portName.toLowerCase().startsWith("tengig-")) {
+            // change eg. "tengig-1-1" to "TenGigabitEthernet 1/1"
+            portName = portName.replaceFirst("tengig-", TENGINTERFACE);
+            return  portName.replaceFirst(DASH, SLASH);
+        } else if (portName.toLowerCase().startsWith("fortygig-")) {
+            // change eg. "fortygig-1-49" to "FortyGigE 1/49"
+            portName = portName.replaceFirst("fortygig-", FORTYGINTERFACE);
+            return  portName.replaceFirst(DASH, SLASH);
+        } else {
+            return portName;
+        }
+    }
+}
diff --git a/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestDriversLoader.java b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestDriversLoader.java
new file mode 100644
index 0000000..670cfff
--- /dev/null
+++ b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestDriversLoader.java
@@ -0,0 +1,31 @@
+/*
+ * 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.dellrest;
+
+import org.osgi.service.component.annotations.Component;
+import org.onosproject.net.driver.AbstractDriverLoader;
+
+/**
+ * Loader for Dell device drivers.
+ */
+@Component(immediate = true)
+public class DellRestDriversLoader extends AbstractDriverLoader {
+
+    public DellRestDriversLoader() {
+        super("/dellrest-drivers.xml");
+    }
+}
diff --git a/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestOpenFlowControllerConfig.java b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestOpenFlowControllerConfig.java
new file mode 100644
index 0000000..dcf0c99
--- /dev/null
+++ b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestOpenFlowControllerConfig.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dellrest;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerConfig;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.protocol.rest.RestSBController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.MediaType;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class DellRestOpenFlowControllerConfig extends AbstractHandlerBehaviour
+        implements ControllerConfig {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String TCP = "tcp";
+    private static final String CLI_REQUEST = "/running/dell/_operations/cli";
+    private static final String SET_CONTROLLERS_XML = "<input>" +
+            "<config-commands>" +
+            "openflow of-instance 1\\r\\n" + // for now supports only one instance
+            "shutdown\\r\\n" +
+            "%s" +
+            "of-version 1.3\\r\\n" +
+            "no shutdown" + // TODO: check previous state
+            "</config-commands>" +
+            "</input>";
+    private static final String SET_CONTROLLER = "controller %d %s port %d tcp\\r\\n";
+    private static final String NO_CONTROLLER = "no controller %d\\r\\n";
+    private static final int MAX_CONTROLLERS = 2;
+
+    @Override
+    public List<ControllerInfo> getControllers() {
+        return new ArrayList<>();
+    }
+
+    // Example test with ONOS CLI:
+    // device-setcontrollers rest:10.251.217.143:8008 tcp:1.2.3.4:2222
+
+    // TODO: assumes that of-instance 1 was in "no shutdown" state,
+    // check previous state
+    @Override
+    public void setControllers(List<ControllerInfo> controllers) {
+
+        // Dell supports max 2 controllers per of-instance
+        if (controllers.size() > MAX_CONTROLLERS) {
+            log.warn("Cannot set more than 2 controllers.");
+        }
+
+        DriverHandler handler = handler();
+        RestSBController controller = checkNotNull(handler.get(RestSBController.class));
+        DeviceId deviceId = handler.data().deviceId();
+
+        String controllerCommands = getControllerCommands(controllers).stream()
+                .reduce(String::concat)
+                .orElse("");
+
+        InputStream payload = new StringBufferInputStream(String.format(SET_CONTROLLERS_XML, controllerCommands));
+        String resp = controller.post(deviceId, CLI_REQUEST, payload, MediaType.valueOf("*/*"), String.class);
+        log.info("{}", resp);
+    }
+
+    // Get controller commands for maximum first 2 controllers
+    private List<String> getControllerCommands(List<ControllerInfo> controllers) {
+        List<String> controllerCommands = new ArrayList<>();
+
+        for (int controllerNum = 0; controllerNum < MAX_CONTROLLERS; ++controllerNum) {
+            if (controllers.size() > controllerNum) {
+                ControllerInfo controllerInfo = controllers.get(controllerNum);
+                // Only "tcp" type is supported
+                if (controllerInfo.type().equals(TCP)) {
+                    String ip = controllerInfo.ip().toString();
+                    int port = controllerInfo.port();
+                    controllerCommands.add(String.format(SET_CONTROLLER, controllerNum + 1, ip, port));
+                } else {
+                    log.warn("Controller type can only be 'tcp'");
+                    controllerCommands.add(String.format(NO_CONTROLLER, controllerNum + 1));
+                }
+            } else {
+                // remove existing controller
+                controllerCommands.add(String.format(NO_CONTROLLER, controllerNum + 1));
+            }
+        }
+
+        return controllerCommands;
+    }
+}
diff --git a/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestPortDiscovery.java b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestPortDiscovery.java
new file mode 100644
index 0000000..851d7a4
--- /dev/null
+++ b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/DellRestPortDiscovery.java
@@ -0,0 +1,93 @@
+/*
+ * 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.dellrest;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.protocol.rest.RestSBController;
+import org.slf4j.Logger;
+
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Discovers the ports from a Dell Rest device.
+ */
+@Deprecated
+public class DellRestPortDiscovery extends AbstractHandlerBehaviour {
+
+    private final Logger log = getLogger(getClass());
+    private static final String NAME = "name";
+    private static final String INTERFACES = "interface";
+    private static final String INTERFACES_REQUEST = "/running/dell/interfaces";
+    private static final String TENGINTERFACENAME = "Te ";
+
+
+    @Deprecated
+    public List<PortDescription> getPorts() {
+        List<PortDescription> ports = Lists.newArrayList();
+        DriverHandler handler = handler();
+        RestSBController controller = checkNotNull(handler.get(RestSBController.class));
+        DeviceId deviceId = handler.data().deviceId();
+        String remotePortName = "";
+
+        // read configuration from REST API
+        HierarchicalConfiguration config = XmlConfigParser.
+                loadXml(controller.get(deviceId, INTERFACES_REQUEST, MediaType.valueOf("*/*")));
+
+        // get the interfaces part
+        List<HierarchicalConfiguration> portsConfig = parseDellPorts(config);
+
+        portsConfig.stream().forEach(sub -> {
+            String portName = sub.getString(NAME);
+            DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+                    .set(AnnotationKeys.PORT_NAME, portName);
+            // TODO: obtain proper port speed and type
+            long portSpeed = 10_000;
+            Port.Type portType = Port.Type.COPPER;
+            PortNumber portNumber = PortNumber.fromString(remotePortName.replaceAll(TENGINTERFACENAME, ""));
+
+            ports.add(DefaultPortDescription.builder()
+                              .withPortNumber(portNumber)
+                              .isEnabled(true)
+                              .type(portType)
+                              .portSpeed(portSpeed)
+                              .annotations(annotations.build())
+                              .build());
+        });
+        return ImmutableList.copyOf(ports);
+    }
+
+    public static List<HierarchicalConfiguration> parseDellPorts(HierarchicalConfiguration cfg) {
+        return cfg.configurationsAt(INTERFACES);
+    }
+}
+
diff --git a/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/package-info.java b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/package-info.java
new file mode 100644
index 0000000..5cbbc4a
--- /dev/null
+++ b/drivers/dellrest/src/main/java/org/onosproject/drivers/dellrest/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 for Dell device drivers.
+ */
+package org.onosproject.drivers.dellrest;
\ No newline at end of file
diff --git a/drivers/dellrest/src/main/resources/dellrest-drivers.xml b/drivers/dellrest/src/main/resources/dellrest-drivers.xml
new file mode 100644
index 0000000..14bd7fe
--- /dev/null
+++ b/drivers/dellrest/src/main/resources/dellrest-drivers.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<drivers>
+    <driver name="dellrest" manufacturer="Dell" hwVersion="1.0.0" swVersion="1.0.0">
+        <behaviour api="org.onosproject.net.behaviour.PortDiscovery"
+                   impl="org.onosproject.drivers.dellrest.DellRestPortDiscovery"/>
+        <behaviour api="org.onosproject.net.behaviour.ControllerConfig"
+                   impl="org.onosproject.drivers.dellrest.DellRestOpenFlowControllerConfig"/>
+        <behaviour api="org.onosproject.net.behaviour.BridgeConfig"
+                   impl="org.onosproject.drivers.dellrest.DellRestBridgeConfig"/>
+    </driver>
+</drivers>
+
diff --git a/drivers/dellrest/src/test/java/org/onosproject/drivers/dellrest/DellrestDriversLoaderTest.java b/drivers/dellrest/src/test/java/org/onosproject/drivers/dellrest/DellrestDriversLoaderTest.java
new file mode 100644
index 0000000..0c3de7f
--- /dev/null
+++ b/drivers/dellrest/src/test/java/org/onosproject/drivers/dellrest/DellrestDriversLoaderTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.dellrest;
+
+import org.junit.Before;
+import org.onosproject.net.driver.AbstractDriverLoaderTest;
+
+/**
+ * Dellrest drivers loader test.
+ */
+public class DellrestDriversLoaderTest extends AbstractDriverLoaderTest {
+
+    @Before
+    public void setUp() {
+        loader = new DellRestDriversLoader();
+    }
+}