Merge remote-tracking branch 'origin/master'
diff --git a/apps/oecfg/pom.xml b/apps/oecfg/pom.xml
new file mode 100644
index 0000000..422e228
--- /dev/null
+++ b/apps/oecfg/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onlab.onos</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-oecfg</artifactId>
+    <packaging>jar</packaging>
+
+    <description>Standalone utility for converting ONOS JSON config to OE-Linc JSON config</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <manifestEntries>
+                                        <Main-Class>org.onlab.onos.oecfg.OELinkConfig</Main-Class>
+                                    </manifestEntries>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/apps/oecfg/src/main/java/org/onlab/onos/oecfg/OELinkConfig.java b/apps/oecfg/src/main/java/org/onlab/onos/oecfg/OELinkConfig.java
new file mode 100644
index 0000000..536e91e
--- /dev/null
+++ b/apps/oecfg/src/main/java/org/onlab/onos/oecfg/OELinkConfig.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.onlab.onos.oecfg;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility program to convert standard ONOS config JSON to format expected
+ * by the OE Link switch.
+ */
+public final class OELinkConfig {
+
+    private ObjectMapper mapper = new ObjectMapper();
+    private Map<String, String> dpidToName = new HashMap<>();
+
+    public static void main(String[] args) {
+        try {
+            OELinkConfig config = new OELinkConfig();
+            JsonNode json = config.convert(System.in);
+            System.out.println(json.toString());
+        } catch (IOException e) {
+            System.err.println("Unable to convert JSON due to: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    private OELinkConfig() {
+    }
+
+    private JsonNode convert(InputStream input) throws IOException {
+        JsonNode json = mapper.readTree(input);
+        ObjectNode result = mapper.createObjectNode();
+        result.set("opticalSwitches", opticalSwitches(json));
+        result.set("opticalLinks", opticalLinks(json));
+        return result;
+    }
+
+    private JsonNode opticalSwitches(JsonNode json) {
+        ArrayNode result = mapper.createArrayNode();
+        for (JsonNode node : json.get("devices")) {
+            String dpid = dpid(node.path("uri"));
+            String name = node.path("name").asText("none");
+            dpidToName.put(dpid, name);
+            if (node.path("type").asText("none").equals("ROADM")) {
+                result.add(opticalSwitch(dpid, name, (ObjectNode) node));
+            }
+        }
+        return result;
+    }
+
+    private ObjectNode opticalSwitch(String dpid, String name, ObjectNode node) {
+        ObjectNode result = mapper.createObjectNode();
+        ObjectNode annot = (ObjectNode) node.path("annotations");
+        result.put("allowed", true).put("type", "Roadm")
+                .put("name", name).put("nodeDpid", dpid)
+                .put("latitude", annot.path("latitude").asDouble(0.0))
+                .put("longitude", annot.path("longitude").asDouble(0.0))
+                .set("params", switchParams(annot));
+        return result;
+    }
+
+    private ObjectNode switchParams(ObjectNode annot) {
+        return mapper.createObjectNode()
+                .put("numRegen", annot.path("optical.regens").asInt(0));
+    }
+
+    private JsonNode opticalLinks(JsonNode json) {
+        ArrayNode result = mapper.createArrayNode();
+        for (JsonNode node : json.get("links")) {
+            if (node.path("type").asText("none").equals("OPTICAL")) {
+                result.add(opticalLink((ObjectNode) node));
+            }
+        }
+        return result;
+    }
+
+    private ObjectNode opticalLink(ObjectNode node) {
+        ObjectNode result = mapper.createObjectNode();
+        ObjectNode annot = (ObjectNode) node.path("annotations");
+        String src = dpid(node.path("src"));
+        String dst = dpid(node.path("dst"));
+        result.put("allowed", true).put("type", linkType(annot))
+                .put("nodeDpid1", src).put("nodeDpid2", dst)
+                .set("params", linkParams(src, dst, node, annot));
+        return result;
+    }
+
+    private String linkType(ObjectNode annot) {
+        return annot.path("optical.type").asText("cross-connect").equals("WDM") ?
+                "wdmLink" : "pktOptLink";
+    }
+
+    private ObjectNode linkParams(String src, String dst,
+                                  ObjectNode node, ObjectNode annot) {
+        ObjectNode result = mapper.createObjectNode()
+                .put("nodeName1", dpidToName.get(src))
+                .put("nodeName2", dpidToName.get(dst))
+                .put("port1", port(node.path("src")))
+                .put("port2", port(node.path("dst")));
+        if (annot.has("bandwidth")) {
+            result.put("bandwidth", annot.path("bandwidth").asInt());
+        }
+        if (annot.has("optical.waves")) {
+            result.put("numWaves", annot.path("optical.waves").asInt());
+        }
+        return result;
+    }
+
+    private String dpid(JsonNode node) {
+        String s = node.asText("of:0000000000000000").substring(3);
+        return s.substring(0, 2) + ":" + s.substring(2, 4) + ":" +
+                s.substring(4, 6) + ":" + s.substring(6, 8) + ":" +
+                s.substring(8, 10) + ":" + s.substring(10, 12) + ":" +
+                s.substring(12, 14) + ":" + s.substring(14, 16);
+    }
+
+    private int port(JsonNode node) {
+        return Integer.parseInt(node.asText("of:0000000000000000/0").substring(20));
+    }
+
+}
diff --git a/apps/optical/pom.xml b/apps/optical/pom.xml
index 081c9a3..b0a737f 100644
--- a/apps/optical/pom.xml
+++ b/apps/optical/pom.xml
@@ -35,7 +35,7 @@
     <description>ONOS application for packet/optical deployments</description>
 
     <dependencies>
-        
+
         <dependency>
             <groupId>org.onlab.onos</groupId>
             <artifactId>onos-cli</artifactId>
@@ -45,21 +45,20 @@
             <groupId>org.apache.karaf.shell</groupId>
             <artifactId>org.apache.karaf.shell.console</artifactId>
         </dependency>
-        
-      <dependency>
-	<groupId>org.codehaus.jackson</groupId>
-	<artifactId>jackson-core-asl</artifactId>
-      </dependency>
-      <dependency>
-	<groupId>org.codehaus.jackson</groupId>
-	<artifactId>jackson-mapper-asl</artifactId>
-      </dependency>
-      <dependency>
-        <groupId>com.fasterxml.jackson.core</groupId>
-        <artifactId>jackson-annotations</artifactId>
-        <scope>provided</scope>
-      </dependency>
 
-    </dependencies> 
-    
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-mapper-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
 </project>
diff --git a/apps/optical/src/main/resources/demo-3-roadm-2-ps.json b/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
index d4f4a14..05c4124 100644
--- a/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
+++ b/apps/optical/src/main/resources/demo-3-roadm-2-ps.json
@@ -94,7 +94,7 @@
                 "port2": 11
             },
             "type": "pktOptLink"
-        },
+        }
 
     ]
 }
