Revert "OECF removed working"

This reverts commit 5f8f8f0cfb4dd0e9f633aa53b9ea8224cc986e5f.

Change-Id: I13207976c26fc210994c90e213349790a4227440
diff --git a/apps/oecfg/pom.xml b/apps/oecfg/pom.xml
new file mode 100644
index 0000000..8f595db
--- /dev/null
+++ b/apps/oecfg/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ 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.
+  -->
+<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.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.3.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.onosproject.oecfg.OELinkConfig</Main-Class>
+                                    </manifestEntries>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/apps/oecfg/src/main/java/org/onosproject/oecfg/OELinkConfig.java b/apps/oecfg/src/main/java/org/onosproject/oecfg/OELinkConfig.java
new file mode 100644
index 0000000..480640c
--- /dev/null
+++ b/apps/oecfg/src/main/java/org/onosproject/oecfg/OELinkConfig.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.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("switchConfig", opticalSwitches(json));
+        result.set("linkConfig", 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/pom.xml b/apps/pom.xml
index e08f306..e30973e 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -41,6 +41,7 @@
         <module>sdnip</module>
         <module>optical</module>
         <module>metrics</module>
+        <module>oecfg</module>
         <module>routing</module>
         <module>routing-api</module>
         <module>reactive-routing</module>
diff --git a/tools/test/topos/opticalUtils.py b/tools/test/topos/opticalUtils.py
index 3315ce6..5b47d53 100644
--- a/tools/test/topos/opticalUtils.py
+++ b/tools/test/topos/opticalUtils.py
@@ -4,7 +4,7 @@
 Notes:
 
 This file contains classes and methods useful for integrating LincOE with Mininet, 
-such as startOE, stopOE, OpticalLink, and OpticalSwitch
+such as startOE, stopOE, LINCLink, and OpticalSwitch
 
 - $ONOS_ROOT ust be set
 - Need to run with sudo -E to preserve ONOS_ROOT env var
@@ -22,10 +22,10 @@
 
             Usage:
         ------------
-    - import OpticalLink and OpticalSwitch from this module
+    - import LINCLink and OpticalSwitch from this module
     - import startOE and stopOE from this module
     - create topology as you would a normal topology. when 
-      to an optical switch with topo.addLink, always specify cls=OpticalLink
+      to an optical switch with topo.addLink, always specify cls=LINCLink
     - when creating an optical switch, use cls=OpticalSwitch in topo.addSwitch
     - for annotations on links and switches, a dictionary must be passed in as
       the annotations argument
@@ -51,11 +51,12 @@
 Topology configuration file to ONOS.
 
 '''
-
+import sys
 import re
 import json
 import os
 from time import sleep
+import urllib2
 
 from mininet.node import Switch, RemoteController
 from mininet.topo import Topo
@@ -65,25 +66,127 @@
 from mininet.link import Link, Intf
 from mininet.cli import CLI
 
-class OpticalSwitch( Switch ):
+# Sleep time and timeout values in seconds
+SLEEP_TIME = 2
+TIMEOUT = 60
 
-    def __init__( self, name, dpid=None, allowed=True,
-                  switchType='ROADM', annotations={}, **params ):
+class OpticalSwitch(Switch):
+    """
+    For now, same as Switch class.
+    """
+    pass
+
+class OpticalIntf(Intf):
+    """
+    For now,same as Intf class.
+    """
+    pass
+
+class OpticalLink(Link):
+    """
+    For now, same as Link.
+    """
+    pass
+
+class LINCSwitch(OpticalSwitch):
+    """
+    LINCSwitch class
+    """
+    # FIXME:Sometimes LINC doesn't remove pipes and on restart increase the pipe
+    # number from erlang.pipe.1.* to erlang.pipe.2.*, so should read and write
+    # from latest pipe files. For now we are removing all the pipes before
+    # starting LINC.
+    ### User Name ###
+    user = os.getlogin()
+    ### pipes ###
+    readPipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.r".format(user)
+    writePipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.w".format(user)
+    ### sys.config path ###
+    sysConfig = "/home/{}/linc-oe/rel/linc/releases/1.0/sys.config".format(user)
+    ### method, mapping dpid to LINC switchId ###
+    @staticmethod
+    def dpids_to_ids(sysConfig):
+        '''
+        return the dict containing switch dpids as key and LINC switch id as values
+        '''
+        dpids_to_ids = {}
+        fd = None
+        try:
+            with open(sysConfig, 'r', 0) as fd:
+                switch_id = 1
+                for line in fd:
+                    dpid = re.search(r'([0-9A-Fa-f]{2}[:-]){7}([0-9A-Fa-f]{2})+', line, re.I)
+                    if dpid:
+                        dpids_to_ids[dpid.group().replace(':', '')] = switch_id
+                        switch_id += 1
+            return dpids_to_ids     
+        except:
+            print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info())
+            fd.close()
+            return None
+    ### dict of containing dpids as key and corresponding LINC switchId as values ###
+    dpidsToLINCSwitchId = dpids_to_ids.__func__(sysConfig)
+    @staticmethod
+    def findDir(directory, userName):
+        "finds and returns the path of any directory in the user's home directory"
+        homeDir = '/home/' + userName
+        Dir = quietRun('find %s -maxdepth 1 -name %s -type d' % (homeDir, directory)).strip('\n')
+        DirList = Dir.split('\n')
+        if not Dir:
+            return None
+        elif len(DirList) > 1 :
+            warn('***WARNING: Found multiple instances of %s; using %s\n'
+                     % (directory, DirList[ 0 ]))
+            return DirList[ 0 ]
+        else:
+            return Dir
+    ### ONOS Directory ###
+    try:
+        onosDir = os.environ[ 'ONOS_ROOT' ]
+    except:
+        onosDir = findDir('onos', user)
+        if not onosDir:
+            error('Please set ONOS_ROOT environment variable!\n')
+        else:
+            os.environ[ 'ONOS_ROOT' ] = onosDir
+    ### LINC-directory
+    lincDir = findDir.__func__('linc-oe', user)
+    if not lincDir:
+        error("***ERROR: Could not find linc-oe in user's home directory\n")
+    ### LINC config generator directory###
+    configGen = findDir.__func__('LINC-config-generator', user)
+    if not configGen:
+        error("***ERROR: Could not find LINC-config-generator in user's home directory\n")
+    # list of all the controllers
+    controllers = None
+    def __init__(self, name, dpid=None, allowed=True,
+                  switchType='ROADM', topo=None, annotations={}, controller=None, **params):
         params[ 'inNamespace' ] = False
-        Switch.__init__( self, name, dpid=dpid, **params )
+        Switch.__init__(self, name, dpid=dpid, **params)
         self.name = name
         self.annotations = annotations
         self.allowed = allowed
         self.switchType = switchType
-        self.configDict = {} # dictionary that holds all of the JSON configuration data
+        self.configDict = {}  # dictionary that holds all of the JSON configuration data
+        self.crossConnects = []
+        self.deletedCrossConnects = []
+        self.controller = controller
+        self.lincId = self._get_linc_id()  # use to communicate with LINC
+        self.lincStarted = False
 
-    def start( self, *opts, **params ):
+    def start(self, *opts, **params):
         '''Instead of starting a virtual switch, we build the JSON
            dictionary for the emulated optical switch'''
+        # TODO:Once LINC has the ability to spawn network element dynamically
+        # we need to use this method to spawn new logical LINC switch rather then
+        # bulding JSON.
+        # if LINC is started then we can start and stop logical switches else create JSON
+        if self.lincStarted:
+            return self.start_oe()
         self.configDict[ 'uri' ] = 'of:' + self.dpid
         self.configDict[ 'annotations' ] = self.annotations
-        self.configDict[ 'annotations' ].setdefault( 'name', self.name )
-        self.configDict[ 'hw' ] = 'OE'
+        self.configDict[ 'annotations' ].setdefault('name', self.name)
+        self.configDict[ 'hw' ] = 'LINC-OE'
         self.configDict[ 'mfr' ] = 'Linc'
         self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1]
         self.configDict[ 'type' ] = self.switchType
@@ -92,74 +195,441 @@
             if intf.name == 'lo':
                 continue
             else:
-                self.configDict[ 'ports' ].append( intf.json() )
+                self.configDict[ 'ports' ].append(intf.json())
+        self.lincStarted = True
 
+    def stop(self, deleteIntfs=False):
+        '''
+        stop the existing switch
+        '''
+        # TODO:Add support for deleteIntf
+        self.stop_oe()
 
-    def json( self ):
-        "return json configuration dictionary for switch"
-        return self.configDict
-    
-    def terminate( self ):
+    def dpctl( self, *args ):
+        "Run dpctl command: ignore for now"
         pass
 
-class OpticalLink( Link ):
+    def write_to_cli(self, command):
+        '''
+        send command to LINC
+        '''
+        fd = None
+        try:
+            fd = open(self.writePipe, 'w', 0)
+            fd.write(command)
+            fd.close()
+        except:
+            print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info())
+            if fd:
+                fd.close()
 
-    def __init__( self, node1, node2, port1=None, port2=None, allowed=True,
+    def read_from_cli(self):
+        '''
+        read the output from the LINC CLI
+        '''
+        response = None
+        fd = None
+        try:
+            fd = open(self.readPipe, 'r', 0)
+            fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)  # for non-blocking read
+            # FIXME:Due to non-blocking read most for the time we read nothing
+            response = fd.read()
+            fd.close()
+        except :
+            # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info())
+            if fd:
+                fd.close()
+        return response
+
+    def _get_linc_id(self):
+        '''
+        return the corresponding LINC switchId.
+        '''
+        return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid)
+    #--------------------------------------------------------------------------
+    # LINC CLI commands
+    #--------------------------------------------------------------------------
+    def start_oe(self):
+        '''
+        start the existing LINC switch
+        '''
+        #starting Switch
+        cmd = "linc:start_switch({}).\r\n".format(self.lincId)
+        self.write_to_cli(cmd)
+        #hanlding taps interfaces related to the switch
+        crossConnectJSON = {}
+        linkConfig = []
+        for i in range(0,len(self.deletedCrossConnects)):
+            crossConnect = self.deletedCrossConnects.pop()
+            tap = None
+            if isinstance(crossConnect.intf1.node, LINCSwitch):
+                intf = crossConnect.intf2
+                tapPort = crossConnect.intf1.port
+            else:
+                intf = crossConnect.intf1
+                tapPort = crossConnect.intf2.port
+            tap = LINCSwitch.findTap(self, tapPort)
+            if tap:
+                LINCSwitch.setupInts([tap])
+                intf.node.attach(tap)
+            self.crossConnects.append(crossConnect)
+            linkConfig.append(crossConnect.json())
+        #Sending crossConnect info to the ONOS.
+        crossConnectJSON['links'] = linkConfig
+        with open("crossConnect.json", 'w') as fd:
+            json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': '))
+        info('*** Pushing crossConnect.json to ONOS\n')
+        output = quietRun('%s/tools/test/bin/onos-topo-cfg %s\
+         Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True)
+
+    def stop_oe(self):
+        '''
+        stop the existing LINC switch
+        '''
+        cmd = "linc:stop_switch({}).\r\n".format(self.lincId)
+        self.write_to_cli(cmd)
+        #handling taps if any
+        for i in range(0, len(self.crossConnects)):
+            crossConnect = self.crossConnects.pop()
+            if isinstance(crossConnect.intf1.node, LINCSwitch):
+                intf = crossConnect.intf2
+                tapPort = crossConnect.intf1.port
+            else:
+                intf = crossConnect.intf1
+                tapPort = crossConnect.intf2.port
+            intf.node.detach(LINCSwitch.findTap(self, tapPort))
+            self.deletedCrossConnects.append(crossConnect)
+
+    def w_port_up(self, port):
+        '''
+        port_up
+        '''
+        cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port)
+        self.write_to_cli(cmd)
+
+    def w_port_down(self, port):
+        '''
+        port_down
+        '''
+        cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port)
+        self.write_to_cli(cmd)
+
+    # helper functions
+    @staticmethod
+    def switchJSON(switch):
+        "Returns the json configuration for a packet switch"
+        configDict = {}
+        configDict[ 'uri' ] = 'of:' + switch.dpid
+        configDict[ 'mac' ] = quietRun('cat /sys/class/net/%s/address' % switch.name).strip('\n').translate(None, ':')
+        configDict[ 'hw' ] = 'PK'  # FIXME what about OVS?
+        configDict[ 'mfr' ] = 'Linc'  # FIXME what about OVS?
+        configDict[ 'type' ] = 'SWITCH'  # FIXME what about OVS?
+        annotations = switch.params.get('annotations', {})
+        annotations.setdefault('name', switch.name)
+        configDict[ 'annotations' ] = annotations
+        ports = []
+        for port, intf in switch.intfs.items():
+            if intf.name == 'lo':
+                continue
+            portDict = {}
+            portDict[ 'port' ] = port
+            portDict[ 'type' ] = 'FIBER' if isinstance(intf.link, LINCLink) else 'COPPER'
+            intfList = [ intf.link.intf1, intf.link.intf2 ]
+            intfList.remove(intf)
+            portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0
+            ports.append(portDict)
+        configDict[ 'ports' ] = ports
+        return configDict
+
+    @staticmethod
+    def bootOE(net):
+        "Start the LINC optical emulator within a mininet instance"
+        opticalJSON = {}
+        linkConfig = []
+        devices = []
+        #setting up the controllers for LINCSwitch class
+        LINCSwitch.controllers = net.controllers
+
+        for switch in net.switches:
+            if isinstance(switch, OpticalSwitch):
+                devices.append(switch.json())
+            else:
+                devices.append(LINCSwitch.switchJSON(switch))
+        opticalJSON[ 'devices' ] = devices
+
+        for link in net.links:
+            if isinstance(link, LINCLink) :
+                linkConfig.append(link.json())
+        opticalJSON[ 'links' ] = linkConfig
+
+        info('*** Writing Topology.json file\n')
+        with open('Topology.json', 'w') as outfile:
+            json.dump(opticalJSON, outfile, indent=4, separators=(',', ': '))
+
+        info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file\n')
+        output = quietRun('%s/tools/test/bin/onos-oecfg ./Topology.json > TopoConfig.json' % LINCSwitch.onosDir, shell=True)
+        if output:
+            error('***ERROR: Error creating topology file: %s ' % output + '\n')
+            return False
+
+        info('*** Creating sys.config...\n')
+        output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s'
+                        % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True)
+        if output:
+            error('***ERROR: Error creating sys.config file: %s\n' % output)
+            return False
+
+        info ('*** Setting multiple controllers in sys.config...\n')
+        searchStr = '\[{"Switch.*$'
+        ctrlStr = ''
+        for index in range(len(LINCSwitch.controllers)):
+            ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port)
+        replaceStr = '[%s]},' % ctrlStr[:-1]  # Cut off last comma
+        sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr)
+        output = quietRun(sedCmd, shell=True)
+
+        info('*** Copying sys.config to linc-oe directory: ', output + '\n')
+        output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n')
+        info(output + '\n')
+
+        info('*** Adding taps and bringing them up...\n')
+        LINCSwitch.setupInts(LINCSwitch.getTaps())
+
+        info('*** removing pipes if any \n')
+        quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True)
+
+        info('*** Starting linc OE...\n')
+        output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True)
+        if output:
+            error('***ERROR: LINC-OE: %s' % output + '\n')
+            quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
+            return False
+
+        info('*** Waiting for linc-oe to start...\n')
+        LINCSwitch.waitStarted(net)
+
+        info('*** Adding cross-connect (tap) interfaces to packet switches...\n')
+        for link in net.links:
+            if isinstance(link, LINCLink):
+                if link.annotations[ 'optical.type' ] == 'cross-connect':
+                    for intf in [ link.intf1, link.intf2 ]:
+                        if not isinstance(intf, LINCIntf):
+                            intfList = [ intf.link.intf1, intf.link.intf2 ]
+                            intfList.remove(intf)
+                            intf2 = intfList[ 0 ]
+                            intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ]))
+
+        info('*** Waiting for all devices to be available in ONOS...\n')
+        url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip
+        time = 0
+        while True:
+            response = json.load(urllib2.urlopen(url))
+            devs = response.get('devices')
+
+            # Wait for all devices to be registered
+            if (len(devices) != len(devs)):
+                continue
+
+            # Wait for all devices to available
+            available = True
+            for d in devs:
+                available &= d['available']
+            if available:
+                break
+
+            if (time >= TIMEOUT):
+                error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT)
+                break
+
+            time += SLEEP_TIME
+            sleep(SLEEP_TIME)
+
+        info('*** Pushing Topology.json to ONOS\n')
+        for index in range(len(LINCSwitch.controllers)):
+            output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json &' % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True)
+            # successful output contains the two characters '{}'
+            # if there is more output than this, there is an issue
+            if output.strip('{}'):
+                warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
+
+    @staticmethod
+    def waitStarted(net, timeout=TIMEOUT):
+        "wait until all tap interfaces are available"
+        tapCount = 0
+        time = 0
+        for link in net.links:
+            if isinstance(link, LINCLink):
+                if link.annotations[ 'optical.type' ] == 'cross-connect':
+                    tapCount += 1
+
+        while True:
+            if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'):
+                return True
+            if timeout:
+                if time >= TIMEOUT:
+                    error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT)
+                    return False
+                time += SLEEP_TIME
+            sleep(SLEEP_TIME)
+
+    @staticmethod
+    def shutdownOE():
+        "stop the optical emulator"
+        info('*** Stopping linc OE...\n')
+        quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True)
+
+    @staticmethod
+    def setupInts(intfs):
+        '''
+        add taps and bring them up.
+        '''
+        for i in intfs:
+            quietRun('ip tuntap add dev %s mode tap' % i)
+            quietRun('ip link set dev %s up' % i)
+            info('*** Intf %s set\n' % i)
+
+    @staticmethod
+    def getTaps(path=None):
+        '''
+        return list of all the tops in sys.config
+        '''
+        if path is None:
+            path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
+        fd = open(path, 'r', 0)
+        sys_data = fd.read()
+        taps = re.findall('tap\d+', sys_data)
+        fd.close()
+        return taps
+
+    @staticmethod
+    def findUser():
+        "Try to return logged-in (usually non-root) user"
+        try:
+            # If we're running sudo
+            return os.environ[ 'SUDO_USER' ]
+        except:
+            try:
+                # Logged-in user (if we have a tty)
+                return quietRun('who am i').split()[ 0 ]
+            except:
+                # Give up and return effective user
+                return quietRun('whoami')
+
+
+    @staticmethod
+    def findTap(node, port, path=None):
+        '''utility function to parse through a sys.config
+           file to find tap interfaces for a switch'''
+        switch = False
+        portLine = ''
+        intfLines = []
+
+        if path is None:
+            path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir
+
+        with open(path) as f:
+            for line in f:
+                if 'tap' in line:
+                    intfLines.append(line)
+                if node.dpid in line.translate(None, ':'):
+                    switch = True
+                    continue
+                if switch:
+                    if 'switch' in line:
+                        switch = False
+                    if 'port_no,%s}' % port in line:
+                        portLine = line
+                        break
+
+        if portLine:
+            m = re.search('port,\d+', portLine)
+            port = m.group(0).split(',')[ 1 ]
+        else:
+            error('***ERROR: Could not find any ports in sys.config\n')
+            return
+
+        for intfLine in intfLines:
+            if 'port,%s' % port in intfLine:
+                return re.findall('tap\d+', intfLine)[ 0 ]
+
+    def json(self):
+        "return json configuration dictionary for switch"
+        return self.configDict
+
+    def terminate(self):
+        pass
+
+class LINCLink(Link):
+    """
+    LINC link class
+    """
+    def __init__(self, node1, node2, port1=None, port2=None, allowed=True,
                   intfName1=None, intfName2=None, linkType='OPTICAL',
-                  annotations={}, speed1=0, speed2=0, **params ):
+                  annotations={}, speed1=0, speed2=0, **params):
         "Creates a dummy link without a virtual ethernet pair."
         self.allowed = allowed
         self.annotations = annotations
         self.linkType = linkType
+        self.port1 = port1
+        self.port2 = port2
         params1 = { 'speed': speed1 }
         params2 = { 'speed': speed2 }
-        
-        if isinstance( node1, OpticalSwitch ):
-            cls1 = OpticalIntf
+        # self.isCrossConnect = True if self.annotations.get('optical.type') == 'cross-connect' else False
+        if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch):
+            self.isCrossConnect = False
+        else:
+            self.isCrossConnect = True
+        if isinstance(node1, LINCSwitch):
+            cls1 = LINCIntf
+            if self.isCrossConnect:
+                node1.crossConnects.append(self)
         else:
             cls1 = Intf
             # bad hack to stop error message from appearing when we try to set up intf in a packet switch, 
             # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up
             intfName1 = 'lo'
-        if isinstance( node2, OpticalSwitch ):
-            cls2 = OpticalIntf
+        if isinstance(node2, LINCSwitch):
+            cls2 = LINCIntf
+            if self.isCrossConnect:
+                node2.crossConnects.append(self)
         else:
             cls2 = Intf
             intfName2 = 'lo'
-        Link.__init__( self, node1, node2, port1=port1, port2=port2,
+        Link.__init__(self, node1, node2, port1=port1, port2=port2,
                        intfName1=intfName1, intfName2=intfName2, cls1=cls1,
-                       cls2=cls2, params1=params1, params2=params2 )
-        
+                       cls2=cls2, params1=params1, params2=params2)
 
     @classmethod
-    def makeIntfPair( _cls, intfName1, intfName2, *args, **kwargs ):
+    def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs):
         pass
 
-    def json( self ):
+    def json(self):
         "build and return the json configuration dictionary for this link"
         configData = {}
-        configData[ 'src' ] = ( 'of:' +  self.intf1.node.dpid + 
-                                '/%s' % self.intf1.node.ports[ self.intf1 ] )
-        configData[ 'dst' ] = ( 'of:' +  self.intf2.node.dpid +
-                                '/%s' % self.intf2.node.ports[ self.intf2 ] )
+        configData[ 'src' ] = ('of:' + self.intf1.node.dpid +
+                                '/%s' % self.intf1.node.ports[ self.intf1 ])
+        configData[ 'dst' ] = ('of:' + self.intf2.node.dpid +
+                                '/%s' % self.intf2.node.ports[ self.intf2 ])
         configData[ 'type' ] = self.linkType
         configData[ 'annotations' ] = self.annotations
         return configData
 
-class OpticalIntf( Intf ):
-
-    def __init__( self, name=None, node=None, speed=0, 
-                  port=None, link=None, **params ):
+class LINCIntf(OpticalIntf):
+    """
+    LINC interface class
+    """
+    def __init__(self, name=None, node=None, speed=0,
+                  port=None, link=None, **params):
         self.node = node
         self.speed = speed
         self.port = port
         self.link = link
         self.name = name
-        node.addIntf( self, port=port )
+        node.addIntf(self, port=port)
         self.params = params
         self.ip = None
 
-    def json( self ):
+    def json(self):
         "build and return the JSON information for this interface( not used right now )"
         configDict = {}
         configDict[ 'port' ] = self.port
@@ -167,356 +637,34 @@
         configDict[ 'type' ] = 'FIBER'
         return configDict
 
-    def config( self, *args, **kwargs ):
+    def config(self, *args, **kwargs):
         "dont configure a dummy interface"
         pass
 
-def switchJSON( switch ):
-    "Returns the json configuration for a packet switch"
-    configDict = {}
-    configDict[ 'uri' ] = 'of:' + switch.dpid
-    configDict[ 'mac' ] = quietRun( 'cat /sys/class/net/%s/address' % switch.name ).strip( '\n' ).translate( None, ':' )
-    configDict[ 'hw' ] = 'PK' # FIXME what about OVS?
-    configDict[ 'mfr' ] = 'Linc' # FIXME what about OVS?
-    configDict[ 'type' ] = 'SWITCH' # FIXME what about OVS?
-    annotations = switch.params.get( 'annotations', {} )
-    annotations.setdefault( 'name', switch.name )
-    configDict[ 'annotations' ] = annotations
-    ports = []
-    for port, intf in switch.intfs.items():
-        if intf.name == 'lo':
-            continue
-        portDict = {}
-        portDict[ 'port' ] = port
-        portDict[ 'type' ] = 'FIBER' if isinstance( intf.link, OpticalLink ) else 'COPPER'
-        intfList = [ intf.link.intf1, intf.link.intf2 ]
-        intfList.remove( intf )
-        portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance( intf.link, OpticalLink ) else 0
-        ports.append( portDict )
-    configDict[ 'ports' ] = ports
-    return configDict
+    def ifconfig(self, status):
+        "configure the status"
+        if status == "up":
+            return self.node.w_port_up(self.port)
+        elif status == "down":
+            return self.node.w_port_down(self.port)
 
 
-def startOE( net ):
-    "Start the LINC optical emulator within a mininet instance"
-    opticalJSON = {}
-    linkConfig = []
-    devices = []
-    
-    for switch in net.switches:
-        if isinstance( switch, OpticalSwitch ):
-            devices.append( switch.json() )
-        else:
-            devices.append( switchJSON( switch ) )
-    opticalJSON[ 'devices' ] = devices
-
-    for link in net.links:
-        if isinstance( link, OpticalLink ) :
-            linkConfig.append( link.json() )
-
-    opticalJSON[ 'links' ] = linkConfig
-
-    try:
-        onosDir = os.environ[ 'ONOS_ROOT' ]
-    except:
-        onosDir = findDir( 'onos' )
-        if not onosDir:
-            error( 'Please set ONOS_ROOT environment variable!\n' )
-            return False
-        else:
-            os.environ[ 'ONOS_ROOT' ] = onosDir
-
-    info( '*** Writing Topology.json file\n' )
-    with open( 'Topology.json', 'w' ) as outfile:
-        json.dump( opticalJSON, outfile, indent=4, separators=(',', ': ') )
-
-    info( '*** Converting Topology.json to linc-oe format (TopoConfig.json) file (not using oecfg) \n' )
-    
-    topoConfigJson = {};
-    newLinkConfig = [];
-    switchConfig = [];
-    dpIdToName = {};
-
-    #Iterate through all switches and convert the ROADM switches to linc-oe format
-    for switch in opticalJSON["devices"]:
-        if switch["type"] == "ROADM":
-            builtSwitch = {}
-
-            #set basic switch params based on annotations
-            builtSwitch["allowed"] = True;
-            builtSwitch["latitude"] = switch["annotations"]["latitude"];
-            builtSwitch["longitude"] = switch["annotations"]["longitude"];
-
-            nodeId = switch["uri"]
-
-            #convert the nodeId to linc-oe format
-            nodeDpid = dpId(nodeId);
-
-            if "name" in switch:
-                builtSwitch["name"] = switch["name"]
-            else:
-                builtSwitch["name"] = "none"
-
-            #keep track of the name corresponding to each switch dpid
-            dpIdToName[nodeDpid] = builtSwitch["name"];
-
-            builtSwitch["nodeDpid"] = nodeDpid
-
-            #set switch params and type
-            builtSwitch["params"] = {};
-            builtSwitch["params"]["numregens"] = switch["annotations"]["optical.regens"];
-            builtSwitch["type"] = "Roadm"
-
-            #append to list of switches
-            switchConfig.append(builtSwitch);
-
-    topoConfigJson["switchConfig"] = switchConfig;
-
-    #Iterate through all optical links and convert them to linc-oe format
-    for link in opticalJSON["links"]:
-        if link["type"] == "OPTICAL":
-            builtLink = {}
-
-            #set basic link params for src and dst
-            builtLink["allowed"] = True;
-            builtLink["nodeDpid1"] = dpId(link["src"])
-            builtLink["nodeDpid2"] = dpId(link["dst"])
-
-            #set more params such as name/bandwidth/port/waves if they exist
-            params = {}
-            params["nodeName1"] = dpIdToName.get(builtLink["nodeDpid1"], "none")
-            params["nodeName2"] = dpIdToName.get(builtLink["nodeDpid2"], "none")
-            if "bandwidth" in link["annotations"]:
-                params["bandwidth"] = link["annotations"]["bandwidth"]
-            params["port1"] = int(link["src"].split("/")[1])
-            params["port2"]  = int(link["dst"].split("/")[1])
-
-            if "optical.waves" in link["annotations"]:
-                params["numWaves"] = link["annotations"]["optical.waves"]
-            builtLink["params"] = params
-
-            #set type of link (WDM or pktOpt)
-            if link["annotations"]["optical.type"] == "WDM":
-                builtLink["type"] = "wdmLink"
-            else:
-                builtLink["type"] = "pktOptLink"
-
-            newLinkConfig.append(builtLink);
-
-    topoConfigJson["linkConfig"] = newLinkConfig;
-
-    #Writing to TopoConfig.json
-    with open( 'TopoConfig.json', 'w' ) as outfile:
-            json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') )
-
-    info( '*** Creating sys.config...\n' )
-    configGen = findDir( 'LINC-config-generator' )
-    if not configGen:
-        error( "***ERROR: Could not find LINC-config-generator in user's home directory\n" )
-        return False
-    output = quietRun( '%s/config_generator TopoConfig.json %s/sys.config.template %s %s'
-                    % ( configGen, configGen, net.controllers[ 0 ].ip, net.controllers[ 0 ].port ), shell=True )
-    if output:
-        error( '***ERROR: Error creating sys.config file: %s\n' % output )
-        return False
-
-    info ('*** Setting multiple controllers in sys.config...\n' )
-    searchStr = '{controllers,.*$'
-    ctrlStr = ''
-    for index in range(len(net.controllers)):
-        ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port)
-    replaceStr = '{controllers,[%s]},' % ctrlStr[:-1]         # Cut off last comma
-    sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr)
-    output = quietRun( sedCmd, shell=True )
-
-    info( '*** Copying sys.config to linc-oe directory: ', output + '\n' )
-    lincDir = findDir( 'linc-oe' )
-    if not lincDir:
-        error( "***ERROR: Could not find linc-oe in user's home directory\n" )
-        return False
-    output = quietRun( 'cp -v sys.config %s/rel/linc/releases/1.0/' % lincDir, shell=True ).strip( '\n' )
-    info( output + '\n' )
-
-    info( '*** Starting linc OE...\n' )
-    output = quietRun( '%s/rel/linc/bin/linc start' % lincDir, shell=True )
-    if output:
-        error( '***ERROR: LINC-OE: %s' % output + '\n' )
-        quietRun( '%s/rel/linc/bin/linc stop' % lincDir, shell=True )
-        return False
-    
-    info( '*** Waiting for linc-oe to start...\n' )
-    waitStarted( net )
-    
-    info( '*** Adding cross-connect (tap) interfaces to packet switches...\n' )
-    for link in net.links:
-        if isinstance( link, OpticalLink ):
-            if link.annotations[ 'optical.type' ] == 'cross-connect':
-                for intf in [ link.intf1, link.intf2 ]:
-                    if not isinstance( intf, OpticalIntf ):
-                        intfList = [ intf.link.intf1, intf.link.intf2 ]
-                        intfList.remove( intf )
-                        intf2 = intfList[ 0 ]
-                        intf.node.attach( findTap( intf2.node, intf2.node.ports[ intf2 ] ) )
-
-    info( '*** Press ENTER to push Topology.json to onos...\n' )
-    raw_input() # FIXME... we should eventually remove this
-    info( '*** Pushing Topology.json to ONOS\n' )
-    output = quietRun( '%s/tools/test/bin/onos-topo-cfg %s Topology.json' % ( onosDir, net.controllers[ 0 ].ip ), shell=True )
-    # successful output contains the two characters '{}'
-    # if there is more output than this, there is an issue
-    if output.strip( '{}' ):
-        warn( '***WARNING: Could not push topology file to ONOS: %s' % output )
-
-#converts node ids to linc-oe format, with colons every two chars
-def dpId(id):
-    nodeDpid = ""
-    id = id.split("/", 1)[0]
-    for i in range(3, len(id) - 1, 2):
-        nodeDpid += (id[i:(i + 2):]) + ":"
-    return nodeDpid[0:(len(nodeDpid) - 1)];
-        
-def waitStarted( net, timeout=None ):
-    "wait until all tap interfaces are available"
-    tapCount = 0
-    time = 0
-    for link in net.links:
-        if isinstance( link, OpticalLink ):
-            if link.annotations[ 'optical.type' ] == 'cross-connect':
-                tapCount += 1
-    
-    while True:
-        if str( tapCount ) == quietRun( 'ip addr | grep tap | wc -l', shell=True ).strip( '\n' ):
-            return True
-        if timeout:
-            if time >= timeout:
-                error( '***ERROR: Linc OE did not start within %s seconds' % timeout )
-                return False
-            time += .5
-        sleep( .5 )
-
-def stopOE():
-    "stop the optical emulator"
-    info( '*** Stopping linc OE...\n' )
-    lincDir = findDir( 'linc-oe' )
-    quietRun( '%s/rel/linc/bin/linc stop' % lincDir, shell=True )
-
-def findDir( directory ):
-    "finds and returns the path of any directory in the user's home directory"
-    user = findUser()
-    homeDir = '/home/' + user
-    Dir = quietRun( 'find %s -maxdepth 1 -name %s -type d' % ( homeDir, directory ) ).strip( '\n' )
-    DirList = Dir.split( '\n' )
-    if not Dir:
-        return None
-    elif len( DirList ) > 1 :
-        warn( '***WARNING: Found multiple instances of %s; using %s\n'
-                 % ( directory, DirList[ 0 ] ) )
-        return DirList[ 0 ]
-    else:
-        return Dir
-
-def findUser():
-    "Try to return logged-in (usually non-root) user"
-    try:
-        # If we're running sudo
-        return os.environ[ 'SUDO_USER' ]
-    except:
-        try:
-            # Logged-in user (if we have a tty)
-            return quietRun( 'who am i' ).split()[ 0 ]
-        except:
-            # Give up and return effective user
-            return quietRun( 'whoami' )
-
-
-def findTap( node, port, path=None ):
-    '''utility function to parse through a sys.config
-       file to find tap interfaces for a switch'''
-    switch=False
-    portLine = ''
-    intfLines = []
-
-    if path is None:
-        lincDir = findDir( 'linc-oe' )
-        if not lincDir:
-            error( '***ERROR: Could not find linc-oe in users home directory\n' )
-            return None
-        path = '%s/rel/linc/releases/1.0/sys.config' % lincDir
-
-    with open( path ) as f:
-        for line in f:
-            if 'tap' in line:
-                intfLines.append( line )
-            if node.dpid in line.translate( None, ':' ):
-                switch=True
-                continue
-            if switch:
-                if 'switch' in line:
-                    switch = False
-                if 'port_no,%s}' % port in line:
-                    portLine = line
-                    break 
-
-    if portLine:
-        m = re.search( 'port,\d+', portLine )
-        port = m.group( 0 ).split( ',' )[ 1 ]
-    else:
-        error( '***ERROR: Could not find any ports in sys.config\n' )
-        return
-
-    for intfLine in intfLines:
-        if 'port,%s' % port in intfLine:
-            return re.findall( 'tap\d+', intfLine )[ 0 ]
-
-
-class MininetOE( Mininet ):
+class MininetOE(Mininet):
     "Mininet with Linc-OE support (starts and stops linc-oe)"
 
-    def start( self ):
-        Mininet.start( self )
-        startOE( self )
+    def start(self):
+        Mininet.start(self)
+        LINCSwitch.bootOE(self)
 
-    def stop( self ):
-        Mininet.stop( self )
-        stopOE()
+    def stop(self):
+        Mininet.stop(self)
+        LINCSwitch.shutdownOE()
 
-    def addControllers( self, controllers ):
+    def addControllers(self, controllers):
         i = 0
         for ctrl in controllers:
-            self.addController( RemoteController( 'c%d' % i, ip=ctrl ) )
-
-
-class OpticalTestTopo( Topo ):
-
-    def build( self ):
-        opticalAnn = { 'optical.waves': 80, 'optical.type': "WDM", 'durable': True }
-        switchAnn = { 'bandwidth': 100000, 'optical.type': 'cross-connect', 'durable': True }
-        h1 = self.addHost( 'h1' )
-        h2 = self.addHost( 'h2' )
-        s1 = self.addSwitch( 's1' )
-        s2 = self.addSwitch( 's2' )
-        O4 = self.addSwitch( 'O4', cls=OpticalSwitch )
-        O5 = self.addSwitch( 'O5', cls=OpticalSwitch )
-        O6 = self.addSwitch( 'O6', cls=OpticalSwitch )
-        self.addLink( O4, O5, cls=OpticalLink, annotations=opticalAnn )
-        self.addLink( O5, O6, cls=OpticalLink, annotations=opticalAnn )
-        self.addLink( s1, O4, cls=OpticalLink, annotations=switchAnn )
-        self.addLink( s2, O6, cls=OpticalLink, annotations=switchAnn )
-        self.addLink( h1, s1 )
-        self.addLink( h2, s2 )
+            self.addController(RemoteController('c%d' % i, ip=ctrl))
+            i += 1
 
 if __name__ == '__main__':
-    import sys
-    if len( sys.argv ) >= 2:
-        controllers = sys.argv[1:]
-    else:
-        print 'Usage: ./opticalUtils.py (<Controller IP>)+'
-        print 'Using localhost...\n'
-        controllers = [ '127.0.0.1' ]
-
-    setLogLevel( 'info' )
-    net = MininetOE( topo=OpticalTestTopo(), controller=None, autoSetMacs=True )
-    net.addControllers( controllers )
-    net.start()
-    CLI( net )
-    net.stop()
+    pass