First changes into new version of mnccnxedit GUI
diff --git a/bin/miniccnxedit b/bin/miniccnxedit
old mode 100644
new mode 100755
index ec14483..5ca6191
--- a/bin/miniccnxedit
+++ b/bin/miniccnxedit
@@ -1,57 +1,620 @@
 #!/usr/bin/python
 
 """
-MiniCCNxEdit: a simple network editor for MiniCCNx
+MiniEdit: a simple network editor for Mininet
 
-Based on miniedit by Bob Lantz, April 2010
+This is a simple demonstration of how one might build a
+GUI application using Mininet as the network model.
 
-Carlos Cabral Jan, 2013
+Bob Lantz, April 2010
+Gregory Gee, July 2013
 
-
+Controller icon from http://semlabs.co.uk/
+OpenFlow icon from https://www.opennetworking.org/
 """
-import optparse
 
-from Tkinter import Frame, Button, Label, Scrollbar, Canvas
-from Tkinter import Menu, BitmapImage, PhotoImage, Wm, Toplevel
+MINIEDIT_VERSION = '2.2.0.1'
+
+from optparse import OptionParser
+from Tkinter import *
+from ttk import Notebook
+from tkMessageBox import showinfo, showerror, showwarning
+from subprocess import call
+import tkFont
+import csv
+import tkFileDialog
+import tkSimpleDialog
+import re
+import json
+from distutils.version import StrictVersion
+import os
+import sys
+from functools import partial
+
+if 'PYTHONPATH' in os.environ:
+    sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
 
 # someday: from ttk import *
 
-from mininet.log import setLogLevel
-from mininet.net import Mininet
-from mininet.util import ipStr
+from mininet.log import info, error, debug, output, setLogLevel
+from mininet.net import Mininet, VERSION
+from mininet.util import ipStr, netParse, ipAdd, quietRun
+from mininet.util import buildTopo
+from mininet.util import custom, customConstructor
 from mininet.term import makeTerm, cleanUpScreens
+from mininet.node import Controller, RemoteController, NOX, OVSController
+from mininet.node import CPULimitedHost, Host, Node
+from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch
+from mininet.link import TCLink, Intf, Link
+from mininet.cli import CLI
+from mininet.moduledeps import moduleDeps, pathCheck
+from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
+from mininet.topolib import TreeTopo
+
+print 'MiniEdit running against MiniNet '+VERSION
+MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
+if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
+    from mininet.node import IVSSwitch
+
+TOPODEF = 'none'
+TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
+          'linear': LinearTopo,
+          'reversed': SingleSwitchReversedTopo,
+          'single': SingleSwitchTopo,
+          'none': None,
+          'tree': TreeTopo }
+LINKDEF = 'default'
+LINKS = { 'default': Link,
+          'tc': TCLink }
+HOSTDEF = 'proc'
+HOSTS = { 'proc': Host,
+          'rt': custom( CPULimitedHost, sched='rt' ),
+          'cfs': custom( CPULimitedHost, sched='cfs' ) }
+
+class LegacyRouter( Node ):
+
+    def __init__( self, name, inNamespace=True, **params ):
+        Node.__init__( self, name, inNamespace, **params )
+
+    def config( self, **_params ):
+        if self.intfs:
+            self.setParam( _params, 'setIP', ip='0.0.0.0' )
+        r = Node.config( self, **_params )
+        self.cmd('sysctl -w net.ipv4.ip_forward=1')
+        return r
+
+class CustomDialog(object):
+
+        # TODO: Fix button placement and Title and window focus lock
+        def __init__(self, master, title):
+            self.top=Toplevel(master)
+
+            self.bodyFrame = Frame(self.top)
+            self.bodyFrame.grid(row=0, column=0, sticky='nswe')
+            self.body(self.bodyFrame)
+
+            #return self.b # initial focus
+            buttonFrame = Frame(self.top, relief='ridge', bd=3, bg='lightgrey')
+            buttonFrame.grid(row=1 , column=0, sticky='nswe')
+
+            okButton = Button(buttonFrame, width=8, text='OK', relief='groove',
+                       bd=4, command=self.okAction)
+            okButton.grid(row=0, column=0, sticky=E)
+
+            canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
+                        bd=4, command=self.cancelAction)
+            canlceButton.grid(row=0, column=1, sticky=W)
+
+        def body(self, master):
+            self.rootFrame = master
+
+        def apply(self):
+            self.top.destroy()
+
+        def cancelAction(self):
+            self.top.destroy()
+
+        def okAction(self):
+            self.apply()
+            self.top.destroy()
+
+class HostDialog(CustomDialog):
+
+        def __init__(self, master, title, prefDefaults):
+
+            self.prefValues = prefDefaults
+            self.result = None
+
+            CustomDialog.__init__(self, master, title)
+
+        def body(self, master):
+            self.rootFrame = master
+            n = Notebook(self.rootFrame)
+            self.propFrame = Frame(n)
+            self.vlanFrame = Frame(n)
+            self.interfaceFrame = Frame(n)
+            self.mountFrame = Frame(n)
+            n.add(self.propFrame, text='Properties')
+            n.add(self.vlanFrame, text='VLAN Interfaces')
+            n.add(self.interfaceFrame, text='External Interfaces')
+            n.add(self.mountFrame, text='Private Directories')
+            n.pack()
+
+            ### TAB 1
+            # Field for Hostname
+            Label(self.propFrame, text="Hostname:").grid(row=0, sticky=E)
+            self.hostnameEntry = Entry(self.propFrame)
+            self.hostnameEntry.grid(row=0, column=1)
+            if 'hostname' in self.prefValues:
+                self.hostnameEntry.insert(0, self.prefValues['hostname'])
+
+            # Field for Switch IP
+            Label(self.propFrame, text="IP Address:").grid(row=1, sticky=E)
+            self.ipEntry = Entry(self.propFrame)
+            self.ipEntry.grid(row=1, column=1)
+            if 'ip' in self.prefValues:
+                self.ipEntry.insert(0, self.prefValues['ip'])
+
+            # Field for default route
+            Label(self.propFrame, text="Default Route:").grid(row=2, sticky=E)
+            self.routeEntry = Entry(self.propFrame)
+            self.routeEntry.grid(row=2, column=1)
+            if 'defaultRoute' in self.prefValues:
+                self.routeEntry.insert(0, self.prefValues['defaultRoute'])
+
+            # Field for CPU
+            Label(self.propFrame, text="Amount CPU:").grid(row=3, sticky=E)
+            self.cpuEntry = Entry(self.propFrame)
+            self.cpuEntry.grid(row=3, column=1)
+            if 'cpu' in self.prefValues:
+                self.cpuEntry.insert(0, str(self.prefValues['cpu']))
+            # Selection of Scheduler
+            if 'sched' in self.prefValues:
+                sched =  self.prefValues['sched']
+            else:
+                sched = 'host'
+            self.schedVar = StringVar(self.propFrame)
+            self.schedOption = OptionMenu(self.propFrame, self.schedVar, "host", "cfs", "rt")
+            self.schedOption.grid(row=3, column=2, sticky=W)
+            self.schedVar.set(sched)
+
+            # Selection of Cores
+            Label(self.propFrame, text="Cores:").grid(row=4, sticky=E)
+            self.coreEntry = Entry(self.propFrame)
+            self.coreEntry.grid(row=4, column=1)
+            if 'cores' in self.prefValues:
+                self.coreEntry.insert(1, self.prefValues['cores'])
+
+            # Start command
+            Label(self.propFrame, text="Start Command:").grid(row=5, sticky=E)
+            self.startEntry = Entry(self.propFrame)
+            self.startEntry.grid(row=5, column=1, sticky='nswe', columnspan=3)
+            if 'startCommand' in self.prefValues:
+                self.startEntry.insert(0, str(self.prefValues['startCommand']))
+            # Stop command
+            Label(self.propFrame, text="Stop Command:").grid(row=6, sticky=E)
+            self.stopEntry = Entry(self.propFrame)
+            self.stopEntry.grid(row=6, column=1, sticky='nswe', columnspan=3)
+            if 'stopCommand' in self.prefValues:
+                self.stopEntry.insert(0, str(self.prefValues['stopCommand']))
+
+            ### TAB 2
+            # External Interfaces
+            self.externalInterfaces = 0
+            Label(self.interfaceFrame, text="External Interface:").grid(row=0, column=0, sticky=E)
+            self.b = Button( self.interfaceFrame, text='Add', command=self.addInterface)
+            self.b.grid(row=0, column=1)
+
+            self.interfaceFrame = VerticalScrolledTable(self.interfaceFrame, rows=0, columns=1, title='External Interfaces')
+            self.interfaceFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
+            self.tableFrame = self.interfaceFrame.interior
+            self.tableFrame.addRow(value=['Interface Name'], readonly=True)
+
+            # Add defined interfaces
+            externalInterfaces = []
+            if 'externalInterfaces' in self.prefValues:
+                externalInterfaces = self.prefValues['externalInterfaces']
+
+            for externalInterface in externalInterfaces:
+                self.tableFrame.addRow(value=[externalInterface])
+
+            ### TAB 3
+            # VLAN Interfaces
+            self.vlanInterfaces = 0
+            Label(self.vlanFrame, text="VLAN Interface:").grid(row=0, column=0, sticky=E)
+            self.vlanButton = Button( self.vlanFrame, text='Add', command=self.addVlanInterface)
+            self.vlanButton.grid(row=0, column=1)
+
+            self.vlanFrame = VerticalScrolledTable(self.vlanFrame, rows=0, columns=2, title='VLAN Interfaces')
+            self.vlanFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
+            self.vlanTableFrame = self.vlanFrame.interior
+            self.vlanTableFrame.addRow(value=['IP Address','VLAN ID'], readonly=True)
+
+            vlanInterfaces = []
+            if 'vlanInterfaces' in self.prefValues:
+                vlanInterfaces = self.prefValues['vlanInterfaces']
+            for vlanInterface in vlanInterfaces:
+                self.vlanTableFrame.addRow(value=vlanInterface)
+
+            ### TAB 4
+            # Private Directories
+            self.privateDirectories = 0
+            Label(self.mountFrame, text="Private Directory:").grid(row=0, column=0, sticky=E)
+            self.mountButton = Button( self.mountFrame, text='Add', command=self.addDirectory)
+            self.mountButton.grid(row=0, column=1)
+
+            self.mountFrame = VerticalScrolledTable(self.mountFrame, rows=0, columns=2, title='Directories')
+            self.mountFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
+            self.mountTableFrame = self.mountFrame.interior
+            self.mountTableFrame.addRow(value=['Mount','Persistent Directory'], readonly=True)
+
+            directoryList = []
+            if 'privateDirectory' in self.prefValues:
+                directoryList = self.prefValues['privateDirectory']
+            for privateDir in directoryList:
+                if isinstance( privateDir, tuple ):
+                    self.mountTableFrame.addRow(value=privateDir)
+                else:
+                    self.mountTableFrame.addRow(value=[privateDir,''])
 
 
-def parse_args():
-    usage="""Usage: miniccnxedit [template_file]
-	If no template_file is given, generated template will be written
-	to the file miniccnx.conf in the current directory.
-    """
+        def addDirectory( self ):
+            self.mountTableFrame.addRow()
 
-    parser = optparse.OptionParser(usage)
+        def addVlanInterface( self ):
+            self.vlanTableFrame.addRow()
+
+        def addInterface( self ):
+            self.tableFrame.addRow()
+
+        def apply(self):
+            externalInterfaces = []
+            for row in range(self.tableFrame.rows):
+                if (len(self.tableFrame.get(row, 0)) > 0 and
+                    row > 0):
+                    externalInterfaces.append(self.tableFrame.get(row, 0))
+            vlanInterfaces = []
+            for row in range(self.vlanTableFrame.rows):
+                if (len(self.vlanTableFrame.get(row, 0)) > 0 and
+                    len(self.vlanTableFrame.get(row, 1)) > 0 and
+                    row > 0):
+                    vlanInterfaces.append([self.vlanTableFrame.get(row, 0), self.vlanTableFrame.get(row, 1)])
+            privateDirectories = []
+            for row in range(self.mountTableFrame.rows):
+                if (len(self.mountTableFrame.get(row, 0)) > 0 and row > 0):
+                    if(len(self.mountTableFrame.get(row, 1)) > 0):
+                        privateDirectories.append((self.mountTableFrame.get(row, 0), self.mountTableFrame.get(row, 1)))
+                    else:
+                        privateDirectories.append(self.mountTableFrame.get(row, 0))
+
+            results = {'cpu': self.cpuEntry.get(),
+                       'cores':self.coreEntry.get(),
+                       'sched':self.schedVar.get(),
+                       'hostname':self.hostnameEntry.get(),
+                       'ip':self.ipEntry.get(),
+                       'defaultRoute':self.routeEntry.get(),
+                       'startCommand':self.startEntry.get(),
+                       'stopCommand':self.stopEntry.get(),
+                       'privateDirectory':privateDirectories,
+                       'externalInterfaces':externalInterfaces,
+                       'vlanInterfaces':vlanInterfaces}
+            self.result = results
+
+
+class VerticalScrolledTable(LabelFrame):
+    """A pure Tkinter scrollable frame that actually works!
+
+    * Use the 'interior' attribute to place widgets inside the scrollable frame
+    * Construct and pack/place/grid normally
+    * This frame only allows vertical scrolling
     
-    _, arg = parser.parse_args()
+    """
+    def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
+        LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)            
 
