diff --git a/examples/miniedit.py b/examples/miniedit.py index 472c0bc80421c0f8a55f143ea1bb36b47ebc9b71..0fcae604b359962f969b6fc61579dae70181560b 100755 --- a/examples/miniedit.py +++ b/examples/miniedit.py @@ -13,10 +13,11 @@ OpenFlow icon from https://www.opennetworking.org/ """ -MINIEDIT_VERSION = '2.1.0.8.1' +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 @@ -28,6 +29,7 @@ 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 @@ -38,9 +40,10 @@ 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 CPULimitedHost, Host, Node, HostWithPrivateDirs from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch from mininet.link import TCLink, Intf, Link from mininet.cli import CLI @@ -66,6 +69,40 @@ 'nox': NOX, 'remote': RemoteController, 'none': lambda name: None } +LINKDEF = 'default' +LINKS = { 'default': Link, + 'tc': TCLink } +HOSTDEF = 'proc' +HOSTS = { 'proc': Host, + 'rt': custom( CPULimitedHost, sched='rt' ), + 'cfs': custom( CPULimitedHost, sched='cfs' ) } + + +class CPULimitedHostWithPrivateDirs( CPULimitedHost ): + "Host with private directories" + + def __init__( self, name, sched='cfs', **kwargs ): + "privateDirs: list of private directory strings or tuples" + self.name = name + self.privateDirs = kwargs.pop( 'privateDirs', [] ) + CPULimitedHost.__init__( self, name, sched, **kwargs ) + self.mountPrivateDirs() + + def mountPrivateDirs( self ): + "mount private directories" + for directory in self.privateDirs: + if isinstance( directory, tuple ): + # mount given private directory + privateDir = directory[ 1 ] % self.__dict__ + mountPoint = directory[ 0 ] + self.cmd( 'mkdir -p %s' % privateDir ) + self.cmd( 'mkdir -p %s' % mountPoint ) + self.cmd( 'mount --bind %s %s' % + ( privateDir, mountPoint ) ) + else: + # mount temporary filesystem on directory + self.cmd( 'mkdir -p %s' % directory ) + self.cmd( 'mount -n -t tmpfs tmpfs %s' % directory ) class InbandController( RemoteController ): @@ -124,35 +161,9 @@ def getSwitchIP(self): def setSwitchIP(self, ip): self.switchIP = ip - def getOpenFlowVersion(self): - return self.openFlowVersions - - def setOpenFlowVersion(self, versions): - self.openFlowVersions = [] - if versions['ovsOf10'] == '1': - self.openFlowVersions.append('OpenFlow10') - if versions['ovsOf11'] == '1': - self.openFlowVersions.append('OpenFlow11') - if versions['ovsOf12'] == '1': - self.openFlowVersions.append('OpenFlow12') - if versions['ovsOf13'] == '1': - self.openFlowVersions.append('OpenFlow13') - - def configureOpenFlowVersion(self): - if not ( 'OpenFlow11' in self.openFlowVersions or - 'OpenFlow12' in self.openFlowVersions or - 'OpenFlow13' in self.openFlowVersions ): - return - - protoList = ",".join(self.openFlowVersions) - print 'Configuring OpenFlow to '+protoList - self.cmd( 'ovs-vsctl -- set bridge', self, 'protocols='+protoList) - def start( self, controllers ): # Call superclass constructor OVSSwitch.start( self, controllers ) - # Set OpenFlow Versions - self.configureOpenFlowVersion() # Set Switch IP address if (self.switchIP is not None): self.cmd( 'ifconfig', self, self.switchIP ) @@ -201,7 +212,7 @@ def body(self, master): # Selection of switch type Label(self.leftfieldFrame, text="Default Switch:").grid(row=3, sticky=E) self.switchType = StringVar(self.leftfieldFrame) - self.switchTypeMenu = OptionMenu(self.leftfieldFrame, self.switchType, "Open vSwitch", "Indigo Virtual Switch", "Userspace Switch", "Userspace Switch inNamespace") + self.switchTypeMenu = OptionMenu(self.leftfieldFrame, self.switchType, "Open vSwitch Kernel Mode", "Indigo Virtual Switch", "Userspace Switch", "Userspace Switch inNamespace") self.switchTypeMenu.grid(row=3, column=1, sticky=W) switchTypePref = self.prefValues['switchType'] if switchTypePref == 'ivs': @@ -211,7 +222,7 @@ def body(self, master): elif switchTypePref == 'user': self.switchType.set("Userspace Switch") else: - self.switchType.set("Open vSwitch") + self.switchType.set("Open vSwitch Kernel Mode") # Fields for OVS OpenFlow version @@ -431,36 +442,43 @@ def __init__(self, master, title, prefDefaults): def body(self, master): self.rootFrame = master - self.leftfieldFrame = Frame(self.rootFrame) - self.leftfieldFrame.grid(row=0, column=0, sticky='nswe', columnspan=2) - self.rightfieldFrame = Frame(self.rootFrame) - self.rightfieldFrame.grid(row=0, column=2, sticky='nswe', columnspan=2) - + 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.leftfieldFrame, text="Hostname:").grid(row=0, sticky=E) - self.hostnameEntry = Entry(self.leftfieldFrame) + 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.leftfieldFrame, text="IP Address:").grid(row=1, sticky=E) - self.ipEntry = Entry(self.leftfieldFrame) + 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.leftfieldFrame, text="Default Route:").grid(row=2, sticky=E) - self.routeEntry = Entry(self.leftfieldFrame) + 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.rightfieldFrame, text="Amount CPU:").grid(row=0, sticky=E) - self.cpuEntry = Entry(self.rightfieldFrame) - self.cpuEntry.grid(row=0, column=1) + 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 @@ -468,26 +486,40 @@ def body(self, master): sched = self.prefValues['sched'] else: sched = 'host' - self.schedVar = StringVar(self.rightfieldFrame) - self.schedOption = OptionMenu(self.rightfieldFrame, self.schedVar, "host", "cfs", "rt") - self.schedOption.grid(row=0, column=2, sticky=W) + 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.rightfieldFrame, text="Cores:").grid(row=1, sticky=E) - self.coreEntry = Entry(self.rightfieldFrame) - self.coreEntry.grid(row=1, column=1) + 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.rootFrame, text="External Interface:").grid(row=1, column=0, sticky=E) - self.b = Button( self.rootFrame, text='Add', command=self.addInterface) - self.b.grid(row=1, column=1) + 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.rootFrame, rows=0, columns=1, title='External Interfaces') - self.interfaceFrame.grid(row=2, column=0, sticky='nswe', columnspan=2) + 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) @@ -499,14 +531,15 @@ def body(self, master): for externalInterface in externalInterfaces: self.tableFrame.addRow(value=[externalInterface]) + ### TAB 3 # VLAN Interfaces self.vlanInterfaces = 0 - Label(self.rootFrame, text="VLAN Interface:").grid(row=1, column=2, sticky=E) - self.vlanButton = Button( self.rootFrame, text='Add', command=self.addVlanInterface) - self.vlanButton.grid(row=1, column=3) + 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.rootFrame, rows=0, columns=2, title='VLAN Interfaces') - self.vlanFrame.grid(row=2, column=2, sticky='nswe', columnspan=2) + 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) @@ -516,6 +549,31 @@ def body(self, master): 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 addDirectory( self ): + self.mountTableFrame.addRow() + def addVlanInterface( self ): self.vlanTableFrame.addRow() @@ -534,6 +592,13 @@ def apply(self): 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(), @@ -541,6 +606,9 @@ def apply(self): '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 @@ -555,34 +623,35 @@ def __init__(self, master, title, prefDefaults): def body(self, master): self.rootFrame = master + self.leftfieldFrame = Frame(self.rootFrame) + self.rightfieldFrame = Frame(self.rootFrame) + self.leftfieldFrame.grid(row=0, column=0, sticky='nswe') + self.rightfieldFrame.grid(row=0, column=1, sticky='nswe') rowCount = 0 externalInterfaces = [] if 'externalInterfaces' in self.prefValues: externalInterfaces = self.prefValues['externalInterfaces'] - self.fieldFrame = Frame(self.rootFrame) - self.fieldFrame.grid(row=0, column=0, sticky='nswe') - # Field for Hostname - Label(self.fieldFrame, text="Hostname:").grid(row=rowCount, sticky=E) - self.hostnameEntry = Entry(self.fieldFrame) + Label(self.leftfieldFrame, text="Hostname:").grid(row=rowCount, sticky=E) + self.hostnameEntry = Entry(self.leftfieldFrame) self.hostnameEntry.grid(row=rowCount, column=1) self.hostnameEntry.insert(0, self.prefValues['hostname']) rowCount+=1 # Field for DPID - Label(self.fieldFrame, text="DPID:").grid(row=rowCount, sticky=E) - self.dpidEntry = Entry(self.fieldFrame) + Label(self.leftfieldFrame, text="DPID:").grid(row=rowCount, sticky=E) + self.dpidEntry = Entry(self.leftfieldFrame) self.dpidEntry.grid(row=rowCount, column=1) if 'dpid' in self.prefValues: self.dpidEntry.insert(0, self.prefValues['dpid']) rowCount+=1 # Field for Netflow - Label(self.fieldFrame, text="Enable NetFlow:").grid(row=rowCount, sticky=E) + Label(self.leftfieldFrame, text="Enable NetFlow:").grid(row=rowCount, sticky=E) self.nflow = IntVar() - self.nflowButton = Checkbutton(self.fieldFrame, variable=self.nflow) + self.nflowButton = Checkbutton(self.leftfieldFrame, variable=self.nflow) self.nflowButton.grid(row=rowCount, column=1, sticky=W) if 'netflow' in self.prefValues: if self.prefValues['netflow'] == '0': @@ -594,9 +663,9 @@ def body(self, master): rowCount+=1 # Field for sflow - Label(self.fieldFrame, text="Enable sFlow:").grid(row=rowCount, sticky=E) + Label(self.leftfieldFrame, text="Enable sFlow:").grid(row=rowCount, sticky=E) self.sflow = IntVar() - self.sflowButton = Checkbutton(self.fieldFrame, variable=self.sflow) + self.sflowButton = Checkbutton(self.leftfieldFrame, variable=self.sflow) self.sflowButton.grid(row=rowCount, column=1, sticky=W) if 'sflow' in self.prefValues: if self.prefValues['sflow'] == '0': @@ -608,9 +677,9 @@ def body(self, master): rowCount+=1 # Selection of switch type - Label(self.fieldFrame, text="Switch Type:").grid(row=rowCount, sticky=E) - self.switchType = StringVar(self.fieldFrame) - self.switchTypeMenu = OptionMenu(self.fieldFrame, self.switchType, "Default", "Open vSwitch", "Indigo Virtual Switch", "Userspace Switch", "Userspace Switch inNamespace") + Label(self.leftfieldFrame, text="Switch Type:").grid(row=rowCount, sticky=E) + self.switchType = StringVar(self.leftfieldFrame) + self.switchTypeMenu = OptionMenu(self.leftfieldFrame, self.switchType, "Default", "Open vSwitch Kernel Mode", "Indigo Virtual Switch", "Userspace Switch", "Userspace Switch inNamespace") self.switchTypeMenu.grid(row=rowCount, column=1, sticky=W) if 'switchType' in self.prefValues: switchTypePref = self.prefValues['switchType'] @@ -621,7 +690,7 @@ def body(self, master): elif switchTypePref == 'user': self.switchType.set("Userspace Switch") elif switchTypePref == 'ovs': - self.switchType.set("Open vSwitch") + self.switchType.set("Open vSwitch Kernel Mode") else: self.switchType.set("Default") else: @@ -629,34 +698,49 @@ def body(self, master): rowCount+=1 # Field for Switch IP - Label(self.fieldFrame, text="IP Address:").grid(row=rowCount, sticky=E) - self.ipEntry = Entry(self.fieldFrame) + Label(self.leftfieldFrame, text="IP Address:").grid(row=rowCount, sticky=E) + self.ipEntry = Entry(self.leftfieldFrame) self.ipEntry.grid(row=rowCount, column=1) if 'switchIP' in self.prefValues: self.ipEntry.insert(0, self.prefValues['switchIP']) rowCount+=1 # Field for DPCTL port - Label(self.fieldFrame, text="DPCTL port:").grid(row=rowCount, sticky=E) - self.dpctlEntry = Entry(self.fieldFrame) + Label(self.leftfieldFrame, text="DPCTL port:").grid(row=rowCount, sticky=E) + self.dpctlEntry = Entry(self.leftfieldFrame) self.dpctlEntry.grid(row=rowCount, column=1) if 'dpctl' in self.prefValues: self.dpctlEntry.insert(0, self.prefValues['dpctl']) rowCount+=1 # External Interfaces - Label(self.fieldFrame, text="External Interface:").grid(row=rowCount, sticky=E) - self.b = Button( self.fieldFrame, text='Add', command=self.addInterface) - self.b.grid(row=rowCount, column=1) + Label(self.rightfieldFrame, text="External Interface:").grid(row=0, sticky=E) + self.b = Button( self.rightfieldFrame, text='Add', command=self.addInterface) + self.b.grid(row=0, column=1) - self.interfaceFrame = VerticalScrolledTable(self.rootFrame, rows=0, columns=1, title='External Interfaces') - self.interfaceFrame.grid(row=2, column=0, sticky='nswe') + self.interfaceFrame = VerticalScrolledTable(self.rightfieldFrame, rows=0, columns=1, title='External Interfaces') + self.interfaceFrame.grid(row=1, column=0, sticky='nswe', columnspan=2) self.tableFrame = self.interfaceFrame.interior # Add defined interfaces for externalInterface in externalInterfaces: self.tableFrame.addRow(value=[externalInterface]) - rowCount+=1 + + self.commandFrame = Frame(self.rootFrame) + self.commandFrame.grid(row=1, column=0, sticky='nswe', columnspan=2) + self.commandFrame.columnconfigure(1, weight=1) + # Start command + Label(self.commandFrame, text="Start Command:").grid(row=0, column=0, sticky=W) + self.startEntry = Entry(self.commandFrame) + self.startEntry.grid(row=0, column=1, sticky='nsew') + if 'startCommand' in self.prefValues: + self.startEntry.insert(0, str(self.prefValues['startCommand'])) + # Stop command + Label(self.commandFrame, text="Stop Command:").grid(row=1, column=0, sticky=W) + self.stopEntry = Entry(self.commandFrame) + self.stopEntry.grid(row=1, column=1, sticky='nsew') + if 'stopCommand' in self.prefValues: + self.stopEntry.insert(0, str(self.prefValues['stopCommand'])) def addInterface( self ): self.tableFrame.addRow() @@ -692,6 +776,8 @@ def apply(self): results = {'externalInterfaces':externalInterfaces, 'hostname':self.hostnameEntry.get(), 'dpid':dpid, + 'startCommand':self.startEntry.get(), + 'stopCommand':self.stopEntry.get(), 'sflow':str(self.sflow.get()), 'netflow':str(self.nflow.get()), 'dpctl':self.dpctlEntry.get(), @@ -707,7 +793,7 @@ def apply(self): results['switchType'] = 'userns' elif sw == 'Userspace Switch': results['switchType'] = 'user' - elif sw == 'Open vSwitch': + elif sw == 'Open vSwitch Kernel Mode': results['switchType'] = 'ovs' else: results['switchType'] = 'default' @@ -877,6 +963,7 @@ def __init__(self, parent, title, ctrlrDefaults=None): def body(self, master): self.var = StringVar(master) + self.protcolvar = StringVar(master) rowCount=0 # Field for Hostname @@ -908,6 +995,20 @@ def body(self, master): 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) @@ -921,14 +1022,11 @@ def body(self, master): return self.hostnameEntry # initial focus def apply(self): - hostname = self.hostnameEntry.get() - controllerType = self.var.get() - remoteIP = self.e1.get() - controllerPort = int(self.e2.get()) - self.result = { 'hostname': hostname, - 'remoteIP': remoteIP, - 'remotePort': controllerPort} + 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': @@ -937,6 +1035,11 @@ def apply(self): 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): @@ -1144,7 +1247,7 @@ def createMenubar( self ): 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", font=font, command=self.exportTopology ) + 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 ) @@ -1305,6 +1408,17 @@ def addNamedNode( self, node, name, x, y): 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 @@ -1317,7 +1431,7 @@ def loadTopology( self ): if f == None: return self.newTopology() - loadedTopology = eval(f.read()) + loadedTopology = self.convertJsonUnicode(json.load(f)) # Load application preferences if 'application' in loadedTopology: @@ -1372,6 +1486,16 @@ def loadTopology( self ): 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 ) @@ -1545,14 +1669,13 @@ def saveTopology( self ): try: f = open(fileName, 'wb') - #f.write(str(savingDictionary)) f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': '))) except Exception as er: print er finally: f.close() - def exportTopology( self ): + def exportScript( self ): "Export command." myFormats = [ ('Mininet Custom Topology','*.py'), @@ -1575,6 +1698,7 @@ def exportTopology( self ): 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 @@ -1613,6 +1737,10 @@ def exportTopology( self ): 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'] @@ -1630,6 +1758,7 @@ def exportTopology( self ): else: f.write(" controller=Controller,\n") + f.write(" protocol='"+controllerProtocol+"',\n") f.write(" port="+str(controllerPort)+")\n") f.write("\n") @@ -1757,7 +1886,7 @@ def exportTopology( self ): f.write(" "+srcName+dstName+" = "+linkOpts+"\n") f.write(" net.addLink("+srcName+", "+dstName) if optsExist: - f.write(", link=TCLink , **"+srcName+dstName) + f.write(", cls=TCLink , **"+srcName+dstName) f.write(")\n") f.write("\n") @@ -1780,7 +1909,7 @@ def exportTopology( self ): f.write("\n") - f.write(" info( '*** Configuring switches\\n')\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 ] ) @@ -1821,10 +1950,75 @@ def exportTopology( self ): 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") @@ -1948,6 +2142,7 @@ def newNode( self, node, event ): 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 @@ -2272,6 +2467,10 @@ def hostDetails( self, _ignore=None ): 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: @@ -2288,6 +2487,8 @@ def hostDetails( self, _ignore=None ): 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) @@ -2309,6 +2510,10 @@ def switchDetails( self, _ignore=None ): 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: @@ -2498,6 +2703,7 @@ def buildNodes( self, net): if 'Switch' in tags: opts = self.switchOpts[name] + #print str(opts) # Create the correct switch class switchClass = customOvs @@ -2525,13 +2731,28 @@ def buildNodes( self, net): 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: - newSwitch.setOpenFlowVersion(self.appPrefs['openFlowVersions']) if ('switchIP' in opts): if (len(opts['switchIP']) > 0): newSwitch.setSwitchIP(opts['switchIP']) @@ -2548,6 +2769,7 @@ def buildNodes( self, net): newSwitch = net.addHost( name , cls=LegacyRouter) elif 'Host' in tags: opts = self.hostOpts[name] + #print str(opts) ip = None defaultRoute = None if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0: @@ -2560,9 +2782,18 @@ def buildNodes( self, net): ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum) # Create the correct host class - hostCls = Host if 'cores' in opts or 'cpu' in opts: - hostCls=CPULimitedHost + if ('privateDirectory' in opts): + hostCls = partial( CPULimitedHostWithPrivateDirs, + privateDirs=opts['privateDirectory'] ) + else: + hostCls=CPULimitedHost + else: + if ('privateDirectory' in opts): + hostCls = partial( HostWithPrivateDirs, + privateDirs=opts['privateDirectory'] ) + else: + hostCls=Host newHost = net.addHost( name, cls=hostCls, ip=ip, @@ -2590,28 +2821,37 @@ def buildNodes( self, net): # 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 - controllerIP = opts['remoteIP'] - controllerPort = opts['remotePort'] 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: @@ -2637,11 +2877,13 @@ def buildLinks( self, net): dst=link['dest'] linkopts=link['linkOpts'] srcName, dstName = src[ 'text' ], dst[ 'text' ] - src, dst = net.nameToNode[ srcName ], net.nameToNode[ dstName ] + srcNode, dstNode = net.nameToNode[ srcName ], net.nameToNode[ dstName ] if linkopts: - net.addLink(src, dst, cls=TCLink, **linkopts) + net.addLink(srcNode, dstNode, cls=TCLink, **linkopts) else: - net.addLink(src, dst) + #print str(srcNode) + #print str(dstNode) + net.addLink(srcNode, dstNode) self.canvas.itemconfig(key, dash=()) @@ -2667,19 +2909,28 @@ def build( self ): def postStartSetup( self ): - # Setup host VLAN subinterfaces + # 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 = self.net.get(name) - newHost.cmdPrint('vconfig add '+name+'-eth0 '+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'] @@ -2782,6 +3033,23 @@ def start( self ): 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 @@ -2908,6 +3176,8 @@ def parseArgs( self ): 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' ) @@ -2947,7 +3217,8 @@ def importTopo( self ): return self.newTopology() topo = buildTopo( TOPOS, self.options.topo ) - importNet = Mininet(topo=topo, build=False) + link = customConstructor( LINKS, self.options.link ) + importNet = Mininet(topo=topo, build=False, link=link) importNet.build() c = self.canvas @@ -2965,6 +3236,7 @@ def importTopo( self ): 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 @@ -3038,6 +3310,7 @@ def importTopo( self ): 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 ] ) @@ -3046,10 +3319,13 @@ def importTopo( self ): 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 ) + self.addLink( src, dest, linkopts=params ) self.createDataLinkBindings() self.link = self.linkWidget = None