diff --git a/apps/pom.xml b/apps/pom.xml
index 759ee92..e9bdaf6 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -46,6 +46,7 @@
         <module>calendar</module>
         <module>optical</module>
         <module>metrics</module>
+        <module>oecfg</module>
     </modules>
 
     <properties>
diff --git a/tools/test/topos/oe-linear-3.json b/tools/test/topos/oe-linear-3.json
index 9214bd9..98206da 100644
--- a/tools/test/topos/oe-linear-3.json
+++ b/tools/test/topos/oe-linear-3.json
@@ -2,28 +2,28 @@
     "devices" : [
         {
             "uri": "of:0000ffffffffff01", "mac": "ffffffffffff01", "type": "ROADM",
-            "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
+            "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?", "name": "ROADM1",
             "annotations": { "latitude": 37.6, "longitude": 122.3, "optical.regens": 0 }
         },
         {
             "uri": "of:0000ffffffffff02", "mac": "ffffffffffff02", "type": "ROADM",
-            "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
+            "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?", "name": "ROADM2",
             "annotations": { "latitude": 37.3, "longitude": 121.9, "optical.regens": 0 }
         },
         {
             "uri": "of:0000ffffffffff03", "mac": "ffffffffffff03", "type": "ROADM",
-            "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
+            "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?", "name": "ROADM3",
             "annotations": { "latitude": 33.9, "longitude": 118.4, "optical.regens": 2 }
         },
 
         {
             "uri": "of:0000ffffffff0001", "mac": "ffffffffff0003", "type": "SWITCH",
-            "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
+            "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?", "name": "ROUTER1",
             "annotations": { "latitude": 37.6, "longitude": 122.3 }
         },
         {
             "uri": "of:0000ffffffff0002", "mac": "ffffffffff0002", "type": "SWITCH",
-            "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
+            "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?", "name": "ROUTER2",
             "annotations": { "latitude": 37.3, "longitude": 121.9 }
         }
     ],