-    if len(arg) != 1:
-        arg = None
-    else:
-        arg = arg[0]
-        
-    return arg
+        # create a canvas object and a vertical scrollbar for scrolling it
+        vscrollbar = Scrollbar(self, orient=VERTICAL)
+        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
+        canvas = Canvas(self, bd=0, highlightthickness=0,
+                        yscrollcommand=vscrollbar.set)
+        canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
+        vscrollbar.config(command=canvas.yview)
+
+        # reset the view
+        canvas.xview_moveto(0)
+        canvas.yview_moveto(0)
+
+        # create a frame inside the canvas which will be scrolled with it
+        self.interior = interior = TableFrame(canvas, rows=rows, columns=columns)
+        interior_id = canvas.create_window(0, 0, window=interior,
+                                           anchor=NW)
+
+        # track changes to the canvas and frame width and sync them,
+        # also updating the scrollbar
+        def _configure_interior(event):
+            # update the scrollbars to match the size of the inner frame
+            size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
+            canvas.config(scrollregion="0 0 %s %s" % size)
+            if interior.winfo_reqwidth() != canvas.winfo_width():
+                # update the canvas's width to fit the inner frame
+                canvas.config(width=interior.winfo_reqwidth())
+        interior.bind('<Configure>', _configure_interior)
+
+        def _configure_canvas(event):
+            if interior.winfo_reqwidth() != canvas.winfo_width():
+                # update the inner frame's width to fill the canvas
+                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
+        canvas.bind('<Configure>', _configure_canvas)
+
+        return
+
+class TableFrame(Frame):
+    def __init__(self, parent, rows=2, columns=2):
+
+        Frame.__init__(self, parent, background="black")
+        self._widgets = []
+        self.rows = rows
+        self.columns = columns
+        for row in range(rows):
+            current_row = []
+            for column in range(columns):
+                label = Entry(self, borderwidth=0)
+                label.grid(row=row, column=column, sticky="wens", padx=1, pady=1)
+                current_row.append(label)
+            self._widgets.append(current_row)
+
+    def set(self, row, column, value):
+        widget = self._widgets[row][column]
+        widget.insert(0, value)
+
+    def get(self, row, column):
+        widget = self._widgets[row][column]
+        return widget.get()
+
+    def addRow( self, value=None, readonly=False ):
+        #print "Adding row " + str(self.rows +1)
+        current_row = []
+        for column in range(self.columns):
+            label = Entry(self, borderwidth=0)
+            label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
+            if value is not None:
+                label.insert(0, value[column])
+            if (readonly == True):
+                label.configure(state='readonly')
+            current_row.append(label)
+        self._widgets.append(current_row)
+        self.update_idletasks()
+        self.rows += 1
+
+class LinkDialog(tkSimpleDialog.Dialog):
+
+        def __init__(self, parent, title, linkDefaults):
+
+            self.linkValues = linkDefaults
+
+            tkSimpleDialog.Dialog.__init__(self, parent, title)
+
+        def body(self, master):
+
+            self.var = StringVar(master)
+            Label(master, text="Bandwidth:").grid(row=0, sticky=E)
+            self.e1 = Entry(master)
+            self.e1.grid(row=0, column=1)
+            Label(master, text="Mbit").grid(row=0, column=2, sticky=W)
+            if 'bw' in self.linkValues:
+                self.e1.insert(0,str(self.linkValues['bw']))
+
+            Label(master, text="Delay:").grid(row=1, sticky=E)
+            self.e2 = Entry(master)
+            self.e2.grid(row=1, column=1)
+            if 'delay' in self.linkValues:
+                self.e2.insert(0, self.linkValues['delay'])
+
+            Label(master, text="Loss:").grid(row=2, sticky=E)
+            self.e3 = Entry(master)
+            self.e3.grid(row=2, column=1)
+            Label(master, text="%").grid(row=2, column=2, sticky=W)
+            if 'loss' in self.linkValues:
+                self.e3.insert(0, str(self.linkValues['loss']))
+
+            Label(master, text="Max Queue size:").grid(row=3, sticky=E)
+            self.e4 = Entry(master)
+            self.e4.grid(row=3, column=1)
+            if 'max_queue_size' in self.linkValues:
+                self.e4.insert(0, str(self.linkValues['max_queue_size']))
+
+            Label(master, text="Jitter:").grid(row=4, sticky=E)
+            self.e5 = Entry(master)
+            self.e5.grid(row=4, column=1)
+            if 'jitter' in self.linkValues:
+                self.e5.insert(0, self.linkValues['jitter'])
+
+            Label(master, text="Speedup:").grid(row=5, sticky=E)
+            self.e6 = Entry(master)
+            self.e6.grid(row=5, column=1)
+            if 'speedup' in self.linkValues:
+                self.e6.insert(0, str(self.linkValues['speedup']))
+
+            return self.e1 # initial focus
+
+        def apply(self):
+            self.result = {}
+            if (len(self.e1.get()) > 0):
+                self.result['bw'] = int(self.e1.get())
+            if (len(self.e2.get()) > 0):
+                self.result['delay'] = self.e2.get()
+            if (len(self.e3.get()) > 0):
+                self.result['loss'] = int(self.e3.get())
+            if (len(self.e4.get()) > 0):
+                self.result['max_queue_size'] = int(self.e4.get())
+            if (len(self.e5.get()) > 0):
+                self.result['jitter'] = self.e5.get()
+            if (len(self.e6.get()) > 0):
+                self.result['speedup'] = int(self.e6.get())
+
+class ControllerDialog(tkSimpleDialog.Dialog):
+
+        def __init__(self, parent, title, ctrlrDefaults=None):
+
+            if ctrlrDefaults:
+                self.ctrlrValues = ctrlrDefaults
+
+            tkSimpleDialog.Dialog.__init__(self, parent, title)
+
+        def body(self, master):
+
+            self.var = StringVar(master)
+            self.protcolvar = StringVar(master)
+
+            rowCount=0
+            # Field for Hostname
+            Label(master, text="Name:").grid(row=rowCount, sticky=E)
+            self.hostnameEntry = Entry(master)
+            self.hostnameEntry.grid(row=rowCount, column=1)
+            self.hostnameEntry.insert(0, self.ctrlrValues['hostname'])
+            rowCount+=1
+
+            # Field for Remove Controller Port
+            Label(master, text="Controller Port:").grid(row=rowCount, sticky=E)
+            self.e2 = Entry(master)
+            self.e2.grid(row=rowCount, column=1)
+            self.e2.insert(0, self.ctrlrValues['remotePort'])
+            rowCount+=1
+
+            # Field for Controller Type
+            Label(master, text="Controller Type:").grid(row=rowCount, sticky=E)
+            controllerType = self.ctrlrValues['controllerType']
+            self.o1 = OptionMenu(master, self.var, "Remote Controller", "In-Band Controller", "OpenFlow Reference", "OVS Controller")
+            self.o1.grid(row=rowCount, column=1, sticky=W)
+            if controllerType == 'ref':
+                self.var.set("OpenFlow Reference")
+            elif controllerType == 'inband':
+                self.var.set("In-Band Controller")
+            elif controllerType == 'remote':
+                self.var.set("Remote Controller")
+            else:
+                self.var.set("OVS Controller")
+            rowCount+=1
+
+            # Field for Controller Protcol
+            Label(master, text="Protocol:").grid(row=rowCount, sticky=E)
+            if 'controllerProtocol' in self.ctrlrValues:
+               controllerProtocol = self.ctrlrValues['controllerProtocol']
+            else:
+               controllerProtocol = 'tcp'
+            self.protcol = OptionMenu(master, self.protcolvar, "TCP", "SSL")
+            self.protcol.grid(row=rowCount, column=1, sticky=W)
+            if controllerProtocol == 'ssl':
+                self.protcolvar.set("SSL")
+            else:
+                self.protcolvar.set("TCP")
+            rowCount+=1
+
+            # Field for Remove Controller IP
+            remoteFrame= LabelFrame(master, text='Remote/In-Band Controller', padx=5, pady=5)
+            remoteFrame.grid(row=rowCount, column=0, columnspan=2, sticky=W)
+
+            Label(remoteFrame, text="IP Address:").grid(row=0, sticky=E)
+            self.e1 = Entry(remoteFrame)
+            self.e1.grid(row=0, column=1)
+            self.e1.insert(0, self.ctrlrValues['remoteIP'])
+            rowCount+=1
+
+            return self.hostnameEntry # initial focus
+
+        def apply(self):
+            self.result = { 'hostname': self.hostnameEntry.get(),
+                            'remoteIP': self.e1.get(),
+                            'remotePort': int(self.e2.get())}
+
+            controllerType = self.var.get()
+            if controllerType == 'Remote Controller':
+                self.result['controllerType'] = 'remote'
+            elif controllerType == 'In-Band Controller':
+                self.result['controllerType'] = 'inband'
+            elif controllerType == 'OpenFlow Reference':
+                self.result['controllerType'] = 'ref'
+            else:
+                self.result['controllerType'] = 'ovsc'
+            controllerProtocol = self.protcolvar.get()
+            if controllerProtocol == 'SSL':
+                self.result['controllerProtocol'] = 'ssl'
+            else:
+                self.result['controllerProtocol'] = 'tcp'
+
+class ToolTip(object):
+
+    def __init__(self, widget):
+        self.widget = widget
+        self.tipwindow = None
+        self.id = None
+        self.x = self.y = 0
+
+    def showtip(self, text):
+        "Display text in tooltip window"
+        self.text = text
+        if self.tipwindow or not self.text:
+            return
+        x, y, cx, cy = self.widget.bbox("insert")
+        x = x + self.widget.winfo_rootx() + 27
+        y = y + cy + self.widget.winfo_rooty() +27
+        self.tipwindow = tw = Toplevel(self.widget)
+        tw.wm_overrideredirect(1)
+        tw.wm_geometry("+%d+%d" % (x, y))
+        try:
+            # For Mac OS
+            tw.tk.call("::tk::unsupported::MacWindowStyle",
+                       "style", tw._w,
+                       "help", "noActivates")
+        except TclError:
+            pass
+        label = Label(tw, text=self.text, justify=LEFT,
+                      background="#ffffe0", relief=SOLID, borderwidth=1,
+                      font=("tahoma", "8", "normal"))
+        label.pack(ipadx=1)
+
+    def hidetip(self):
+        tw = self.tipwindow
+        self.tipwindow = None
+        if tw:
+            tw.destroy()
 
 class MiniEdit( Frame ):
 
     "A simple network editor for Mininet."
 
-    def __init__( self, parent=None, cheight=200, cwidth=500, template_file='miniccnx.conf' ):
+    def __init__( self, parent=None, cheight=600, cwidth=1000 ):
+
+        self.defaultIpBase='10.0.0.0/8'
+
+        self.nflowDefaults = {'nflowTarget':'',
+                              'nflowTimeout':'600',
+                              'nflowAddId':'0'}
+        self.sflowDefaults = {'sflowTarget':'',
+                              'sflowSampling':'400',
+                              'sflowHeader':'128',
+                              'sflowPolling':'30'}
+
+        self.appPrefs={
+            "ipBase": self.defaultIpBase,
+            "startCLI": "0",
+            "terminalType": 'xterm',
+            "switchType": 'ovs',
+            "dpctl": '',
+            'sflow':self.sflowDefaults,
+            'netflow':self.nflowDefaults,
+            'openFlowVersions':{'ovsOf10':'1',
+                                'ovsOf11':'0',
+                                'ovsOf12':'0',
+                                'ovsOf13':'0'}
+
+        }
+
 
         Frame.__init__( self, parent )
         self.action = None
-        self.appName = 'Mini-CCNx'
-        if template_file == None:
-            self.template_file='miniccnx.conf'
-        else:
-	        self.template_file = template_file
+        self.appName = 'MiniEdit'
+        self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
 
         # Style
         self.font = ( 'Geneva', 9 )
@@ -70,11 +633,14 @@
         self.cframe, self.canvas = self.createCanvas()
 
         # Toolbar
+        self.controllers = {}
+
+        # Toolbar
         self.images = miniEditImages()
         self.buttons = {}
         self.active = None
-        self.tools = ( 'Select', 'Host', 'Switch', 'Link' )
-        self.customColors = { 'Switch': 'darkGreen', 'Host': 'blue' }
+        self.tools = ( 'Select', 'Host', 'LegacyRouter', 'NetLink' )
+        self.customColors = { 'LegacyRouter': 'darkGreen', 'Host': 'blue' }
         self.toolbar = self.createToolbar()
 
         # Layout
@@ -89,7 +655,7 @@
 
         # Initialize node data
         self.nodeBindings = self.createNodeBindings()
-        self.nodePrefixes = { 'Switch': 's', 'Host': 'h' }
+        self.nodePrefixes = { 'LegacyRouter': 'r', 'Host': 'h'}
         self.widgetToItem = {}
         self.itemToWidget = {}
 
@@ -105,13 +671,32 @@
         self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
         self.focus()
 
+	#Mouse bindings
+
+	self.bind( '<Button-1>', lambda event: self.clearPopups )
+
+        self.hostPopup = Menu(self.top, tearoff=0)
+        self.hostPopup.add_command(label='Host Options', font=self.font)
+        self.hostPopup.add_separator()
+        self.hostPopup.add_command(label='Properties', font=self.font, command=self.hostDetails )
+
+        self.legacyRouterPopup = Menu(self.top, tearoff=0)
+        self.legacyRouterPopup.add_command(label='Router Options', font=self.font)
+
+        self.linkPopup = Menu(self.top, tearoff=0)
+        self.linkPopup.add_command(label='Link Options', font=self.font)
+
         # Event handling initalization
         self.linkx = self.linky = self.linkItem = None
         self.lastSelection = None
 
         # Model initialization
         self.links = {}
-        self.nodeCount = 0
+        self.hostOpts = {}
+        self.switchOpts = {}
+        self.hostCount = 0
+        self.switchCount = 0
+        self.controllerCount = 0
         self.net = None
 
         # Close window gracefully
@@ -122,7 +707,7 @@
         self.stop()
         Frame.quit( self )
 
-    def createMenubar( self ):
+    def createMenubar( self ): # MODIFICADO - OK
         "Create our menu bar."
 
         font = self.font
@@ -130,6 +715,26 @@
         mbar = Menu( self.top, font=font )
         self.top.configure( menu=mbar )
 
+
+        #fileMenu = Menu( mbar, tearoff=False )
+        #mbar.add_cascade( label="File", font=font, menu=fileMenu )
+        #fileMenu.add_command( label="New", font=font, command=self.newTopology )
+        #fileMenu.add_command( label="Open", font=font, command=self.loadTopology )
+        #fileMenu.add_command( label="Save", font=font, command=self.saveTopology )
+        #fileMenu.add_command( label="Export Level 2 Script", font=font, command=self.exportScript )
+        #fileMenu.add_separator()
+        #fileMenu.add_command( label='Quit', command=self.quit, font=font )
+
+        #editMenu.add_command( label="Preferences", font=font, command=self.prefDetails)
+
+        #runMenu = Menu( mbar, tearoff=False )
+        #mbar.add_cascade( label="Run", font=font, menu=runMenu )
+        #runMenu.add_command( label="Run", font=font, command=self.doRun )
+        #runMenu.add_command( label="Stop", font=font, command=self.doStop )
+        #fileMenu.add_separator()
+        #runMenu.add_command( label='Show OVS Summary', font=font, command=self.ovsShow )
+        #runMenu.add_command( label='Root Terminal', font=font, command=self.rootTerminal )
+
         # Application menu
         appMenu = Menu( mbar, tearoff=False )
         mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
@@ -137,28 +742,13 @@
                              font=font)
         appMenu.add_separator()
         appMenu.add_command( label='Quit', command=self.quit, font=font )
-
-        #fileMenu = Menu( mbar, tearoff=False )
-        #mbar.add_cascade( label="File", font=font, menu=fileMenu )
-        #fileMenu.add_command( label="Load...", font=font )
-        #fileMenu.add_separator()
-        #fileMenu.add_command( label="Save", font=font )
-        #fileMenu.add_separator()
-        #fileMenu.add_command( label="Print", font=font )
-
+	
         editMenu = Menu( mbar, tearoff=False )
         mbar.add_cascade( label="Edit", font=font, menu=editMenu )
         editMenu.add_command( label="Cut", font=font,
                               command=lambda: self.deleteSelection( None ) )
 
-       # runMenu = Menu( mbar, tearoff=False )
-       # mbar.add_cascade( label="Run", font=font, menu=runMenu )
-       # runMenu.add_command( label="Run", font=font, command=self.doRun )
-       # runMenu.add_command( label="Stop", font=font, command=self.doStop )
-       # runMenu.add_separator()
-       # runMenu.add_command( label='Xterm', font=font, command=self.xterm )
-
-    # Canvas
+    # Canvas - TUDO IGUAL - OK
 
     def createCanvas( self ):
         "Create and return our scrolling canvas frame."
@@ -213,7 +803,7 @@
 
     # Toolbar
 
-    def activate( self, toolName ):
+    def activate( self, toolName ): #IGUAL - OK
         "Activate a tool and press its button."
         # Adjust button appearance
         if self.active:
@@ -222,7 +812,17 @@
         # Activate dynamic bindings
         self.active = toolName
 
-    def createToolbar( self ):
+
+    def createToolTip(self, widget, text): #NOVA - CRIA HINTS E TIPS
+        toolTip = ToolTip(widget)
+        def enter(event):
+            toolTip.showtip(text)
+        def leave(event):
+            toolTip.hidetip()
+        widget.bind('<Enter>', enter)
+        widget.bind('<Leave>', leave)
+
+    def createToolbar( self ): #MODIFICADO - OK
         "Create and return our toolbar frame."
 
         toolbar = Frame( self )
@@ -232,7 +832,8 @@
             cmd = ( lambda t=tool: self.activate( t ) )
             b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
             if tool in self.images:
-                b.config( height=50, image=self.images[ tool ] )
+                b.config( height=35, image=self.images[ tool ] )
+                self.createToolTip(b, str(tool))
                 # b.config( compound='top' )
             b.pack( fill='x' )
             self.buttons[ tool ] = b
@@ -248,16 +849,17 @@
         #                fg=color, command=doCmd )
         #    b.pack( fill='x', side='bottom' )
 
-	for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
+	# abaixo copiado Mini-CCNx para criar botao Generate
+
+        for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
             doCmd = getattr( self, 'do' + cmd )
             b = Button( toolbar, text=cmd, font=self.smallFont,
                         fg=color, command=doCmd )
             b.pack( fill='x', side='bottom' )
 
-
         return toolbar
 
-    def doGenerate( self ):
+    def doGenerate( self ): #COPIA Mini-CCNx - GERA TEMPLATE
         "Generate template."
         self.activate( 'Select' )
         for tool in self.tools:
@@ -265,44 +867,644 @@
 
         self.buildTemplate()
 
-	for tool in self.tools:
+        for tool in self.tools:
             self.buttons[ tool ].config( state='normal' )
 
+    #def doRun( self ):
+    #    "Run command."
+    #    self.activate( 'Select' )
+    #    for tool in self.tools:
+    #        self.buttons[ tool ].config( state='disabled' )
+    #    self.start()
+
     def doStop( self ):
         "Stop command."
         self.stop()
         for tool in self.tools:
             self.buttons[ tool ].config( state='normal' )
 
-    def buildTemplate( self ):
-	"Generate template"
-	
-	template = open(self.template_file, 'w')
+    def buildTemplate( self ): #COPIA Mini-CCNx para criar Template
+        "Generate template"
 
-	# hosts
-	template.write('[nodes]\n')
-	for widget in self.widgetToItem:
+        template = open(self.template_file, 'w')
+
+        # hosts
+        template.write('[hosts]\n')
+        for widget in self.widgetToItem:
             name = widget[ 'text' ]
             tags = self.canvas.gettags( self.widgetToItem[ widget ] )
-	    if 'Host' in tags:
-		template.write(name + ':\n')	
+            if 'Host' in tags:
+                template.write(name + ':\n')
 
-	# switches/routers
-	# template.write('[routers]\n')
-	for widget in self.widgetToItem:
+        # switches/routers
+        template.write('[routers]\n')
+        for widget in self.widgetToItem:
             name = widget[ 'text' ]
             tags = self.canvas.gettags( self.widgetToItem[ widget ] )
-	    if 'Switch' in tags:
-		template.write(name + ':\n')	
+            if 'Switch' in tags:
+                template.write(name + ':\n')
 
         # Make links
-	template.write('[links]\n')
+        template.write('[links]\n')
         for link in self.links.values():
              ( src, dst ) = link
              srcName, dstName = src[ 'text' ], dst[ 'text' ]
              template.write(srcName + ':' + dstName + '\n')
 
-	template.close()   
+        template.close()
+
+    def addNode( self, node, nodeNum, x, y, name=None):
+        "Add a new node to our canvas."
+        if 'Switch' == node:
+            self.switchCount += 1
+        if 'Host' == node:
+            self.hostCount += 1
+        if 'Controller' == node:
+            self.controllerCount += 1
+        if name is None:
+            name = self.nodePrefixes[ node ] + nodeNum
+        self.addNamedNode(node, name, x, y)
+
+    def addNamedNode( self, node, name, x, y):
+        "Add a new node to our canvas."
+        c = self.canvas
+        icon = self.nodeIcon( node, name )
+        item = self.canvas.create_window( x, y, anchor='c', window=icon,
+                                          tags=node )
+        self.widgetToItem[ icon ] = item
+        self.itemToWidget[ item ] = icon
+        icon.links = {}
+
+    def convertJsonUnicode(self, input):
+        "Some part of Mininet don't like Unicode"
+        if isinstance(input, dict):
+            return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
+        elif isinstance(input, list):
+            return [self.convertJsonUnicode(element) for element in input]
+        elif isinstance(input, unicode):
+            return input.encode('utf-8')
+        else:
+            return input
+
+    def loadTopology( self ):
+        "Load command."
+        c = self.canvas
+
+        myFormats = [
+            ('Mininet Topology','*.mn'),
+            ('All Files','*'),
+        ]
+        f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
+        if f == None:
+            return
+        self.newTopology()
+        loadedTopology = self.convertJsonUnicode(json.load(f))
+
+        # Load application preferences
+        if 'application' in loadedTopology:
+            self.appPrefs = dict(self.appPrefs.items() + loadedTopology['application'].items())
+            if "ovsOf10" not in self.appPrefs["openFlowVersions"]:
+                self.appPrefs["openFlowVersions"]["ovsOf10"] = '0'
+            if "ovsOf11" not in self.appPrefs["openFlowVersions"]:
+                self.appPrefs["openFlowVersions"]["ovsOf11"] = '0'
+            if "ovsOf12" not in self.appPrefs["openFlowVersions"]:
+                self.appPrefs["openFlowVersions"]["ovsOf12"] = '0'
+            if "ovsOf13" not in self.appPrefs["openFlowVersions"]:
+                self.appPrefs["openFlowVersions"]["ovsOf13"] = '0'
+            if "sflow" not in self.appPrefs:
+                self.appPrefs["sflow"] = self.sflowDefaults
+            if "netflow" not in self.appPrefs:
+                self.appPrefs["netflow"] = self.nflowDefaults
+
+        # Load controllers
+        if ('controllers' in loadedTopology):
+            if (loadedTopology['version'] == '1'):
+                # This is old location of controller info
+                hostname = 'c0'
+                self.controllers = {}
+                self.controllers[hostname] = loadedTopology['controllers']['c0']
+                self.controllers[hostname]['hostname'] = hostname
+                #elf.addNode('Controller', 0, float(30), float(30), name=hostname)
+                icon = self.findWidgetByName(hostname)
+                #con.bind('<Button-3>', self.do_controllerPopup )
+            else:
+                controllers = loadedTopology['controllers']
+                for controller in controllers:
+                    hostname = controller['opts']['hostname']
+                    x = controller['x']
+                    y = controller['y']
+                    self.addNode('Controller', 0, float(x), float(y), name=hostname)
+                    self.controllers[hostname] = controller['opts']
+                    icon = self.findWidgetByName(hostname)
+                    #con.bind('<Button-3>', self.do_controllerPopup )
+
+
+        # Load hosts
+        hosts = loadedTopology['hosts']
+        for host in hosts:
+            nodeNum = host['number']
+            hostname = 'h'+nodeNum
+            if 'hostname' in host['opts']:
+                hostname = host['opts']['hostname']
+            else:
+                host['opts']['hostname'] = hostname
+            if 'nodeNum' not in host['opts']:
+                host['opts']['nodeNum'] = int(nodeNum)
+            x = host['x']
+            y = host['y']
+            self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
+
+            # Fix JSON converting tuple to list when saving
+            if 'privateDirectory' in host['opts']:
+                newDirList = []
+                for privateDir in host['opts']['privateDirectory']:
+                    if isinstance( privateDir, list ):
+                        newDirList.append((privateDir[0],privateDir[1]))
+                    else:
+                        newDirList.append(privateDir)
+                host['opts']['privateDirectory'] = newDirList
+            self.hostOpts[hostname] = host['opts']
+            icon = self.findWidgetByName(hostname)
+            icon.bind('<Button-3>', self.do_hostPopup )
+
+        # Load switches
+        switches = loadedTopology['switches']
+        for switch in switches:
+            nodeNum = switch['number']
+            hostname = 's'+nodeNum
+            if 'controllers' not in switch['opts']:
+                switch['opts']['controllers'] = []
+            if 'switchType' not in switch['opts']:
+                switch['opts']['switchType'] = 'default'
+            if 'hostname' in switch['opts']:
+                hostname = switch['opts']['hostname']
+            else:
+                switch['opts']['hostname'] = hostname
+            if 'nodeNum' not in switch['opts']:
+                switch['opts']['nodeNum'] = int(nodeNum)
+            x = switch['x']
+            y = switch['y']
+            if switch['opts']['switchType'] == "legacyRouter":
+                self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
+                icon = self.findWidgetByName(hostname)
+                icon.bind('<Button-3>', self.do_legacyRouterPopup )
+
+        # Load links
+        links = loadedTopology['links']
+        for link in links:
+            srcNode = link['src']
+            src = self.findWidgetByName(srcNode)
+            sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
+
+            destNode = link['dest']
+            dest = self.findWidgetByName(destNode)
+            dx, dy = self.canvas.coords( self.widgetToItem[ dest]  )
+
+            self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
+                                             fill='blue', tag='link' )
+            c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
+            self.addLink( src, dest, linkopts=link['opts'] )
+            self.createDataLinkBindings()
+            self.link = self.linkWidget = None
+
+        f.close
+
+    def findWidgetByName( self, name ):
+        for widget in self.widgetToItem:
+            if name ==  widget[ 'text' ]:
+                return widget
+
+    def newTopology( self ):
+        "New command."
+        for widget in self.widgetToItem.keys():
+            self.deleteItem( self.widgetToItem[ widget ] )
+        self.hostCount = 0
+        self.switchCount = 0
+        self.links = {}
+        self.hostOpts = {}
+        self.switchOpts = {}
+        self.appPrefs["ipBase"]= self.defaultIpBase
+
+    def saveTopology( self ):
+        "Save command."
+        myFormats = [
+            ('Mininet Topology','*.mn'),
+            ('All Files','*'),
+        ]
+
+        savingDictionary = {}
+        fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
+        if len(fileName ) > 0:
+            # Save Application preferences
+            savingDictionary['version'] = '2'
+
+            # Save Switches and Hosts
+            hostsToSave = []
+            switchesToSave = []
+            controllersToSave = []
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
+                if 'LegacyRouter' in tags:
+                    nodeNum = self.switchOpts[name]['nodeNum']
+                    nodeToSave = {'number':str(nodeNum),
+                                  'x':str(x1),
+                                  'y':str(y1),
+                                  'opts':self.switchOpts[name] }
+                    switchesToSave.append(nodeToSave)
+                elif 'Host' in tags:
+                    nodeNum = self.hostOpts[name]['nodeNum']
+                    nodeToSave = {'number':str(nodeNum),
+                                  'x':str(x1),
+                                  'y':str(y1),
+                                  'opts':self.hostOpts[name] }
+                    hostsToSave.append(nodeToSave)
+                else:
+                    raise Exception( "Cannot create mystery node: " + name )
+            savingDictionary['hosts'] = hostsToSave
+            savingDictionary['switches'] = switchesToSave
+            savingDictionary['controllers'] = controllersToSave
+
+            # Save Links
+            linksToSave = []
+            for link in self.links.values():
+                src = link['src']
+                dst = link['dest']
+                linkopts = link['linkOpts']
+
+                srcName, dstName = src[ 'text' ], dst[ 'text' ]
+                linkToSave = {'src':srcName,
+                              'dest':dstName,
+                              'opts':linkopts}
+                if link['type'] == 'data':
+                    linksToSave.append(linkToSave)
+            savingDictionary['links'] = linksToSave
+
+            # Save Application preferences
+            savingDictionary['application'] = self.appPrefs
+
+            try:
+                f = open(fileName, 'wb')
+                f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
+            except Exception as er:
+                print er
+            finally:
+                f.close()
+
+    def exportScript( self ):
+        "Export command."
+        myFormats = [
+            ('Mininet Custom Topology','*.py'),
+            ('All Files','*'),
+        ]
+
+        fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
+        if len(fileName ) > 0:
+            #print "Now saving under %s" % fileName
+            f = open(fileName, 'wb')
+
+            f.write("#!/usr/bin/python\n")
+            f.write("\n")
+            f.write("from mininet.net import Mininet\n")
+            f.write("from mininet.node import Controller, RemoteController, OVSController\n")
+            f.write("from mininet.node import CPULimitedHost, Host, Node\n")
+            f.write("from mininet.node import OVSKernelSwitch, UserSwitch\n")
+            if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
+                f.write("from mininet.node import IVSSwitch\n")
+            f.write("from mininet.cli import CLI\n")
+            f.write("from mininet.log import setLogLevel, info\n")
+            f.write("from mininet.link import TCLink, Intf\n")
+            f.write("from subprocess import call\n")
+
+            inBandCtrl = False
+            hasLegacySwitch = False
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+
+                if 'Controller' in tags:
+                    opts = self.controllers[name]
+                    controllerType = opts['controllerType']
+                    if controllerType == 'inband':
+                        inBandCtrl = True
+
+            if inBandCtrl == True:
+                f.write("\n")
+                f.write("class InbandController( RemoteController ):\n")
+                f.write("\n")
+                f.write("    def checkListening( self ):\n")
+                f.write("        \"Overridden to do nothing.\"\n")
+                f.write("        return\n")
+
+            f.write("\n")
+            f.write("def myNetwork():\n")
+            f.write("\n")
+            f.write("    net = Mininet( topo=None,\n")
+            if len(self.appPrefs['dpctl']) > 0:
+                f.write("                   listenPort="+self.appPrefs['dpctl']+",\n")
+            f.write("                   build=False,\n")
+            f.write("                   ipBase='"+self.appPrefs['ipBase']+"')\n")
+            f.write("\n")
+            f.write("    info( '*** Adding controller\\n' )\n")
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+    
+                if 'Controller' in tags:
+                    opts = self.controllers[name]
+                    controllerType = opts['controllerType']
+                    if 'controllerProtocol' in opts:
+                        controllerProtocol = opts['controllerProtocol']
+                    else:
+                        controllerProtocol = 'tcp'
+                    controllerIP = opts['remoteIP']
+                    controllerPort = opts['remotePort']
+
+    
+                    f.write("    "+name+"=net.addController(name='"+name+"',\n")
+        
+                    if controllerType == 'remote':
+                        f.write("                      controller=RemoteController,\n")
+                        f.write("                      ip='"+controllerIP+"',\n")
+                    elif controllerType == 'inband':
+                        f.write("                      controller=InbandController,\n")
+                        f.write("                      ip='"+controllerIP+"',\n")
+                    elif controllerType == 'ovsc':
+                        f.write("                      controller=OVSController,\n")
+                    else:
+                        f.write("                      controller=Controller,\n")
+        
+                    f.write("                      protocol='"+controllerProtocol+"',\n")
+                    f.write("                      port="+str(controllerPort)+")\n")
+                    f.write("\n")
+
+            # Save Switches and Hosts
+            f.write("    info( '*** Add switches\\n')\n")
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'LegacyRouter' in tags:
+                    f.write("    "+name+" = net.addHost('"+name+"', cls=Node, ip='0.0.0.0')\n")
+                    f.write("    "+name+".cmd('sysctl -w net.ipv4.ip_forward=1')\n")
+                if 'LegacySwitch' in tags:
+                    f.write("    "+name+" = net.addSwitch('"+name+"', cls=OVSKernelSwitch, failMode='standalone')\n")
+                if 'Switch' in tags:
+                    opts = self.switchOpts[name]
+                    nodeNum = opts['nodeNum']
+                    f.write("    "+name+" = net.addSwitch('"+name+"'")
+                    if opts['switchType'] == 'default':
+                        if self.appPrefs['switchType'] == 'ivs':
+                            f.write(", cls=IVSSwitch")
+                        elif self.appPrefs['switchType'] == 'user':
+                            f.write(", cls=UserSwitch")
+                        elif self.appPrefs['switchType'] == 'userns':
+                            f.write(", cls=UserSwitch, inNamespace=True")
+                        else:
+                            f.write(", cls=OVSKernelSwitch")
+                    elif opts['switchType'] == 'ivs':
+                        f.write(", cls=IVSSwitch")
+                    elif opts['switchType'] == 'user':
+                        f.write(", cls=UserSwitch")
+                    elif opts['switchType'] == 'userns':
+                        f.write(", cls=UserSwitch, inNamespace=True")
+                    else:
+                        f.write(", cls=OVSKernelSwitch")
+                    if 'dpctl' in opts:
+                        f.write(", listenPort="+opts['dpctl'])
+                    if 'dpid' in opts:
+                        f.write(", dpid='"+opts['dpid']+"'")
+                    f.write(")\n")
+                    if ('externalInterfaces' in opts):
+                        for extInterface in opts['externalInterfaces']:
+                            f.write("    Intf( '"+extInterface+"', node="+name+" )\n")
+
+            f.write("\n")
+            f.write("    info( '*** Add hosts\\n')\n")
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'Host' in tags:
+                    opts = self.hostOpts[name]
+                    ip = None
+                    defaultRoute = None
+                    if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
+                        defaultRoute = "'via "+opts['defaultRoute']+"'"
+                    else:
+                        defaultRoute = 'None'
+                    if 'ip' in opts and len(opts['ip']) > 0:
+                        ip = opts['ip']
+                    else:
+                        nodeNum = self.hostOpts[name]['nodeNum']
+                        ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
+                        ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)
+
+                    if 'cores' in opts or 'cpu' in opts:
+                        f.write("    "+name+" = net.addHost('"+name+"', cls=CPULimitedHost, ip='"+ip+"', defaultRoute="+defaultRoute+")\n")
+                        if 'cores' in opts:
+                            f.write("    "+name+".setCPUs(cores='"+opts['cores']+"')\n")
+                        if 'cpu' in opts:
+                            f.write("    "+name+".setCPUFrac(f="+str(opts['cpu'])+", sched='"+opts['sched']+"')\n")
+                    else:
+                        f.write("    "+name+" = net.addHost('"+name+"', cls=Host, ip='"+ip+"', defaultRoute="+defaultRoute+")\n")
+                    if ('externalInterfaces' in opts):
+                        for extInterface in opts['externalInterfaces']:
+                            f.write("    Intf( '"+extInterface+"', node="+name+" )\n")
+            f.write("\n")
+
+            # Save Links
+            f.write("    info( '*** Add links\\n')\n")
+            for key,linkDetail in self.links.iteritems():
+              tags = self.canvas.gettags(key)
+              if 'data' in tags:
+                optsExist = False
+                src = linkDetail['src']
+                dst = linkDetail['dest']
+                linkopts = linkDetail['linkOpts']
+                srcName, dstName = src[ 'text' ], dst[ 'text' ]
+                bw = ''
+                delay = ''
+                loss = ''
+                max_queue_size = ''
+                linkOpts = "{"
+                if 'bw' in linkopts:
+                    bw =  linkopts['bw']
+                    linkOpts = linkOpts + "'bw':"+str(bw)
+                    optsExist = True
+                if 'delay' in linkopts:
+                    delay =  linkopts['delay']
+                    if optsExist:
+                        linkOpts = linkOpts + ","
+                    linkOpts = linkOpts + "'delay':'"+linkopts['delay']+"'"
+                    optsExist = True
+                if 'loss' in linkopts:
+                    if optsExist:
+                        linkOpts = linkOpts + ","
+                    linkOpts = linkOpts + "'loss':"+str(linkopts['loss'])
+                    optsExist = True
+                if 'max_queue_size' in linkopts:
+                    if optsExist:
+                        linkOpts = linkOpts + ","
+                    linkOpts = linkOpts + "'max_queue_size':"+str(linkopts['max_queue_size'])
+                    optsExist = True
+                if 'jitter' in linkopts:
+                    if optsExist:
+                        linkOpts = linkOpts + ","
+                    linkOpts = linkOpts + "'jitter':'"+linkopts['jitter']+"'"
+                    optsExist = True
+                if 'speedup' in linkopts:
+                    if optsExist:
+                        linkOpts = linkOpts + ","
+                    linkOpts = linkOpts + "'speedup':"+str(linkopts['speedup'])
+                    optsExist = True
+
+                linkOpts = linkOpts + "}"
+                if optsExist:
+                    f.write("    "+srcName+dstName+" = "+linkOpts+"\n")
+                f.write("    net.addLink("+srcName+", "+dstName)
+                if optsExist:
+                    f.write(", cls=TCLink , **"+srcName+dstName)
+                f.write(")\n")
+
+            f.write("\n")
+            f.write("    info( '*** Starting network\\n')\n")
+            f.write("    net.build()\n")
+
+            f.write("    info( '*** Starting controllers\\n')\n")
+            f.write("    for controller in net.controllers:\n")
+            f.write("        controller.start()\n")
+            f.write("\n")
+
+            f.write("    info( '*** Starting switches\\n')\n")
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'Switch' in tags or 'LegacySwitch' in tags:
+                    opts = self.switchOpts[name]
+                    ctrlList = ",".join(opts['controllers'])
+                    f.write("    net.get('"+name+"').start(["+ctrlList+"])\n")
+
+            f.write("\n")
+
+            f.write("    info( '*** Post configure switches and hosts\\n')\n")
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'Switch' in tags:
+                    opts = self.switchOpts[name]
+                    if opts['switchType'] == 'default':
+                        if self.appPrefs['switchType'] == 'user':
+                            if ('switchIP' in opts):
+                                if (len(opts['switchIP'])>0):
+                                    f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
+                        elif self.appPrefs['switchType'] == 'userns':
+                            if ('switchIP' in opts):
+                                if (len(opts['switchIP'])>0):
+                                    f.write("    "+name+".cmd('ifconfig lo "+opts['switchIP']+"')\n")
+                        elif self.appPrefs['switchType'] == 'ovs':
+                            if ('switchIP' in opts):
+                                if (len(opts['switchIP'])>0):
+                                    f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
+                    elif opts['switchType'] == 'user':
+                        if ('switchIP' in opts):
+                            if (len(opts['switchIP'])>0):
+                                f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
+                    elif opts['switchType'] == 'userns':
+                        if ('switchIP' in opts):
+                            if (len(opts['switchIP'])>0):
+                                f.write("    "+name+".cmd('ifconfig lo "+opts['switchIP']+"')\n")
+                    elif opts['switchType'] == 'ovs':
+                        if ('switchIP' in opts):
+                            if (len(opts['switchIP'])>0):
+                                f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'Host' in tags:
+                    opts = self.hostOpts[name]
+                    # Attach vlan interfaces
+                    if ('vlanInterfaces' in opts):
+                        for vlanInterface in opts['vlanInterfaces']:
+                            f.write("    "+name+".cmd('vconfig add "+name+"-eth0 "+vlanInterface[1]+"')\n")
+                            f.write("    "+name+".cmd('ifconfig "+name+"-eth0."+vlanInterface[1]+" "+vlanInterface[0]+"')\n")
+                    # Run User Defined Start Command
+                    if ('startCommand' in opts):
+                        f.write("    "+name+".cmdPrint('"+opts['startCommand']+"')\n")
+                if 'Switch' in tags:
+                    opts = self.switchOpts[name]
+                    # Run User Defined Start Command
+                    if ('startCommand' in opts):
+                        f.write("    "+name+".cmdPrint('"+opts['startCommand']+"')\n")
+
+            # Configure NetFlow
+            nflowValues = self.appPrefs['netflow']
+            if len(nflowValues['nflowTarget']) > 0:
+                nflowEnabled = False
+                nflowSwitches = ''
+                for widget in self.widgetToItem:
+                    name = widget[ 'text' ]
+                    tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+    
+                    if 'Switch' in tags:
+                        opts = self.switchOpts[name]
+                        if 'netflow' in opts:
+                            if opts['netflow'] == '1':
+                                nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
+                                nflowEnabled=True
+                if nflowEnabled:
+                    nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '+ 'target=\\\"'+nflowValues['nflowTarget']+'\\\" '+ 'active-timeout='+nflowValues['nflowTimeout']
+                    if nflowValues['nflowAddId'] == '1':
+                        nflowCmd = nflowCmd + ' add_id_to_interface=true'
+                    else:
+                        nflowCmd = nflowCmd + ' add_id_to_interface=false'
+                    f.write("    \n")
+                    f.write("    call('"+nflowCmd+nflowSwitches+"', shell=True)\n")
+
+            # Configure sFlow
+            sflowValues = self.appPrefs['sflow']
+            if len(sflowValues['sflowTarget']) > 0:
+                sflowEnabled = False
+                sflowSwitches = ''
+                for widget in self.widgetToItem:
+                    name = widget[ 'text' ]
+                    tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+    
+                    if 'Switch' in tags:
+                        opts = self.switchOpts[name]
+                        if 'sflow' in opts:
+                            if opts['sflow'] == '1':
+                                sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
+                                sflowEnabled=True
+                if sflowEnabled:
+                    sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
+                    f.write("    \n")
+                    f.write("    call('"+sflowCmd+sflowSwitches+"', shell=True)\n")
+
+            f.write("\n")
+            f.write("    CLI(net)\n")
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'Host' in tags:
+                    opts = self.hostOpts[name]
+                    # Run User Defined Stop Command
+                    if ('stopCommand' in opts):
+                        f.write("    "+name+".cmdPrint('"+opts['stopCommand']+"')\n")
+                if 'Switch' in tags:
+                    opts = self.switchOpts[name]
+                    # Run User Defined Stop Command
+                    if ('stopCommand' in opts):
+                        f.write("    "+name+".cmdPrint('"+opts['stopCommand']+"')\n")
+
+            f.write("    net.stop()\n")
+            f.write("\n")
+            f.write("if __name__ == '__main__':\n")
+            f.write("    setLogLevel( 'info' )\n")
+            f.write("    myNetwork()\n")
+            f.write("\n")
+
+
+            f.close()
 
 
     # Generic canvas handler
@@ -369,6 +1571,16 @@
             self.deleteItem( self.selection )
         self.selectItem( None )
 
+    def clearPopups(self):
+	print 'Entrou funcao clear_popups'
+
+    	if isHostPopup == True:
+		print 'Hostpopup = true'
+    		self.hostPopup.unpost
+		isHostPopup = False
+	#if isRouterPopup == True
+	#if isLinkPopup == True
+
     def nodeIcon( self, node, name ):
         "Create a new node icon."
         icon = Button( self.canvas, image=self.images[ node ],
@@ -383,8 +1595,47 @@
         "Add a new node to our canvas."
         c = self.canvas
         x, y = c.canvasx( event.x ), c.canvasy( event.y )
-        self.nodeCount += 1
-        name = self.nodePrefixes[ node ] + str( self.nodeCount )
+        name = self.nodePrefixes[ node ]
+        if 'Switch' == node:
+            self.switchCount += 1
+            name = self.nodePrefixes[ node ] + str( self.switchCount )
+            self.switchOpts[name] = {}
+            self.switchOpts[name]['nodeNum']=self.switchCount
+            self.switchOpts[name]['hostname']=name
+            self.switchOpts[name]['switchType']='default'
+            self.switchOpts[name]['controllers']=[]
+        if 'LegacyRouter' == node:
+            self.switchCount += 1
+            name = self.nodePrefixes[ node ] + str( self.switchCount )
+            self.switchOpts[name] = {}
+            self.switchOpts[name]['nodeNum']=self.switchCount
+            self.switchOpts[name]['hostname']=name
+            self.switchOpts[name]['switchType']='legacyRouter'
+        if 'LegacySwitch' == node:
+            self.switchCount += 1
+            name = self.nodePrefixes[ node ] + str( self.switchCount )
+            self.switchOpts[name] = {}
+            self.switchOpts[name]['nodeNum']=self.switchCount
+            self.switchOpts[name]['hostname']=name
+            self.switchOpts[name]['switchType']='legacySwitch'
+            self.switchOpts[name]['controllers']=[]
+        if 'Host' == node:
+            self.hostCount += 1
+            name = self.nodePrefixes[ node ] + str( self.hostCount )
+            self.hostOpts[name] = {'sched':'host'}
+            self.hostOpts[name]['nodeNum']=self.hostCount
+            self.hostOpts[name]['hostname']=name
+        if 'Controller' == node:
+            name = self.nodePrefixes[ node ] + str( self.controllerCount )
+            ctrlr = { 'controllerType': 'ref',
+                      'hostname': name,
+                      'controllerProtocol': 'tcp',
+                      'remoteIP': '127.0.0.1',
+                      'remotePort': 6633}
+            self.controllers[name] = ctrlr
+            # We want to start controller count at 0
+            self.controllerCount += 1
+
         icon = self.nodeIcon( node, name )
         item = self.canvas.create_window( x, y, anchor='c', window=icon,
                                           tags=node )
@@ -392,16 +1643,32 @@
         self.itemToWidget[ item ] = icon
         self.selectItem( item )
         icon.links = {}
+        if 'LegacyRouter' == node:
+            icon.bind('<Button-3>', self.do_legacyRouterPopup )
+        if 'Host' == node:
+            icon.bind('<Button-3>', self.do_hostPopup )
+
+    def clickController( self, event ):
+        "Add a new Controller to our canvas."
+        self.newNode( 'Controller', event )
 
     def clickHost( self, event ):
         "Add a new host to our canvas."
         self.newNode( 'Host', event )
 
+    def clickLegacyRouter( self, event ):
+        "Add a new switch to our canvas."
+        self.newNode( 'LegacyRouter', event )
+
+    def clickLegacySwitch( self, event ):
+        "Add a new switch to our canvas."
+        self.newNode( 'LegacySwitch', event )
+
     def clickSwitch( self, event ):
         "Add a new switch to our canvas."
         self.newNode( 'Switch', event )
 
-    def dragLink( self, event ):
+    def dragNetLink( self, event ):
         "Drag a link's endpoint to another node."
         if self.link is None:
             return
@@ -411,7 +1678,7 @@
         c = self.canvas
         c.coords( self.link, self.linkx, self.linky, x, y )
 
-    def releaseLink( self, _event ):
+    def releaseNetLink( self, _event ):
         "Give up on the current link."
         if self.link is not None:
             self.canvas.delete( self.link )
@@ -426,8 +1693,7 @@
             '<B1-Motion>': self.dragNode,
             '<ButtonRelease-1>': self.releaseNode,
             '<Enter>': self.enterNode,
-            '<Leave>': self.leaveNode,
-            '<Double-ButtonPress-1>': self.xterm
+            '<Leave>': self.leaveNode
         }
         l = Label()  # lightweight-ish owner for bindings
         for event, binding in bindings.items():
@@ -449,7 +1715,7 @@
 
     def clickNode( self, event ):
         "Node click handler."
-        if self.active is 'Link':
+        if self.active is 'NetLink':
             self.startLink( event )
         else:
             self.selectNode( event )
@@ -457,14 +1723,14 @@
 
     def dragNode( self, event ):
         "Node drag handler."
-        if self.active is 'Link':
-            self.dragLink( event )
+        if self.active is 'NetLink':
+            self.dragNetLink( event )
         else:
             self.dragNodeAround( event )
 
     def releaseNode( self, event ):
         "Node release handler."
-        if self.active is 'Link':
+        if self.active is 'NetLink':
             self.finishLink( event )
 
     # Specific node handlers
@@ -491,21 +1757,10 @@
             item = self.widgetToItem[ dest ]
             x1, y1 = c.coords( item )
             c.coords( link, x, y, x1, y1 )
+        self.updateScrollRegion()
 
-    def startLink( self, event ):
-        "Start a new link."
-        if event.widget not in self.widgetToItem:
-            # Didn't click on a node
-            return
-        w = event.widget
-        item = self.widgetToItem[ w ]
-        x, y = self.canvas.coords( item )
-        self.link = self.canvas.create_line( x, y, x, y, width=4,
-                                             fill='blue', tag='link' )
-        self.linkx, self.linky = x, y
-        self.linkWidget = w
-        self.linkItem = item
-
+    def createControlLinkBindings( self ):
+        "Create a set of bindings for nodes."
         # Link bindings
         # Selection still needs a bit of work overall
         # Callbacks ignore event
@@ -516,17 +1771,59 @@
 
         def highlight( _event, link=self.link ):
             "Highlight item on mouse entry."
-            # self.selectItem( link )
+            self.selectItem( link )
+            self.canvas.itemconfig( link, fill='green' )
+
+        def unhighlight( _event, link=self.link ):
+            "Unhighlight item on mouse exit."
+            self.canvas.itemconfig( link, fill='red' )
+            #self.selectItem( None )
+
+        self.canvas.tag_bind( self.link, '<Enter>', highlight )
+        self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
+        self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
+
+    def createDataLinkBindings( self ):
+        "Create a set of bindings for nodes."
+        # Link bindings
+        # Selection still needs a bit of work overall
+        # Callbacks ignore event
+
+        def select( _event, link=self.link ):
+            "Select item on mouse entry."
+            self.selectItem( link )
+
+        def highlight( _event, link=self.link ):
+            "Highlight item on mouse entry."
+            self.selectItem( link )
             self.canvas.itemconfig( link, fill='green' )
 
         def unhighlight( _event, link=self.link ):
             "Unhighlight item on mouse exit."
             self.canvas.itemconfig( link, fill='blue' )
-            # self.selectItem( None )
+            #self.selectItem( None )
 
         self.canvas.tag_bind( self.link, '<Enter>', highlight )
         self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
         self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
+        self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
+
+
+    def startLink( self, event ):
+        "Start a new link."
+        if event.widget not in self.widgetToItem:
+            # Didn't click on a node
+            return
+
+        w = event.widget
+        item = self.widgetToItem[ w ]
+        x, y = self.canvas.coords( item )
+        self.link = self.canvas.create_line( x, y, x, y, width=4,
+                                             fill='blue', tag='link' )
+        self.linkx, self.linky = x, y
+        self.linkWidget = w
+        self.linkItem = item
+
 
     def finishLink( self, event ):
         "Finish creating a link"
@@ -540,17 +1837,48 @@
         dest = self.itemToWidget.get( target, None )
         if ( source is None or dest is None or source == dest
                 or dest in source.links or source in dest.links ):
-            self.releaseLink( event )
+            self.releaseNetLink( event )
             return
         # For now, don't allow hosts to be directly linked
-#        stags = self.canvas.gettags( self.widgetToItem[ source ] )
-#        dtags = self.canvas.gettags( target )
-#        if 'Host' in stags and 'Host' in dtags:
-#            self.releaseLink( event )
-#            return
+        stags = self.canvas.gettags( self.widgetToItem[ source ] )
+        dtags = self.canvas.gettags( target )
+        if (('Host' in stags and 'Host' in dtags) or
+           ('Controller' in dtags and 'LegacyRouter' in stags) or
+           ('Controller' in stags and 'LegacyRouter' in dtags) or
+           ('Controller' in dtags and 'LegacySwitch' in stags) or
+           ('Controller' in stags and 'LegacySwitch' in dtags) or
+           ('Controller' in dtags and 'Host' in stags) or
+           ('Controller' in stags and 'Host' in dtags) or
+           ('Controller' in stags and 'Controller' in dtags)):
+            self.releaseNetLink( event )
+            return
+
+        # Set link type
+        linkType='data'
+        if 'Controller' in stags or 'Controller' in dtags:
+            linkType='control'
+            c.itemconfig(self.link, dash=(6, 4, 2, 4), fill='red')
+            self.createControlLinkBindings()
+        else:
+            linkType='data'
+            self.createDataLinkBindings()
+        c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
+
         x, y = c.coords( target )
         c.coords( self.link, self.linkx, self.linky, x, y )
-        self.addLink( source, dest )
+        self.addLink( source, dest, linktype=linkType )
+        if linkType == 'control':
+            controllerName = ''
+            switchName = ''
+            if 'Controller' in stags:
+                controllerName = source[ 'text' ]
+                switchName = dest[ 'text' ]
+            else:
+                controllerName = dest[ 'text' ]
+                switchName = source[ 'text' ]
+
+            self.switchOpts[switchName]['controllers'].append(controllerName)
+
         # We're done
         self.link = self.linkWidget = None
 
@@ -563,18 +1891,23 @@
             bg = 'white'
             about = Toplevel( bg='white' )
             about.title( 'About' )
-            info = self.appName + ': a simple network editor for Mini-CCNx - based on Miniedit '
-            warning = 'Development version - not entirely functional!'
-	    author = 'Carlos Cabral, Jan 2013'
-            author2 = 'Miniedit by Bob Lantz <rlantz@cs>, April 2010'
+            info = self.appName + ': a simple network editor for MiniNet'
+            version = 'MiniEdit '+MINIEDIT_VERSION
+            author = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
+            enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
+            www = 'http://gregorygee.wordpress.com/category/miniedit/'
             line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
-            line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
+            line2 = Label( about, text=version, font='Helvetica 9', bg=bg )
             line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
-	    line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
+            line4 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
+            line5 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
+            line5.insert(0, www)
+            line5.configure(state='readonly')
             line1.pack( padx=20, pady=10 )
             line2.pack(pady=10 )
             line3.pack(pady=10 )
-	    line4.pack(pady=10 )
+            line4.pack(pady=10 )
+            line5.pack(pady=10 )
             hide = ( lambda about=about: about.withdraw() )
             self.aboutBox = about
             # Hide on close rather than destroying window
@@ -585,79 +1918,658 @@
     def createToolImages( self ):
         "Create toolbar (and icon) images."
 
+    def checkIntf( self, intf ):
+        "Make sure intf exists and is not configured."
+        if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
+            showerror(title="Error",
+                      message='External interface ' +intf + ' does not exist! Skipping.')
+            return False
+        ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
+        if ips:
+            showerror(title="Error",
+                      message= intf + ' has an IP address and is probably in use! Skipping.' )
+            return False
+        return True
+
+    def hostDetails( self, _ignore=None ):
+        if ( self.selection is None or
+             self.net is not None or
+             self.selection not in self.itemToWidget ):
+            return
+        widget = self.itemToWidget[ self.selection ]
+        name = widget[ 'text' ]
+        tags = self.canvas.gettags( self.selection )
+        if 'Host' not in tags:
+            return
+
+        prefDefaults = self.hostOpts[name]
+        hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults)
+        self.master.wait_window(hostBox.top)
+        if hostBox.result:
+            newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
+            newHostOpts['sched'] = hostBox.result['sched']
+            if len(hostBox.result['startCommand']) > 0:
+                newHostOpts['startCommand'] = hostBox.result['startCommand']
+            if len(hostBox.result['stopCommand']) > 0:
+                newHostOpts['stopCommand'] = hostBox.result['stopCommand']
+            if len(hostBox.result['cpu']) > 0:
+                newHostOpts['cpu'] = float(hostBox.result['cpu'])
+            if len(hostBox.result['cores']) > 0:
+                newHostOpts['cores'] = hostBox.result['cores']
+            if len(hostBox.result['hostname']) > 0:
+                newHostOpts['hostname'] = hostBox.result['hostname']
+                name = hostBox.result['hostname']
+                widget[ 'text' ] = name
+            if len(hostBox.result['defaultRoute']) > 0:
+                newHostOpts['defaultRoute'] = hostBox.result['defaultRoute']
+            if len(hostBox.result['ip']) > 0:
+                newHostOpts['ip'] = hostBox.result['ip']
+            if len(hostBox.result['externalInterfaces']) > 0:
+                newHostOpts['externalInterfaces'] = hostBox.result['externalInterfaces']
+            if len(hostBox.result['vlanInterfaces']) > 0:
+                newHostOpts['vlanInterfaces'] = hostBox.result['vlanInterfaces']
+            if len(hostBox.result['privateDirectory']) > 0:
+                newHostOpts['privateDirectory'] = hostBox.result['privateDirectory']
+            self.hostOpts[name] = newHostOpts
+            print 'New host details for ' + name + ' = ' + str(newHostOpts)
+
+    def switchDetails( self, _ignore=None ):
+        if ( self.selection is None or
+             self.net is not None or
+             self.selection not in self.itemToWidget ):
+            return
+        widget = self.itemToWidget[ self.selection ]
+        name = widget[ 'text' ]
+        tags = self.canvas.gettags( self.selection )
+        if 'Switch' not in tags:
+            return
+
+        prefDefaults = self.switchOpts[name]
+        switchBox = SwitchDialog(self, title='Switch Details', prefDefaults=prefDefaults)
+        self.master.wait_window(switchBox.top)
+        if switchBox.result:
+            newSwitchOpts = {'nodeNum':self.switchOpts[name]['nodeNum']}
+            newSwitchOpts['switchType'] = switchBox.result['switchType']
+            newSwitchOpts['controllers'] = self.switchOpts[name]['controllers']
+            if len(switchBox.result['startCommand']) > 0:
+                newSwitchOpts['startCommand'] = switchBox.result['startCommand']
+            if len(switchBox.result['stopCommand']) > 0:
+                newSwitchOpts['stopCommand'] = switchBox.result['stopCommand']
+            if len(switchBox.result['dpctl']) > 0:
+                newSwitchOpts['dpctl'] = switchBox.result['dpctl']
+            if len(switchBox.result['dpid']) > 0:
+                newSwitchOpts['dpid'] = switchBox.result['dpid']
+            if len(switchBox.result['hostname']) > 0:
+                newSwitchOpts['hostname'] = switchBox.result['hostname']
+                name = switchBox.result['hostname']
+                widget[ 'text' ] = name
+            if len(switchBox.result['externalInterfaces']) > 0:
+                newSwitchOpts['externalInterfaces'] = switchBox.result['externalInterfaces']
+            newSwitchOpts['switchIP'] = switchBox.result['switchIP']
+            newSwitchOpts['sflow'] = switchBox.result['sflow']
+            newSwitchOpts['netflow'] = switchBox.result['netflow']
+            self.switchOpts[name] = newSwitchOpts
+            print 'New switch details for ' + name + ' = ' + str(newSwitchOpts)
+
+    def linkUp( self ):
+        if ( self.selection is None or
+             self.net is None):
+            return
+        link = self.selection
+        linkDetail =  self.links[link]
+        src = linkDetail['src']
+        dst = linkDetail['dest']
+        srcName, dstName = src[ 'text' ], dst[ 'text' ]
+        self.net.configLinkStatus(srcName, dstName, 'up')
+        self.canvas.itemconfig(link, dash=())
+
+    def linkDown( self ):
+        if ( self.selection is None or
+             self.net is None):
+            return
+        link = self.selection
+        linkDetail =  self.links[link]
+        src = linkDetail['src']
+        dst = linkDetail['dest']
+        srcName, dstName = src[ 'text' ], dst[ 'text' ]
+        self.net.configLinkStatus(srcName, dstName, 'down')
+        self.canvas.itemconfig(link, dash=(4, 4))
+
+    def linkDetails( self, _ignore=None ):
+        if ( self.selection is None or
+             self.net is not None):
+            return
+        link = self.selection
+
+        linkDetail =  self.links[link]
+        src = linkDetail['src']
+        dest = linkDetail['dest']
+        linkopts = linkDetail['linkOpts']
+        linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
+        if linkBox.result is not None:
+            linkDetail['linkOpts'] = linkBox.result
+            print 'New link details = ' + str(linkBox.result)
+
+    def prefDetails( self ):
+        prefDefaults = self.appPrefs
+        prefBox = PrefsDialog(self, title='Preferences', prefDefaults=prefDefaults)
+        print 'New Prefs = ' + str(prefBox.result)
+        if prefBox.result:
+            self.appPrefs = prefBox.result
+
+
+    def controllerDetails( self ):
+        if ( self.selection is None or
+             self.net is not None or
+             self.selection not in self.itemToWidget ):
+            return
+        widget = self.itemToWidget[ self.selection ]
+        name = widget[ 'text' ]
+        tags = self.canvas.gettags( self.selection )
+        oldName = name
+        if 'Controller' not in tags:
+            return
+
+        ctrlrBox = ControllerDialog(self, title='Controller Details', ctrlrDefaults=self.controllers[name])
+        if ctrlrBox.result:
+            #print 'Controller is ' + ctrlrBox.result[0]
+            if len(ctrlrBox.result['hostname']) > 0:
+                name = ctrlrBox.result['hostname']
+                widget[ 'text' ] = name
+            else:
+                ctrlrBox.result['hostname'] = name
+            self.controllers[name] = ctrlrBox.result
+            print 'New controller details for ' + name + ' = ' + str(self.controllers[name])
+            # Find references to controller and change name
+            if oldName != name:
+                for widget in self.widgetToItem:
+                    switchName = widget[ 'text' ]
+                    tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                    if 'Switch' in tags:
+                        switch = self.switchOpts[switchName]
+                        if oldName in switch['controllers']:
+                            switch['controllers'].remove(oldName)
+                            switch['controllers'].append(name)
+
+
+    def listBridge( self, _ignore=None ):
+        if ( self.selection is None or
+             self.net is None or
+             self.selection not in self.itemToWidget ):
+            return
+        name = self.itemToWidget[ self.selection ][ 'text' ]
+        tags = self.canvas.gettags( self.selection )
+
+        if name not in self.net.nameToNode:
+            return
+        if 'Switch' in tags or 'LegacySwitch' in tags:
+           call(["xterm -T 'Bridge Details' -sb -sl 2000 -e 'ovs-vsctl list bridge " + name + "; read -p \"Press Enter to close\"' &"], shell=True)
+
+    def ovsShow( self, _ignore=None ):
+        call(["xterm -T 'OVS Summary' -sb -sl 2000 -e 'ovs-vsctl show; read -p \"Press Enter to close\"' &"], shell=True)
+
+    def rootTerminal( self, _ignore=None ):
+        call(["xterm -T 'Root Terminal' -sb -sl 2000 &"], shell=True)
+
     # Model interface
     #
     # Ultimately we will either want to use a topo or
     # mininet object here, probably.
 
-    def addLink( self, source, dest ):
+    def addLink( self, source, dest, linktype='data', linkopts={} ):
         "Add link to model."
         source.links[ dest ] = self.link
         dest.links[ source ] = self.link
-        self.links[ self.link ] = ( source, dest )
+        self.links[ self.link ] = {'type' :linktype,
+                                   'src':source,
+                                   'dest':dest,
+                                   'linkOpts':linkopts}
 
     def deleteLink( self, link ):
         "Delete link from model."
         pair = self.links.get( link, None )
         if pair is not None:
-            source, dest = pair
+            source=pair['src']
+            dest=pair['dest']
             del source.links[ dest ]
             del dest.links[ source ]
+            stags = self.canvas.gettags( self.widgetToItem[ source ] )
+            dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
+            ltags = self.canvas.gettags( link )
+
+            if 'control' in ltags:
+                controllerName = ''
+                switchName = ''
+                if 'Controller' in stags:
+                    controllerName = source[ 'text' ]
+                    switchName = dest[ 'text' ]
+                else:
+                    controllerName = dest[ 'text' ]
+                    switchName = source[ 'text' ]
+    
+                if controllerName in self.switchOpts[switchName]['controllers']:
+                    self.switchOpts[switchName]['controllers'].remove(controllerName)
+
+
         if link is not None:
             del self.links[ link ]
 
     def deleteNode( self, item ):
         "Delete node (and its links) from model."
+
         widget = self.itemToWidget[ item ]
+        tags = self.canvas.gettags(item)
+        if 'Controller' in tags:
+            # remove from switch controller lists
+            for serachwidget in self.widgetToItem:
+                name = serachwidget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ serachwidget ] )
+                if 'Switch' in tags:
+                    if widget['text'] in self.switchOpts[name]['controllers']:
+                        self.switchOpts[name]['controllers'].remove(widget['text'])
+            
         for link in widget.links.values():
             # Delete from view and model
             self.deleteItem( link )
         del self.itemToWidget[ item ]
         del self.widgetToItem[ widget ]
 
-    def build( self ):
-        "Build network based on our topology."
-
-        net = Mininet( topo=None )
-
-        # Make controller
-        net.addController( 'c0' )
+    def buildNodes( self, net):
         # Make nodes
+        print "Getting Hosts and Switches."
         for widget in self.widgetToItem:
             name = widget[ 'text' ]
             tags = self.canvas.gettags( self.widgetToItem[ widget ] )
-            nodeNum = int( name[ 1: ] )
+            #print name+' has '+str(tags)
+
             if 'Switch' in tags:
-                net.addSwitch( name )
+                opts = self.switchOpts[name]
+                #print str(opts)
+
+                # Create the correct switch class
+                switchClass = customOvs
+                switchParms={}
+                if 'dpctl' in opts:
+                    switchParms['listenPort']=int(opts['dpctl'])
+                if 'dpid' in opts:
+                    switchParms['dpid']=opts['dpid']
+                if opts['switchType'] == 'default':
+                    if self.appPrefs['switchType'] == 'ivs':
+                        switchClass = IVSSwitch
+                    elif self.appPrefs['switchType'] == 'user':
+                        switchClass = CustomUserSwitch
+                    elif self.appPrefs['switchType'] == 'userns':
+                        switchParms['inNamespace'] = True
+                        switchClass = CustomUserSwitch
+                    else:
+                        switchClass = customOvs
+                elif opts['switchType'] == 'user':
+                    switchClass = CustomUserSwitch
+                elif opts['switchType'] == 'userns':
+                    switchClass = CustomUserSwitch
+                    switchParms['inNamespace'] = True
+                elif opts['switchType'] == 'ivs':
+                    switchClass = IVSSwitch
+                else:
+                    switchClass = customOvs
+
+                if switchClass == customOvs:
+                    # Set OpenFlow versions
+                    self.openFlowVersions = []
+                    if self.appPrefs['openFlowVersions']['ovsOf10'] == '1':
+                        self.openFlowVersions.append('OpenFlow10')
+                    if self.appPrefs['openFlowVersions']['ovsOf11'] == '1':
+                        self.openFlowVersions.append('OpenFlow11')
+                    if self.appPrefs['openFlowVersions']['ovsOf12'] == '1':
+                        self.openFlowVersions.append('OpenFlow12')
+                    if self.appPrefs['openFlowVersions']['ovsOf13'] == '1':
+                        self.openFlowVersions.append('OpenFlow13')
+                    protoList = ",".join(self.openFlowVersions)
+                    switchParms['protocols'] = protoList
+                newSwitch = net.addSwitch( name , cls=switchClass, **switchParms)
+
+                # Some post startup config
+                if switchClass == CustomUserSwitch:
+                    if ('switchIP' in opts):
+                        if (len(opts['switchIP']) > 0):
+                            newSwitch.setSwitchIP(opts['switchIP'])
+                if switchClass == customOvs:
+                    if ('switchIP' in opts):
+                        if (len(opts['switchIP']) > 0):
+                            newSwitch.setSwitchIP(opts['switchIP'])
+
+                # Attach external interfaces
+                if ('externalInterfaces' in opts):
+                    for extInterface in opts['externalInterfaces']:
+                        if self.checkIntf(extInterface):
+                           Intf( extInterface, node=newSwitch )
+
+            elif 'LegacySwitch' in tags:
+                newSwitch = net.addSwitch( name , cls=LegacySwitch)
+            elif 'LegacyRouter' in tags:
+                newSwitch = net.addHost( name , cls=LegacyRouter)
             elif 'Host' in tags:
-                net.addHost( name, ip=ipStr( nodeNum ) )
+                opts = self.hostOpts[name]
+                #print str(opts)
+                ip = None
+                defaultRoute = None
+                if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
+                    defaultRoute = 'via '+opts['defaultRoute']
+                if 'ip' in opts and len(opts['ip']) > 0:
+                    ip = opts['ip']
+                else:
+                    nodeNum = self.hostOpts[name]['nodeNum']
+                    ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
+                    ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)
+
+                # Create the correct host class
+                if 'cores' in opts or 'cpu' in opts:
+                    if ('privateDirectory' in opts):
+                        hostCls = partial( CPULimitedHost,
+                                           privateDirs=opts['privateDirectory'] )
+                    else:
+                        hostCls=CPULimitedHost
+                else:
+                    if ('privateDirectory' in opts):
+                        hostCls = partial( Host,
+                                           privateDirs=opts['privateDirectory'] )
+                    else:
+                        hostCls=Host
+                print hostCls
+                newHost = net.addHost( name,
+                                       cls=hostCls,
+                                       ip=ip,
+                                       defaultRoute=defaultRoute
+                                      )
+
+                # Set the CPULimitedHost specific options
+                if 'cores' in opts:
+                    newHost.setCPUs(cores = opts['cores'])
+                if 'cpu' in opts:
+                    newHost.setCPUFrac(f=opts['cpu'], sched=opts['sched'])
+
+                # Attach external interfaces
+                if ('externalInterfaces' in opts):
+                    for extInterface in opts['externalInterfaces']:
+                        if self.checkIntf(extInterface):
+                           Intf( extInterface, node=newHost )
+                if ('vlanInterfaces' in opts):
+                    if len(opts['vlanInterfaces']) > 0:
+                        print 'Checking that OS is VLAN prepared'
+                        self.pathCheck('vconfig', moduleName='vlan package')
+                        moduleDeps( add='8021q' )
+            elif 'Controller' in tags:
+                opts = self.controllers[name]
+
+                # Get controller info from panel
+                controllerType = opts['controllerType']
+                if 'controllerProtocol' in opts:
+                    controllerProtocol = opts['controllerProtocol']
+                else:
+                    controllerProtocol = 'tcp'
+                    opts['controllerProtocol'] = 'tcp'
+                controllerIP = opts['remoteIP']
+                controllerPort = opts['remotePort']
+
+                # Make controller
+                print 'Getting controller selection:'+controllerType
+                if controllerType == 'remote':
+                    net.addController(name=name,
+                                      controller=RemoteController,
+                                      ip=controllerIP,
+                                      protocol=controllerProtocol,
+                                      port=controllerPort)
+                elif controllerType == 'inband':
+                    net.addController(name=name,
+                                      controller=InbandController,
+                                      ip=controllerIP,
+                                      protocol=controllerProtocol,
+                                      port=controllerPort)
+                elif controllerType == 'ovsc':
+                    net.addController(name=name,
+                                      controller=OVSController,
+                                      protocol=controllerProtocol,
+                                      port=controllerPort)
+                else:
+                    net.addController(name=name,
+                                      controller=Controller,
+                                      protocol=controllerProtocol,
+                                      port=controllerPort)
+
             else:
                 raise Exception( "Cannot create mystery node: " + name )
+
+    def pathCheck( self, *args, **kwargs ):
+        "Make sure each program in *args can be found in $PATH."
+        moduleName = kwargs.get( 'moduleName', 'it' )
+        for arg in args:
+            if not quietRun( 'which ' + arg ):
+                showerror(title="Error",
+                      message= 'Cannot find required executable %s.\n' % arg +
+                       'Please make sure that %s is installed ' % moduleName +
+                       'and available in your $PATH.' )
+
+    def buildLinks( self, net):
         # Make links
-        for link in self.links.values():
-            ( src, dst ) = link
-            srcName, dstName = src[ 'text' ], dst[ 'text' ]
-            src, dst = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
-            src.linkTo( dst )
+        print "Getting Links."
+        for key,link in self.links.iteritems():
+            tags = self.canvas.gettags(key)
+            if 'data' in tags:
+                src=link['src']
+                dst=link['dest']
+                linkopts=link['linkOpts']
+                srcName, dstName = src[ 'text' ], dst[ 'text' ]
+                srcNode, dstNode = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
+                if linkopts:
+                    net.addLink(srcNode, dstNode, cls=TCLink, **linkopts)
+                else:
+                    #print str(srcNode)
+                    #print str(dstNode)
+                    net.addLink(srcNode, dstNode)
+                self.canvas.itemconfig(key, dash=())
+
+
+    def build( self ):
+        print "Build network based on our topology."
+
+        dpctl = None
+        if len(self.appPrefs['dpctl']) > 0:
+            dpctl = int(self.appPrefs['dpctl'])
+        net = Mininet( topo=None,
+                       listenPort=dpctl,
+                       build=False,
+                       ipBase=self.appPrefs['ipBase'] )
+
+        self.buildNodes(net)
+        self.buildLinks(net)
 
         # Build network (we have to do this separately at the moment )
         net.build()
 
         return net
 
+
+    def postStartSetup( self ):
+
+        # Setup host details
+        for widget in self.widgetToItem:
+            name = widget[ 'text' ]
+            tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+            if 'Host' in tags:
+                newHost = self.net.get(name)
+                opts = self.hostOpts[name]
+                # Attach vlan interfaces
+                if ('vlanInterfaces' in opts):
+                    for vlanInterface in opts['vlanInterfaces']:
+                        print 'adding vlan interface '+vlanInterface[1]
+                        newHost.cmdPrint('ifconfig '+name+'-eth0.'+vlanInterface[1]+' '+vlanInterface[0])
+                # Run User Defined Start Command
+                if ('startCommand' in opts):
+                    newHost.cmdPrint(opts['startCommand'])
+            if 'Switch' in tags:
+                newNode = self.net.get(name)
+                opts = self.switchOpts[name]
+                # Run User Defined Start Command
+                if ('startCommand' in opts):
+                    newNode.cmdPrint(opts['startCommand'])
+
+
+        # Configure NetFlow
+        nflowValues = self.appPrefs['netflow']
+        if len(nflowValues['nflowTarget']) > 0:
+            nflowEnabled = False
+            nflowSwitches = ''
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+    
+                if 'Switch' in tags:
+                    opts = self.switchOpts[name]
+                    if 'netflow' in opts:
+                        if opts['netflow'] == '1':
+                            print name+' has Netflow enabled'
+                            nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
+                            nflowEnabled=True
+            if nflowEnabled:
+                nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '+ 'target=\\\"'+nflowValues['nflowTarget']+'\\\" '+ 'active-timeout='+nflowValues['nflowTimeout']
+                if nflowValues['nflowAddId'] == '1':
+                    nflowCmd = nflowCmd + ' add_id_to_interface=true'
+                else:
+                    nflowCmd = nflowCmd + ' add_id_to_interface=false'
+                print 'cmd = '+nflowCmd+nflowSwitches
+                call(nflowCmd+nflowSwitches, shell=True)
+
+            else:
+                print 'No switches with Netflow'
+        else:
+            print 'No NetFlow targets specified.'
+
+        # Configure sFlow
+        sflowValues = self.appPrefs['sflow']
+        if len(sflowValues['sflowTarget']) > 0:
+            sflowEnabled = False
+            sflowSwitches = ''
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+    
+                if 'Switch' in tags:
+                    opts = self.switchOpts[name]
+                    if 'sflow' in opts:
+                        if opts['sflow'] == '1':
+                            print name+' has sflow enabled'
+                            sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
+                            sflowEnabled=True
+            if sflowEnabled:
+                sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
+                print 'cmd = '+sflowCmd+sflowSwitches
+                call(sflowCmd+sflowSwitches, shell=True)
+
+            else:
+                print 'No switches with sflow'
+        else:
+            print 'No sFlow targets specified.'
+
+        ## NOTE: MAKE SURE THIS IS LAST THING CALLED
+        # Start the CLI if enabled
+        if self.appPrefs['startCLI'] == '1':
+            info( "\n\n NOTE: PLEASE REMEMBER TO EXIT THE CLI BEFORE YOU PRESS THE STOP BUTTON. Not exiting will prevent MiniEdit from quitting and will prevent you from starting the network again during this sessoin.\n\n")
+            CLI(self.net)
+
     def start( self ):
         "Start network."
         if self.net is None:
             self.net = self.build()
-            self.net.start()
+
+            # Since I am going to inject per switch controllers.
+            # I can't call net.start().  I have to replicate what it
+            # does and add the controller options.
+            #self.net.start()
+            info( '**** Starting %s controllers\n' % len( self.net.controllers ) )
+            for controller in self.net.controllers:
+                info( str(controller) + ' ')
+                controller.start()
+            info('\n')
+            info( '**** Starting %s switches\n' % len( self.net.switches ) )
+            #for switch in self.net.switches:
+            #    info( switch.name + ' ')
+            #    switch.start( self.net.controllers )
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'Switch' in tags:
+                    opts = self.switchOpts[name]
+                    switchControllers = []
+                    for ctrl in opts['controllers']:
+                        switchControllers.append(self.net.get(ctrl))
+                    info( name + ' ')
+                    # Figure out what controllers will manage this switch
+                    self.net.get(name).start( switchControllers )
+                if 'LegacySwitch' in tags:
+                    self.net.get(name).start( [] )
+                    info( name + ' ')
+            info('\n')
+
+            self.postStartSetup()
 
     def stop( self ):
         "Stop network."
         if self.net is not None:
+            # Stop host details
+            for widget in self.widgetToItem:
+                name = widget[ 'text' ]
+                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
+                if 'Host' in tags:
+                    newHost = self.net.get(name)
+                    opts = self.hostOpts[name]
+                    # Run User Defined Stop Command
+                    if ('stopCommand' in opts):
+                        newHost.cmdPrint(opts['stopCommand'])
+                if 'Switch' in tags:
+                    newNode = self.net.get(name)
+                    opts = self.switchOpts[name]
+                    # Run User Defined Stop Command
+                    if ('stopCommand' in opts):
+                        newNode.cmdPrint(opts['stopCommand'])
+
             self.net.stop()
         cleanUpScreens()
         self.net = None
 
+    def do_linkPopup(self, event):
+        # display the popup menu
+        if ( self.net is None ):
+            try:
+                self.linkPopup.tk_popup(event.x_root, event.y_root)
+            finally:
+                # make sure to release the grab (Tk 8.0a1 only)
+                self.linkPopup.grab_release()
+        else:
+            try:
+                self.linkPopup.tk_popup(event.x_root, event.y_root)
+            finally:
+                # make sure to release the grab (Tk 8.0a1 only)
+                self.linkPopup.grab_release()
+
+    def do_legacyRouterPopup(self, event):
+        # display the popup menu
+        if ( self.net is None ):
+            try:
+                self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
+            finally:
+                # make sure to release the grab (Tk 8.0a1 only)
+                self.legacyRouterPopup.grab_release()
+
+    def do_hostPopup(self, event):
+        # display the popup menu
+
+        try:
+            self.hostPopup.tk_popup(event.x_root, event.y_root)
+	    isHostPopup = True
+        finally:
+                # make sure to release the grab (Tk 8.0a1 only)
+            self.hostPopup.grab_release()
+
     def xterm( self, _ignore=None ):
         "Make an xterm when a button is pressed."
         if ( self.selection is None or
@@ -667,9 +2579,201 @@
         name = self.itemToWidget[ self.selection ][ 'text' ]
         if name not in self.net.nameToNode:
             return
-        term = makeTerm( self.net.nameToNode[ name ], 'Host' )
-        self.net.terms.append( term )
+        term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
+        if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
+            self.net.terms += term
+        else:
+            self.net.terms.append(term)
 
+    def iperf( self, _ignore=None ):
+        "Make an xterm when a button is pressed."
+        if ( self.selection is None or
+             self.net is None or
+             self.selection not in self.itemToWidget ):
+            return
+        name = self.itemToWidget[ self.selection ][ 'text' ]
+        if name not in self.net.nameToNode:
+            return
+        self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
+
+    """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
+
+    def parseArgs( self ):
+        """Parse command-line args and return options object.
+           returns: opts parse options dict"""
+
+        if '--custom' in sys.argv:
+            index = sys.argv.index( '--custom' )
+            if len( sys.argv ) > index + 1:
+                filename = sys.argv[ index + 1 ]
+                self.parseCustomFile( filename )
+            else:
+                raise Exception( 'Custom file name not found' )
+
+        desc = ( "The %prog utility creates Mininet network from the\n"
+                 "command line. It can create parametrized topologies,\n"
+                 "invoke the Mininet CLI, and run tests." )
+
+        usage = ( '%prog [options]\n'
+                  '(type %prog -h for details)' )
+
+        opts = OptionParser( description=desc, usage=usage )
+
+        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
+        addDictOption( opts, LINKS, LINKDEF, 'link' )
+
+        opts.add_option( '--custom', type='string', default=None,
+                         help='read custom topo and node params from .py' +
+                         'file' )
+
+        self.options, self.args = opts.parse_args()
+        # We don't accept extra arguments after the options
+        if self.args:
+            opts.print_help()
+            exit()
+
+    def setCustom( self, name, value ):
+        "Set custom parameters for MininetRunner."
+        if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
+            # Update dictionaries
+            param = name.upper()
+            globals()[ param ].update( value )
+        elif name == 'validate':
+            # Add custom validate function
+            self.validate = value
+        else:
+            # Add or modify global variable or class
+            globals()[ name ] = value
+
+    def parseCustomFile( self, fileName ):
+        "Parse custom file and add params before parsing cmd-line options."
+        customs = {}
+        if os.path.isfile( fileName ):
+            execfile( fileName, customs, customs )
+            for name, val in customs.iteritems():
+                self.setCustom( name, val )
+        else:
+            raise Exception( 'could not find custom file: %s' % fileName )
+
+    def importTopo( self ):
+        print 'topo='+self.options.topo
+        if self.options.topo == 'none':
+            return
+        self.newTopology()
+        topo = buildTopo( TOPOS, self.options.topo )
+        link = customConstructor( LINKS, self.options.link )
+        importNet = Mininet(topo=topo, build=False, link=link)
+        importNet.build()
+
+        c = self.canvas
+        rowIncrement = 100
+        currentY = 100
+
+        # Add Controllers
+        print 'controllers:'+str(len(importNet.controllers))
+        for controller in importNet.controllers:
+            name = controller.name
+            x = self.controllerCount*100+100
+            self.addNode('Controller', self.controllerCount,
+                 float(x), float(currentY), name=name)
+            icon = self.findWidgetByName(name)
+           #icon.bind('<Button-3>', self.do_controllerPopup )
+            ctrlr = { 'controllerType': 'ref',
+                      'hostname': name,
+                      'controllerProtocol': controller.protocol,
+                      'remoteIP': controller.ip,
+                      'remotePort': controller.port}
+            self.controllers[name] = ctrlr
+
+
+
+        currentY = currentY + rowIncrement
+
+        # Add switches
+        print 'switches:'+str(len(importNet.switches))
+        columnCount = 0
+        for switch in importNet.switches:
+            name = switch.name
+            self.switchOpts[name] = {}
+            self.switchOpts[name]['nodeNum']=self.switchCount
+            self.switchOpts[name]['hostname']=name
+            self.switchOpts[name]['switchType']='default'
+            self.switchOpts[name]['controllers']=[]
+
+            x = columnCount*100+100
+            self.addNode('Switch', self.switchCount,
+                 float(x), float(currentY), name=name)
+            icon = self.findWidgetByName(name)
+            icon.bind('<Button-3>', self.do_switchPopup )
+            # Now link to controllers
+            for controller in importNet.controllers:
+                self.switchOpts[name]['controllers'].append(controller.name)
+                dest = self.findWidgetByName(controller.name)
+                dx, dy = c.coords( self.widgetToItem[ dest ] )
+                self.link = c.create_line(float(x),
+                                          float(currentY),
+                                          dx,
+                                          dy,
+                                          width=4,
+                                          fill='red',
+                                          dash=(6, 4, 2, 4),
+                                          tag='link' )
+                c.itemconfig(self.link, tags=c.gettags(self.link)+('control',))
+                self.addLink( icon, dest, linktype='control' )
+                self.createControlLinkBindings()
+                self.link = self.linkWidget = None
+            if columnCount == 9:
+                columnCount = 0
+                currentY = currentY + rowIncrement
+            else:
+                columnCount =columnCount+1
+
+
+        currentY = currentY + rowIncrement
+        # Add hosts
+        print 'hosts:'+str(len(importNet.hosts))
+        columnCount = 0
+        for host in importNet.hosts:
+            name = host.name
+            self.hostOpts[name] = {'sched':'host'}
+            self.hostOpts[name]['nodeNum']=self.hostCount
+            self.hostOpts[name]['hostname']=name
+            self.hostOpts[name]['ip']=host.IP()
+
+            x = columnCount*100+100
+            self.addNode('Host', self.hostCount,
+                 float(x), float(currentY), name=name)
+            icon = self.findWidgetByName(name)
+            icon.bind('<Button-3>', self.do_hostPopup )
+            if columnCount == 9:
+                columnCount = 0
+                currentY = currentY + rowIncrement
+            else:
+                columnCount =columnCount+1
+
+        print 'links:'+str(len(topo.links()))
+        #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
+        for link in topo.links():
+            print str(link)
+            srcNode = link[0]
+            src = self.findWidgetByName(srcNode)
+            sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
+
+            destNode = link[1]
+            dest = self.findWidgetByName(destNode)
+            dx, dy = self.canvas.coords( self.widgetToItem[ dest]  )
+
+            params = topo.linkInfo( srcNode, destNode )
+            print 'Link Parameters='+str(params)
+
+            self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
+                                             fill='blue', tag='link' )
+            c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
+            self.addLink( src, dest, linkopts=params )
+            self.createDataLinkBindings()
+            self.link = self.linkWidget = None
+
+        importNet.stop()
 
 def miniEditImages():
     "Create and return images for MiniEdit."
@@ -682,13 +2786,68 @@
         'Select': BitmapImage(
             file='/usr/include/X11/bitmaps/left_ptr' ),
 
-	'Switch' : PhotoImage( data=r"""
-	   R0lGODlhOgArAMIEAB8aFwB7tQCb343L8P///////////////yH+GlNvZnR3YXJlOiBNaWNyb3NvZnQgT2ZmaWNlACwAAAAAOgArAAAD/ki63P4wykmrvTjr3YYfQigKH7d5Y6qmnjmBayyHg8vAAqDPaUTbowaA13OIahqcyEgEQEbIi7LIGA1FzsaSQK0QfbnH10sMa83VsqX53HLL7sgUTudR5s367F7PEq4CYDJRcngqfgshiAqAMwF3AYdWTCERjSoBjy+ZVItvMg6XIZmaEgOkSmJwlKOkkKSRlaqraaewr7ABhnqBNLmZuL+6vCzCrpvGsB9EH8m5wc7R0sbQ09bT1dOEBLbXwMjeEN7HpuO6Dt3hFObi7Ovj7d7bEOnYD+4V8PfqF/wN/lKsxZPmop6wBwaFzTsRbVvCWzYQmlMW0UKzZCUqatzICLGjx48gKyYAADs=
-"""),
-	'Host' : PhotoImage( data=r"""
-	    R0lGODlhKQAyAMIHAJyeoK+wsrW2uMHCxM7P0Ozt7fn5+f///yH+EUNyZWF0ZWQgd2l0aCBHSU1QACwAAAAAKQAyAAAD63i63P4wykmrvS4cwLv/IEhxRxGeKGBM3pa+X9QeBmxrT3gMNpyLrt6rgcJgisKXgIFMopaLpjMEVUinn2pQ1ImSrN8uGKCVegHn8bl8CqbV7jFbJ47H650592sX4zl6MX9ocIOBLYNvhkxtiYV8eYx0kJSEi2d7WFmSmZqRmIKeHoddoqOcoaZkqIiqq6CtqqQkrq9jnaKzaLW6Wy8DBMHCp7ClPT+ArMY2t1u9Qs3Et6k+W87KtMfW0r6x1d7P2uDYu+LLtt3nQ9ufxeXM7MkOuCnR7UTe6/jyEOqeWj/SYQEowxXBfgYPJAAAOw==
-"""),
-        'Link': PhotoImage( data=r"""
+        'LegacyRouter': PhotoImage( data=r"""
+	    R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+3QFq1DmL3wJMmAMzZZW11dnZ
+	    2SFrtyNdmTSO6gIZMUKa8gJVqEOHzR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq
+	    6ymF4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9vwNgvwJZsX+69gsXJQFH
+	    jTtjizF0tvHx8VOm9z2V736Dhz2N3QM2acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtg
+	    tktjfQFu3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh312Gt+VGm/AQIDTmB
+	    yAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i56gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia
+	    7DpeggFt2QNPm97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er/yVVhwJJktPh
+	    70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVVhQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18
+	    zKfP9wwcLAMHCwFFiS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh7cve8pG/
+	    7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR1RMjNTF3vU2X4TZupwRSolNne4nB+T+L
+	    2YGz4zJ/zYe99YGHjRdDcT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6WlpW2t
+	    7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90uvPz8wIVKBp42SV5zbfT7wtXpStV
+	    fwFWrBVvyTt3swFz5kGBv2+1/QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
+	    u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T5yH5BAEAAAAALAAAAAAyABgA
+	    Bwj/AAEIHEiQYJY7Qwg9UsTplRIbENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4c
+	    HeoIabJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPATqEBoRB9gVJsxRlhPwHI
+	    0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkS
+	    rtwADuxCG/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmDjhTMmseoKQIFDx7R
+	    oxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq
+	    9dGvv09RHFhcIUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p9HEUFhxgMSAv
+	    jbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CF
+	    oVggmEgCyRf01WcFCYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57AgyZckpKKP
+	    GFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemMIQggeaJywSQ/wgHOAmJskQEfWqBlFBEH
+	    1P/QaGY3QOpDZXA2+A6m7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlBpZdi
+	    isd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtChRmVPNWgpr+Be+Nc9icARww9TkIEu
+	    DAsQ0O7DzGIQzD2QdDEJHTsIAROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
+	    xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE42Q9jtFIp8z0Dy1jQMA1AGzi
+	    z9VoW7310V0znYDTGMQgwUDXLDBO2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOM
+	    LQkcjvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZMDHKCTwI8EcQFHBBAAFc
+	    gGPLHwLwcMIo12Qxu0ABAQA7
+            """),
+
+        'Host': PhotoImage( data=r"""
+            R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
+            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
+            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
+            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
+            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
+            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
+            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
+            mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
+            ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
+            M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
+            AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
+            /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
+            zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
+            mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
+            ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
+            MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
+            AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
+            ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
+            AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
+            RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
+            ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
+            BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
+            HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
+            p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
+            C8cSBBAQADs=
+        """ ),
+
+        'NetLink': PhotoImage( data=r"""
             R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
             mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
             Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
@@ -715,8 +2874,33 @@
         """ )
     }
 
+def addDictOption( opts, choicesDict, default, name, helpStr=None ):
+    """Convenience function to add choices dicts to OptionParser.
+       opts: OptionParser instance
+       choicesDict: dictionary of valid choices, must include default
+       default: default choice key
+       name: long option name
+       help: string"""
+    if default not in choicesDict:
+        raise Exception( 'Invalid  default %s for choices dict: %s' %
+                         ( default, name ) )
+    if not helpStr:
+        helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
+                    '[,param=value...]' )
+    opts.add_option( '--' + name,
+                     type='string',
+                     default = default,
+                     help = helpStr )
+
 if __name__ == '__main__':
     setLogLevel( 'info' )
-    temp_file = parse_args()
-    app = MiniEdit(template_file=temp_file)
+    app = MiniEdit()
+    """ import topology if specified """
+    app.parseArgs()
+    app.importTopo()
+
+    global isHostPopup
+    global isRouterPopup
+    global isLinkPopup
+
     app.mainloop()