Statistics
| Branch: | Tag: | Revision:

mininet / examples / miniedit.py @ 49654212

History | View | Annotate | Download (151 KB)

1
#!/usr/bin/python
2

    
3
"""
4
MiniEdit: a simple network editor for Mininet
5

6
This is a simple demonstration of how one might build a
7
GUI application using Mininet as the network model.
8

9
Bob Lantz, April 2010
10
Gregory Gee, July 2013
11

12
Controller icon from http://semlabs.co.uk/
13
OpenFlow icon from https://www.opennetworking.org/
14
"""
15

    
16
# Miniedit needs some work in order to pass pylint...
17
# pylint: disable=line-too-long,too-many-lines,too-many-branches
18
# pylint: disable=too-many-statements,attribute-defined-outside-init
19
# pylint: disable=missing-docstring
20

    
21
MINIEDIT_VERSION = '2.2.0.1'
22

    
23
from optparse import OptionParser
24
# from Tkinter import *
25
from Tkinter import ( Frame, Label, LabelFrame, Entry, OptionMenu, Checkbutton,
26
                      Menu, Toplevel, Button, BitmapImage, PhotoImage, Canvas,
27
                      Scrollbar, Wm, TclError, StringVar, IntVar,
28
                      E, W, EW, NW, Y, VERTICAL, SOLID, CENTER,
29
                      RIGHT, LEFT, BOTH, TRUE, FALSE )
30
from ttk import Notebook
31
from tkMessageBox import showerror
32
from subprocess import call
33
import tkFont
34
import tkFileDialog
35
import tkSimpleDialog
36
import re
37
import json
38
from distutils.version import StrictVersion
39
import os
40
import sys
41
from functools import partial
42

    
43
if 'PYTHONPATH' in os.environ:
44
    sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
45

    
46
# someday: from ttk import *
47

    
48
from mininet.log import info, setLogLevel
49
from mininet.net import Mininet, VERSION
50
from mininet.util import netParse, ipAdd, quietRun
51
from mininet.util import buildTopo
52
from mininet.util import custom, customConstructor
53
from mininet.term import makeTerm, cleanUpScreens
54
from mininet.node import Controller, RemoteController, NOX, OVSController
55
from mininet.node import CPULimitedHost, Host, Node
56
from mininet.node import OVSSwitch, UserSwitch
57
from mininet.link import TCLink, Intf, Link
58
from mininet.cli import CLI
59
from mininet.moduledeps import moduleDeps
60
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
61
from mininet.topolib import TreeTopo
62

    
63
print 'MiniEdit running against Mininet '+VERSION
64
MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
65
if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
66
    from mininet.node import IVSSwitch
67

    
68
TOPODEF = 'none'
69
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
70
          'linear': LinearTopo,
71
          'reversed': SingleSwitchReversedTopo,
72
          'single': SingleSwitchTopo,
73
          'none': None,
74
          'tree': TreeTopo }
75
CONTROLLERDEF = 'ref'
76
CONTROLLERS = { 'ref': Controller,
77
                'ovsc': OVSController,
78
                'nox': NOX,
79
                'remote': RemoteController,
80
                'none': lambda name: None }
81
LINKDEF = 'default'
82
LINKS = { 'default': Link,
83
          'tc': TCLink }
84
HOSTDEF = 'proc'
85
HOSTS = { 'proc': Host,
86
          'rt': custom( CPULimitedHost, sched='rt' ),
87
          'cfs': custom( CPULimitedHost, sched='cfs' ) }
88

    
89

    
90
class InbandController( RemoteController ):
91
    "RemoteController that ignores checkListening"
92
    def checkListening( self ):
93
        "Overridden to do nothing."
94
        return
95

    
96
class CustomUserSwitch(UserSwitch):
97
    "Customized UserSwitch"
98
    def __init__( self, name, dpopts='--no-slicing', **kwargs ):
99
        UserSwitch.__init__( self, name, **kwargs )
100
        self.switchIP = None
101

    
102
    def getSwitchIP(self):
103
        "Return management IP address"
104
        return self.switchIP
105

    
106
    def setSwitchIP(self, ip):
107
        "Set management IP address"
108
        self.switchIP = ip
109

    
110
    def start( self, controllers ):
111
        "Start and set management IP address"
112
        # Call superclass constructor
113
        UserSwitch.start( self, controllers )
114
        # Set Switch IP address
115
        if self.switchIP is not None:
116
            if not self.inNamespace:
117
                self.cmd( 'ifconfig', self, self.switchIP )
118
            else:
119
                self.cmd( 'ifconfig lo', self.switchIP )
120

    
121
class LegacyRouter( Node ):
122
    "Simple IP router"
123
    def __init__( self, name, inNamespace=True, **params ):
124
        Node.__init__( self, name, inNamespace, **params )
125

    
126
    def config( self, **_params ):
127
        if self.intfs:
128
            self.setParam( _params, 'setIP', ip='0.0.0.0' )
129
        r = Node.config( self, **_params )
130
        self.cmd('sysctl -w net.ipv4.ip_forward=1')
131
        return r
132

    
133
class LegacySwitch(OVSSwitch):
134
    "OVS switch in standalone/bridge mode"
135
    def __init__( self, name, **params ):
136
        OVSSwitch.__init__( self, name, failMode='standalone', **params )
137
        self.switchIP = None
138

    
139
class customOvs(OVSSwitch):
140
    "Customized OVS switch"
141

    
142
    def __init__( self, name, failMode='secure', datapath='kernel', **params ):
143
        OVSSwitch.__init__( self, name, failMode=failMode, datapath=datapath, **params )
144
        self.switchIP = None
145

    
146
    def getSwitchIP(self):
147
        "Return management IP address"
148
        return self.switchIP
149

    
150
    def setSwitchIP(self, ip):
151
        "Set management IP address"
152
        self.switchIP = ip
153

    
154
    def start( self, controllers ):
155
        "Start and set management IP address"
156
        # Call superclass constructor
157
        OVSSwitch.start( self, controllers )
158
        # Set Switch IP address
159
        if self.switchIP is not None:
160
            self.cmd( 'ifconfig', self, self.switchIP )
161

    
162
class PrefsDialog(tkSimpleDialog.Dialog):
163
    "Preferences dialog"
164

    
165
    def __init__(self, parent, title, prefDefaults):
166

    
167
        self.prefValues = prefDefaults
168

    
169
        tkSimpleDialog.Dialog.__init__(self, parent, title)
170

    
171
    def body(self, master):
172
        "Create dialog body"
173
        self.rootFrame = master
174
        self.leftfieldFrame = Frame(self.rootFrame, padx=5, pady=5)
175
        self.leftfieldFrame.grid(row=0, column=0, sticky='nswe', columnspan=2)
176
        self.rightfieldFrame = Frame(self.rootFrame, padx=5, pady=5)
177
        self.rightfieldFrame.grid(row=0, column=2, sticky='nswe', columnspan=2)
178

    
179
        # Field for Base IP
180
        Label(self.leftfieldFrame, text="IP Base:").grid(row=0, sticky=E)
181
        self.ipEntry = Entry(self.leftfieldFrame)
182
        self.ipEntry.grid(row=0, column=1)
183
        ipBase =  self.prefValues['ipBase']
184
        self.ipEntry.insert(0, ipBase)
185

    
186
        # Selection of terminal type
187
        Label(self.leftfieldFrame, text="Default Terminal:").grid(row=1, sticky=E)
188
        self.terminalVar = StringVar(self.leftfieldFrame)
189
        self.terminalOption = OptionMenu(self.leftfieldFrame, self.terminalVar, "xterm", "gterm")
190
        self.terminalOption.grid(row=1, column=1, sticky=W)
191
        terminalType = self.prefValues['terminalType']
192
        self.terminalVar.set(terminalType)
193

    
194
        # Field for CLI
195
        Label(self.leftfieldFrame, text="Start CLI:").grid(row=2, sticky=E)
196
        self.cliStart = IntVar()
197
        self.cliButton = Checkbutton(self.leftfieldFrame, variable=self.cliStart)
198
        self.cliButton.grid(row=2, column=1, sticky=W)
199
        if self.prefValues['startCLI'] == '0':
200
            self.cliButton.deselect()
201
        else:
202
            self.cliButton.select()
203

    
204
        # Selection of switch type
205
        Label(self.leftfieldFrame, text="Default Switch:").grid(row=3, sticky=E)
206
        self.switchType = StringVar(self.leftfieldFrame)
207
        self.switchTypeMenu = OptionMenu(self.leftfieldFrame, self.switchType, "Open vSwitch Kernel Mode", "Indigo Virtual Switch", "Userspace Switch", "Userspace Switch inNamespace")
208
        self.switchTypeMenu.grid(row=3, column=1, sticky=W)
209
        switchTypePref = self.prefValues['switchType']
210
        if switchTypePref == 'ivs':
211
            self.switchType.set("Indigo Virtual Switch")
212
        elif switchTypePref == 'userns':
213
            self.switchType.set("Userspace Switch inNamespace")
214
        elif switchTypePref == 'user':
215
            self.switchType.set("Userspace Switch")
216
        else:
217
            self.switchType.set("Open vSwitch Kernel Mode")
218

    
219

    
220
        # Fields for OVS OpenFlow version
221
        ovsFrame= LabelFrame(self.leftfieldFrame, text='Open vSwitch', padx=5, pady=5)
222
        ovsFrame.grid(row=4, column=0, columnspan=2, sticky=EW)
223
        Label(ovsFrame, text="OpenFlow 1.0:").grid(row=0, sticky=E)
224
        Label(ovsFrame, text="OpenFlow 1.1:").grid(row=1, sticky=E)
225
        Label(ovsFrame, text="OpenFlow 1.2:").grid(row=2, sticky=E)
226
        Label(ovsFrame, text="OpenFlow 1.3:").grid(row=3, sticky=E)
227

    
228
        self.ovsOf10 = IntVar()
229
        self.covsOf10 = Checkbutton(ovsFrame, variable=self.ovsOf10)
230
        self.covsOf10.grid(row=0, column=1, sticky=W)
231
        if self.prefValues['openFlowVersions']['ovsOf10'] == '0':
232
            self.covsOf10.deselect()
233
        else:
234
            self.covsOf10.select()
235

    
236
        self.ovsOf11 = IntVar()
237
        self.covsOf11 = Checkbutton(ovsFrame, variable=self.ovsOf11)
238
        self.covsOf11.grid(row=1, column=1, sticky=W)
239
        if self.prefValues['openFlowVersions']['ovsOf11'] == '0':
240
            self.covsOf11.deselect()
241
        else:
242
            self.covsOf11.select()
243

    
244
        self.ovsOf12 = IntVar()
245
        self.covsOf12 = Checkbutton(ovsFrame, variable=self.ovsOf12)
246
        self.covsOf12.grid(row=2, column=1, sticky=W)
247
        if self.prefValues['openFlowVersions']['ovsOf12'] == '0':
248
            self.covsOf12.deselect()
249
        else:
250
            self.covsOf12.select()
251

    
252
        self.ovsOf13 = IntVar()
253
        self.covsOf13 = Checkbutton(ovsFrame, variable=self.ovsOf13)
254
        self.covsOf13.grid(row=3, column=1, sticky=W)
255
        if self.prefValues['openFlowVersions']['ovsOf13'] == '0':
256
            self.covsOf13.deselect()
257
        else:
258
            self.covsOf13.select()
259

    
260
        # Field for DPCTL listen port
261
        Label(self.leftfieldFrame, text="dpctl port:").grid(row=5, sticky=E)
262
        self.dpctlEntry = Entry(self.leftfieldFrame)
263
        self.dpctlEntry.grid(row=5, column=1)
264
        if 'dpctl' in self.prefValues:
265
            self.dpctlEntry.insert(0, self.prefValues['dpctl'])
266

    
267
        # sFlow
268
        sflowValues = self.prefValues['sflow']
269
        self.sflowFrame= LabelFrame(self.rightfieldFrame, text='sFlow Profile for Open vSwitch', padx=5, pady=5)
270
        self.sflowFrame.grid(row=0, column=0, columnspan=2, sticky=EW)
271

    
272
        Label(self.sflowFrame, text="Target:").grid(row=0, sticky=E)
273
        self.sflowTarget = Entry(self.sflowFrame)
274
        self.sflowTarget.grid(row=0, column=1)
275
        self.sflowTarget.insert(0, sflowValues['sflowTarget'])
276

    
277
        Label(self.sflowFrame, text="Sampling:").grid(row=1, sticky=E)
278
        self.sflowSampling = Entry(self.sflowFrame)
279
        self.sflowSampling.grid(row=1, column=1)
280
        self.sflowSampling.insert(0, sflowValues['sflowSampling'])
281

    
282
        Label(self.sflowFrame, text="Header:").grid(row=2, sticky=E)
283
        self.sflowHeader = Entry(self.sflowFrame)
284
        self.sflowHeader.grid(row=2, column=1)
285
        self.sflowHeader.insert(0, sflowValues['sflowHeader'])
286

    
287
        Label(self.sflowFrame, text="Polling:").grid(row=3, sticky=E)
288
        self.sflowPolling = Entry(self.sflowFrame)
289
        self.sflowPolling.grid(row=3, column=1)
290
        self.sflowPolling.insert(0, sflowValues['sflowPolling'])
291

    
292
        # NetFlow
293
        nflowValues = self.prefValues['netflow']
294
        self.nFrame= LabelFrame(self.rightfieldFrame, text='NetFlow Profile for Open vSwitch', padx=5, pady=5)
295
        self.nFrame.grid(row=1, column=0, columnspan=2, sticky=EW)
296

    
297
        Label(self.nFrame, text="Target:").grid(row=0, sticky=E)
298
        self.nflowTarget = Entry(self.nFrame)
299
        self.nflowTarget.grid(row=0, column=1)
300
        self.nflowTarget.insert(0, nflowValues['nflowTarget'])
301

    
302
        Label(self.nFrame, text="Active Timeout:").grid(row=1, sticky=E)
303
        self.nflowTimeout = Entry(self.nFrame)
304
        self.nflowTimeout.grid(row=1, column=1)
305
        self.nflowTimeout.insert(0, nflowValues['nflowTimeout'])
306

    
307
        Label(self.nFrame, text="Add ID to Interface:").grid(row=2, sticky=E)
308
        self.nflowAddId = IntVar()
309
        self.nflowAddIdButton = Checkbutton(self.nFrame, variable=self.nflowAddId)
310
        self.nflowAddIdButton.grid(row=2, column=1, sticky=W)
311
        if nflowValues['nflowAddId'] == '0':
312
            self.nflowAddIdButton.deselect()
313
        else:
314
            self.nflowAddIdButton.select()
315

    
316
        # initial focus
317
        return self.ipEntry
318

    
319
    def apply(self):
320
        ipBase = self.ipEntry.get()
321
        terminalType = self.terminalVar.get()
322
        startCLI = str(self.cliStart.get())
323
        sw = self.switchType.get()
324
        dpctl = self.dpctlEntry.get()
325

    
326
        ovsOf10 = str(self.ovsOf10.get())
327
        ovsOf11 = str(self.ovsOf11.get())
328
        ovsOf12 = str(self.ovsOf12.get())
329
        ovsOf13 = str(self.ovsOf13.get())
330

    
331
        sflowValues = {'sflowTarget':self.sflowTarget.get(),
332
                       'sflowSampling':self.sflowSampling.get(),
333
                       'sflowHeader':self.sflowHeader.get(),
334
                       'sflowPolling':self.sflowPolling.get()}
335
        nflowvalues = {'nflowTarget':self.nflowTarget.get(),
336
                       'nflowTimeout':self.nflowTimeout.get(),
337
                       'nflowAddId':str(self.nflowAddId.get())}
338
        self.result = {'ipBase':ipBase,
339
                       'terminalType':terminalType,
340
                       'dpctl':dpctl,
341
                       'sflow':sflowValues,
342
                       'netflow':nflowvalues,
343
                       'startCLI':startCLI}
344
        if sw == 'Indigo Virtual Switch':
345
            self.result['switchType'] = 'ivs'
346
            if StrictVersion(MININET_VERSION) < StrictVersion('2.1'):
347
                self.ovsOk = False
348
                showerror(title="Error",
349
                          message='MiniNet version 2.1+ required. You have '+VERSION+'.')
350
        elif sw == 'Userspace Switch':
351
            self.result['switchType'] = 'user'
352
        elif sw == 'Userspace Switch inNamespace':
353
            self.result['switchType'] = 'userns'
354
        else:
355
            self.result['switchType'] = 'ovs'
356

    
357
        self.ovsOk = True
358
        if ovsOf11 == "1":
359
            ovsVer = self.getOvsVersion()
360
            if StrictVersion(ovsVer) < StrictVersion('2.0'):
361
                self.ovsOk = False
362
                showerror(title="Error",
363
                          message='Open vSwitch version 2.0+ required. You have '+ovsVer+'.')
364
        if ovsOf12 == "1" or ovsOf13 == "1":
365
            ovsVer = self.getOvsVersion()
366
            if StrictVersion(ovsVer) < StrictVersion('1.10'):
367
                self.ovsOk = False
368
                showerror(title="Error",
369
                          message='Open vSwitch version 1.10+ required. You have '+ovsVer+'.')
370

    
371
        if self.ovsOk:
372
            self.result['openFlowVersions']={'ovsOf10':ovsOf10,
373
                                             'ovsOf11':ovsOf11,
374
                                             'ovsOf12':ovsOf12,
375
                                             'ovsOf13':ovsOf13}
376
        else:
377
            self.result = None
378

    
379
    def getOvsVersion(self):
380
        "Return OVS version"
381
        outp = quietRun("ovs-vsctl show")
382
        r = r'ovs_version: "(.*)"'
383
        m = re.search(r, outp)
384
        if m is None:
385
            print 'Version check failed'
386
            return None
387
        else:
388
            print 'Open vSwitch version is '+m.group(1)
389
            return m.group(1)
390

    
391

    
392
class CustomDialog(object):
393

    
394
    # TODO: Fix button placement and Title and window focus lock
395
    def __init__(self, master, title):
396
        self.top=Toplevel(master)
397

    
398
        self.bodyFrame = Frame(self.top)
399
        self.bodyFrame.grid(row=0, column=0, sticky='nswe')
400
        self.body(self.bodyFrame)
401

    
402
        #return self.b # initial focus
403
        buttonFrame = Frame(self.top, relief='ridge', bd=3, bg='lightgrey')
404
        buttonFrame.grid(row=1 , column=0, sticky='nswe')
405

    
406
        okButton = Button(buttonFrame, width=8, text='OK', relief='groove',
407
                   bd=4, command=self.okAction)
408
        okButton.grid(row=0, column=0, sticky=E)
409

    
410
        canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
411
                    bd=4, command=self.cancelAction)
412
        canlceButton.grid(row=0, column=1, sticky=W)
413

    
414
    def body(self, master):
415
        self.rootFrame = master
416

    
417
    def apply(self):
418
        self.top.destroy()
419

    
420
    def cancelAction(self):
421
        self.top.destroy()
422

    
423
    def okAction(self):
424
        self.apply()
425
        self.top.destroy()
426

    
427
class HostDialog(CustomDialog):
428

    
429
    def __init__(self, master, title, prefDefaults):
430

    
431
        self.prefValues = prefDefaults
432
        self.result = None
433

    
434
        CustomDialog.__init__(self, master, title)
435

    
436
    def body(self, master):
437
        self.rootFrame = master
438
        n = Notebook(self.rootFrame)
439
        self.propFrame = Frame(n)
440
        self.vlanFrame = Frame(n)
441
        self.interfaceFrame = Frame(n)
442
        self.mountFrame = Frame(n)
443
        n.add(self.propFrame, text='Properties')
444
        n.add(self.vlanFrame, text='VLAN Interfaces')
445
        n.add(self.interfaceFrame, text='External Interfaces')
446
        n.add(self.mountFrame, text='Private Directories')
447
        n.pack()
448

    
449
        ### TAB 1
450
        # Field for Hostname
451
        Label(self.propFrame, text="Hostname:").grid(row=0, sticky=E)
452
        self.hostnameEntry = Entry(self.propFrame)
453
        self.hostnameEntry.grid(row=0, column=1)
454
        if 'hostname' in self.prefValues:
455
            self.hostnameEntry.insert(0, self.prefValues['hostname'])
456

    
457
        # Field for Switch IP
458
        Label(self.propFrame, text="IP Address:").grid(row=1, sticky=E)
459
        self.ipEntry = Entry(self.propFrame)
460
        self.ipEntry.grid(row=1, column=1)
461
        if 'ip' in self.prefValues:
462
            self.ipEntry.insert(0, self.prefValues['ip'])
463

    
464
        # Field for default route
465
        Label(self.propFrame, text="Default Route:").grid(row=2, sticky=E)
466
        self.routeEntry = Entry(self.propFrame)
467
        self.routeEntry.grid(row=2, column=1)
468
        if 'defaultRoute' in self.prefValues:
469
            self.routeEntry.insert(0, self.prefValues['defaultRoute'])
470

    
471
        # Field for CPU
472
        Label(self.propFrame, text="Amount CPU:").grid(row=3, sticky=E)
473
        self.cpuEntry = Entry(self.propFrame)
474
        self.cpuEntry.grid(row=3, column=1)
475
        if 'cpu' in self.prefValues:
476
            self.cpuEntry.insert(0, str(self.prefValues['cpu']))
477
        # Selection of Scheduler
478
        if 'sched' in self.prefValues:
479
            sched =  self.prefValues['sched']
480
        else:
481
            sched = 'host'
482
        self.schedVar = StringVar(self.propFrame)
483
        self.schedOption = OptionMenu(self.propFrame, self.schedVar, "host", "cfs", "rt")
484
        self.schedOption.grid(row=3, column=2, sticky=W)
485
        self.schedVar.set(sched)
486

    
487
        # Selection of Cores
488
        Label(self.propFrame, text="Cores:").grid(row=4, sticky=E)
489
        self.coreEntry = Entry(self.propFrame)
490
        self.coreEntry.grid(row=4, column=1)
491
        if 'cores' in self.prefValues:
492
            self.coreEntry.insert(1, self.prefValues['cores'])
493

    
494
        # Start command
495
        Label(self.propFrame, text="Start Command:").grid(row=5, sticky=E)
496
        self.startEntry = Entry(self.propFrame)
497
        self.startEntry.grid(row=5, column=1, sticky='nswe', columnspan=3)
498
        if 'startCommand' in self.prefValues:
499
            self.startEntry.insert(0, str(self.prefValues['startCommand']))
500
        # Stop command
501
        Label(self.propFrame, text="Stop Command:").grid(row=6, sticky=E)
502
        self.stopEntry = Entry(self.propFrame)
503
        self.stopEntry.grid(row=6, column=1, sticky='nswe', columnspan=3)
504
        if 'stopCommand' in self.prefValues:
505
            self.stopEntry.insert(0, str(self.prefValues['stopCommand']))
506

    
507
        ### TAB 2
508
        # External Interfaces
509
        self.externalInterfaces = 0
510
        Label(self.interfaceFrame, text="External Interface:").grid(row=0, column=0, sticky=E)
511
        self.b = Button( self.interfaceFrame, text='Add', command=self.addInterface)
512
        self.b.grid(row=0, column=1)
513

    
514
        self.interfaceFrame = VerticalScrolledTable(self.interfaceFrame, rows=0, columns=1, title='External Interfaces')
515
        self.interfaceFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
516
        self.tableFrame = self.interfaceFrame.interior
517
        self.tableFrame.addRow(value=['Interface Name'], readonly=True)
518

    
519
        # Add defined interfaces
520
        externalInterfaces = []
521
        if 'externalInterfaces' in self.prefValues:
522
            externalInterfaces = self.prefValues['externalInterfaces']
523

    
524
        for externalInterface in externalInterfaces:
525
            self.tableFrame.addRow(value=[externalInterface])
526

    
527
        ### TAB 3
528
        # VLAN Interfaces
529
        self.vlanInterfaces = 0
530
        Label(self.vlanFrame, text="VLAN Interface:").grid(row=0, column=0, sticky=E)
531
        self.vlanButton = Button( self.vlanFrame, text='Add', command=self.addVlanInterface)
532
        self.vlanButton.grid(row=0, column=1)
533

    
534
        self.vlanFrame = VerticalScrolledTable(self.vlanFrame, rows=0, columns=2, title='VLAN Interfaces')
535
        self.vlanFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
536
        self.vlanTableFrame = self.vlanFrame.interior
537
        self.vlanTableFrame.addRow(value=['IP Address','VLAN ID'], readonly=True)
538

    
539
        vlanInterfaces = []
540
        if 'vlanInterfaces' in self.prefValues:
541
            vlanInterfaces = self.prefValues['vlanInterfaces']
542
        for vlanInterface in vlanInterfaces:
543
            self.vlanTableFrame.addRow(value=vlanInterface)
544

    
545
        ### TAB 4
546
        # Private Directories
547
        self.privateDirectories = 0
548
        Label(self.mountFrame, text="Private Directory:").grid(row=0, column=0, sticky=E)
549
        self.mountButton = Button( self.mountFrame, text='Add', command=self.addDirectory)
550
        self.mountButton.grid(row=0, column=1)
551

    
552
        self.mountFrame = VerticalScrolledTable(self.mountFrame, rows=0, columns=2, title='Directories')
553
        self.mountFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
554
        self.mountTableFrame = self.mountFrame.interior
555
        self.mountTableFrame.addRow(value=['Mount','Persistent Directory'], readonly=True)
556

    
557
        directoryList = []
558
        if 'privateDirectory' in self.prefValues:
559
            directoryList = self.prefValues['privateDirectory']
560
        for privateDir in directoryList:
561
            if isinstance( privateDir, tuple ):
562
                self.mountTableFrame.addRow(value=privateDir)
563
            else:
564
                self.mountTableFrame.addRow(value=[privateDir,''])
565

    
566

    
567
    def addDirectory( self ):
568
        self.mountTableFrame.addRow()
569

    
570
    def addVlanInterface( self ):
571
        self.vlanTableFrame.addRow()
572

    
573
    def addInterface( self ):
574
        self.tableFrame.addRow()
575

    
576
    def apply(self):
577
        externalInterfaces = []
578
        for row in range(self.tableFrame.rows):
579
            if (len(self.tableFrame.get(row, 0)) > 0 and
580
                row > 0):
581
                externalInterfaces.append(self.tableFrame.get(row, 0))
582
        vlanInterfaces = []
583
        for row in range(self.vlanTableFrame.rows):
584
            if (len(self.vlanTableFrame.get(row, 0)) > 0 and
585
                len(self.vlanTableFrame.get(row, 1)) > 0 and
586
                row > 0):
587
                vlanInterfaces.append([self.vlanTableFrame.get(row, 0), self.vlanTableFrame.get(row, 1)])
588
        privateDirectories = []
589
        for row in range(self.mountTableFrame.rows):
590
            if len(self.mountTableFrame.get(row, 0)) > 0 and row > 0:
591
                if len(self.mountTableFrame.get(row, 1)) > 0:
592
                    privateDirectories.append((self.mountTableFrame.get(row, 0), self.mountTableFrame.get(row, 1)))
593
                else:
594
                    privateDirectories.append(self.mountTableFrame.get(row, 0))
595

    
596
        results = {'cpu': self.cpuEntry.get(),
597
                   'cores':self.coreEntry.get(),
598
                   'sched':self.schedVar.get(),
599
                   'hostname':self.hostnameEntry.get(),
600
                   'ip':self.ipEntry.get(),
601
                   'defaultRoute':self.routeEntry.get(),
602
                   'startCommand':self.startEntry.get(),
603
                   'stopCommand':self.stopEntry.get(),
604
                   'privateDirectory':privateDirectories,
605
                   'externalInterfaces':externalInterfaces,
606
                   'vlanInterfaces':vlanInterfaces}
607
        self.result = results
608

    
609
class SwitchDialog(CustomDialog):
610

    
611
    def __init__(self, master, title, prefDefaults):
612

    
613
        self.prefValues = prefDefaults
614
        self.result = None
615
        CustomDialog.__init__(self, master, title)
616

    
617
    def body(self, master):
618
        self.rootFrame = master
619
        self.leftfieldFrame = Frame(self.rootFrame)
620
        self.rightfieldFrame = Frame(self.rootFrame)
621
        self.leftfieldFrame.grid(row=0, column=0, sticky='nswe')
622
        self.rightfieldFrame.grid(row=0, column=1, sticky='nswe')
623

    
624
        rowCount = 0
625
        externalInterfaces = []
626
        if 'externalInterfaces' in self.prefValues:
627
            externalInterfaces = self.prefValues['externalInterfaces']
628

    
629
        # Field for Hostname
630
        Label(self.leftfieldFrame, text="Hostname:").grid(row=rowCount, sticky=E)
631
        self.hostnameEntry = Entry(self.leftfieldFrame)
632
        self.hostnameEntry.grid(row=rowCount, column=1)
633
        self.hostnameEntry.insert(0, self.prefValues['hostname'])
634
        rowCount+=1
635

    
636
        # Field for DPID
637
        Label(self.leftfieldFrame, text="DPID:").grid(row=rowCount, sticky=E)
638
        self.dpidEntry = Entry(self.leftfieldFrame)
639
        self.dpidEntry.grid(row=rowCount, column=1)
640
        if 'dpid' in self.prefValues:
641
            self.dpidEntry.insert(0, self.prefValues['dpid'])
642
        rowCount+=1
643

    
644
        # Field for Netflow
645
        Label(self.leftfieldFrame, text="Enable NetFlow:").grid(row=rowCount, sticky=E)
646
        self.nflow = IntVar()
647
        self.nflowButton = Checkbutton(self.leftfieldFrame, variable=self.nflow)
648
        self.nflowButton.grid(row=rowCount, column=1, sticky=W)
649
        if 'netflow' in self.prefValues:
650
            if self.prefValues['netflow'] == '0':
651
                self.nflowButton.deselect()
652
            else:
653
                self.nflowButton.select()
654
        else:
655
            self.nflowButton.deselect()
656
        rowCount+=1
657

    
658
        # Field for sflow
659
        Label(self.leftfieldFrame, text="Enable sFlow:").grid(row=rowCount, sticky=E)
660
        self.sflow = IntVar()
661
        self.sflowButton = Checkbutton(self.leftfieldFrame, variable=self.sflow)
662
        self.sflowButton.grid(row=rowCount, column=1, sticky=W)
663
        if 'sflow' in self.prefValues:
664
            if self.prefValues['sflow'] == '0':
665
                self.sflowButton.deselect()
666
            else:
667
                self.sflowButton.select()
668
        else:
669
            self.sflowButton.deselect()
670
        rowCount+=1
671

    
672
        # Selection of switch type
673
        Label(self.leftfieldFrame, text="Switch Type:").grid(row=rowCount, sticky=E)
674
        self.switchType = StringVar(self.leftfieldFrame)
675
        self.switchTypeMenu = OptionMenu(self.leftfieldFrame, self.switchType, "Default", "Open vSwitch Kernel Mode", "Indigo Virtual Switch", "Userspace Switch", "Userspace Switch inNamespace")
676
        self.switchTypeMenu.grid(row=rowCount, column=1, sticky=W)
677
        if 'switchType' in self.prefValues:
678
            switchTypePref = self.prefValues['switchType']
679
            if switchTypePref == 'ivs':
680
                self.switchType.set("Indigo Virtual Switch")
681
            elif switchTypePref == 'userns':
682
                self.switchType.set("Userspace Switch inNamespace")
683
            elif switchTypePref == 'user':
684
                self.switchType.set("Userspace Switch")
685
            elif switchTypePref == 'ovs':
686
                self.switchType.set("Open vSwitch Kernel Mode")
687
            else:
688
                self.switchType.set("Default")
689
        else:
690
            self.switchType.set("Default")
691
        rowCount+=1
692

    
693
        # Field for Switch IP
694
        Label(self.leftfieldFrame, text="IP Address:").grid(row=rowCount, sticky=E)
695
        self.ipEntry = Entry(self.leftfieldFrame)
696
        self.ipEntry.grid(row=rowCount, column=1)
697
        if 'switchIP' in self.prefValues:
698
            self.ipEntry.insert(0, self.prefValues['switchIP'])
699
        rowCount+=1
700

    
701
        # Field for DPCTL port
702
        Label(self.leftfieldFrame, text="DPCTL port:").grid(row=rowCount, sticky=E)
703
        self.dpctlEntry = Entry(self.leftfieldFrame)
704
        self.dpctlEntry.grid(row=rowCount, column=1)
705
        if 'dpctl' in self.prefValues:
706
            self.dpctlEntry.insert(0, self.prefValues['dpctl'])
707
        rowCount+=1
708

    
709
        # External Interfaces
710
        Label(self.rightfieldFrame, text="External Interface:").grid(row=0, sticky=E)
711
        self.b = Button( self.rightfieldFrame, text='Add', command=self.addInterface)
712
        self.b.grid(row=0, column=1)
713

    
714
        self.interfaceFrame = VerticalScrolledTable(self.rightfieldFrame, rows=0, columns=1, title='External Interfaces')
715
        self.interfaceFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
716
        self.tableFrame = self.interfaceFrame.interior
717

    
718
        # Add defined interfaces
719
        for externalInterface in externalInterfaces:
720
            self.tableFrame.addRow(value=[externalInterface])
721

    
722
        self.commandFrame = Frame(self.rootFrame)
723
        self.commandFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
724
        self.commandFrame.columnconfigure(1, weight=1)
725
        # Start command
726
        Label(self.commandFrame, text="Start Command:").grid(row=0, column=0, sticky=W)
727
        self.startEntry = Entry(self.commandFrame)
728
        self.startEntry.grid(row=0, column=1,  sticky='nsew')
729
        if 'startCommand' in self.prefValues:
730
            self.startEntry.insert(0, str(self.prefValues['startCommand']))
731
        # Stop command
732
        Label(self.commandFrame, text="Stop Command:").grid(row=1, column=0, sticky=W)
733
        self.stopEntry = Entry(self.commandFrame)
734
        self.stopEntry.grid(row=1, column=1, sticky='nsew')
735
        if 'stopCommand' in self.prefValues:
736
            self.stopEntry.insert(0, str(self.prefValues['stopCommand']))
737

    
738
    def addInterface( self ):
739
        self.tableFrame.addRow()
740

    
741
    def defaultDpid( self ,name):
742
        "Derive dpid from switch name, s1 -> 1"
743
        try:
744
            dpid = int( re.findall( r'\d+', name )[ 0 ] )
745
            dpid = hex( dpid )[ 2: ]
746
            return dpid
747
        except IndexError:
748
            return None
749
            #raise Exception( 'Unable to derive default datapath ID - '
750
            #                 'please either specify a dpid or use a '
751
            #                 'canonical switch name such as s23.' )
752

    
753
    def apply(self):
754
        externalInterfaces = []
755
        for row in range(self.tableFrame.rows):
756
            #print 'Interface is ' + self.tableFrame.get(row, 0)
757
            if len(self.tableFrame.get(row, 0)) > 0:
758
                externalInterfaces.append(self.tableFrame.get(row, 0))
759

    
760
        dpid = self.dpidEntry.get()
761
        if (self.defaultDpid(self.hostnameEntry.get()) is None
762
           and len(dpid) == 0):
763
            showerror(title="Error",
764
                          message= 'Unable to derive default datapath ID - '
765
                             'please either specify a DPID or use a '
766
                             'canonical switch name such as s23.' )
767

    
768

    
769
        results = {'externalInterfaces':externalInterfaces,
770
                   'hostname':self.hostnameEntry.get(),
771
                   'dpid':dpid,
772
                   'startCommand':self.startEntry.get(),
773
                   'stopCommand':self.stopEntry.get(),
774
                   'sflow':str(self.sflow.get()),
775
                   'netflow':str(self.nflow.get()),
776
                   'dpctl':self.dpctlEntry.get(),
777
                   'switchIP':self.ipEntry.get()}
778
        sw = self.switchType.get()
779
        if sw == 'Indigo Virtual Switch':
780
            results['switchType'] = 'ivs'
781
            if StrictVersion(MININET_VERSION) < StrictVersion('2.1'):
782
                self.ovsOk = False
783
                showerror(title="Error",
784
                          message='MiniNet version 2.1+ required. You have '+VERSION+'.')
785
        elif sw == 'Userspace Switch inNamespace':
786
            results['switchType'] = 'userns'
787
        elif sw == 'Userspace Switch':
788
            results['switchType'] = 'user'
789
        elif sw == 'Open vSwitch Kernel Mode':
790
            results['switchType'] = 'ovs'
791
        else:
792
            results['switchType'] = 'default'
793
        self.result = results
794

    
795

    
796
class VerticalScrolledTable(LabelFrame):
797
    """A pure Tkinter scrollable frame that actually works!
798

799
    * Use the 'interior' attribute to place widgets inside the scrollable frame
800
    * Construct and pack/place/grid normally
801
    * This frame only allows vertical scrolling
802

803
    """
804
    def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
805
        LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
806

    
807
        # create a canvas object and a vertical scrollbar for scrolling it
808
        vscrollbar = Scrollbar(self, orient=VERTICAL)
809
        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
810
        canvas = Canvas(self, bd=0, highlightthickness=0,
811
                        yscrollcommand=vscrollbar.set)
812
        canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
813
        vscrollbar.config(command=canvas.yview)
814

    
815
        # reset the view
816
        canvas.xview_moveto(0)
817
        canvas.yview_moveto(0)
818

    
819
        # create a frame inside the canvas which will be scrolled with it
820
        self.interior = interior = TableFrame(canvas, rows=rows, columns=columns)
821
        interior_id = canvas.create_window(0, 0, window=interior,
822
                                           anchor=NW)
823

    
824
        # track changes to the canvas and frame width and sync them,
825
        # also updating the scrollbar
826
        def _configure_interior(event):
827
        # update the scrollbars to match the size of the inner frame
828
            size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
829
            canvas.config(scrollregion="0 0 %s %s" % size)
830
            if interior.winfo_reqwidth() != canvas.winfo_width():
831
            # update the canvas's width to fit the inner frame
832
                canvas.config(width=interior.winfo_reqwidth())
833
        interior.bind('<Configure>', _configure_interior)
834

    
835
        def _configure_canvas(event):
836
            if interior.winfo_reqwidth() != canvas.winfo_width():
837
                # update the inner frame's width to fill the canvas
838
                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
839
        canvas.bind('<Configure>', _configure_canvas)
840

    
841
        return
842

    
843
class TableFrame(Frame):
844
    def __init__(self, parent, rows=2, columns=2):
845

    
846
        Frame.__init__(self, parent, background="black")
847
        self._widgets = []
848
        self.rows = rows
849
        self.columns = columns
850
        for row in range(rows):
851
            current_row = []
852
            for column in range(columns):
853
                label = Entry(self, borderwidth=0)
854
                label.grid(row=row, column=column, sticky="wens", padx=1, pady=1)
855
                current_row.append(label)
856
            self._widgets.append(current_row)
857

    
858
    def set(self, row, column, value):
859
        widget = self._widgets[row][column]
860
        widget.insert(0, value)
861

    
862
    def get(self, row, column):
863
        widget = self._widgets[row][column]
864
        return widget.get()
865

    
866
    def addRow( self, value=None, readonly=False ):
867
        #print "Adding row " + str(self.rows +1)
868
        current_row = []
869
        for column in range(self.columns):
870
            label = Entry(self, borderwidth=0)
871
            label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
872
            if value is not None:
873
                label.insert(0, value[column])
874
            if readonly == True:
875
                label.configure(state='readonly')
876
            current_row.append(label)
877
        self._widgets.append(current_row)
878
        self.update_idletasks()
879
        self.rows += 1
880

    
881
class LinkDialog(tkSimpleDialog.Dialog):
882

    
883
    def __init__(self, parent, title, linkDefaults):
884

    
885
        self.linkValues = linkDefaults
886

    
887
        tkSimpleDialog.Dialog.__init__(self, parent, title)
888

    
889
    def body(self, master):
890

    
891
        self.var = StringVar(master)
892
        Label(master, text="Bandwidth:").grid(row=0, sticky=E)
893
        self.e1 = Entry(master)
894
        self.e1.grid(row=0, column=1)
895
        Label(master, text="Mbit").grid(row=0, column=2, sticky=W)
896
        if 'bw' in self.linkValues:
897
            self.e1.insert(0,str(self.linkValues['bw']))
898

    
899
        Label(master, text="Delay:").grid(row=1, sticky=E)
900
        self.e2 = Entry(master)
901
        self.e2.grid(row=1, column=1)
902
        if 'delay' in self.linkValues:
903
            self.e2.insert(0, self.linkValues['delay'])
904

    
905
        Label(master, text="Loss:").grid(row=2, sticky=E)
906
        self.e3 = Entry(master)
907
        self.e3.grid(row=2, column=1)
908
        Label(master, text="%").grid(row=2, column=2, sticky=W)
909
        if 'loss' in self.linkValues:
910
            self.e3.insert(0, str(self.linkValues['loss']))
911

    
912
        Label(master, text="Max Queue size:").grid(row=3, sticky=E)
913
        self.e4 = Entry(master)
914
        self.e4.grid(row=3, column=1)
915
        if 'max_queue_size' in self.linkValues:
916
            self.e4.insert(0, str(self.linkValues['max_queue_size']))
917

    
918
        Label(master, text="Jitter:").grid(row=4, sticky=E)
919
        self.e5 = Entry(master)
920
        self.e5.grid(row=4, column=1)
921
        if 'jitter' in self.linkValues:
922
            self.e5.insert(0, self.linkValues['jitter'])
923

    
924
        Label(master, text="Speedup:").grid(row=5, sticky=E)
925
        self.e6 = Entry(master)
926
        self.e6.grid(row=5, column=1)
927
        if 'speedup' in self.linkValues:
928
            self.e6.insert(0, str(self.linkValues['speedup']))
929

    
930
        return self.e1 # initial focus
931

    
932
    def apply(self):
933
        self.result = {}
934
        if len(self.e1.get()) > 0:
935
            self.result['bw'] = int(self.e1.get())
936
        if len(self.e2.get()) > 0:
937
            self.result['delay'] = self.e2.get()
938
        if len(self.e3.get()) > 0:
939
            self.result['loss'] = int(self.e3.get())
940
        if len(self.e4.get()) > 0:
941
            self.result['max_queue_size'] = int(self.e4.get())
942
        if len(self.e5.get()) > 0:
943
            self.result['jitter'] = self.e5.get()
944
        if len(self.e6.get()) > 0:
945
            self.result['speedup'] = int(self.e6.get())
946

    
947
class ControllerDialog(tkSimpleDialog.Dialog):
948

    
949
    def __init__(self, parent, title, ctrlrDefaults=None):
950

    
951
        if ctrlrDefaults:
952
            self.ctrlrValues = ctrlrDefaults
953

    
954
        tkSimpleDialog.Dialog.__init__(self, parent, title)
955

    
956
    def body(self, master):
957

    
958
        self.var = StringVar(master)
959
        self.protcolvar = StringVar(master)
960

    
961
        rowCount=0
962
        # Field for Hostname
963
        Label(master, text="Name:").grid(row=rowCount, sticky=E)
964
        self.hostnameEntry = Entry(master)
965
        self.hostnameEntry.grid(row=rowCount, column=1)
966
        self.hostnameEntry.insert(0, self.ctrlrValues['hostname'])
967
        rowCount+=1
968

    
969
        # Field for Remove Controller Port
970
        Label(master, text="Controller Port:").grid(row=rowCount, sticky=E)
971
        self.e2 = Entry(master)
972
        self.e2.grid(row=rowCount, column=1)
973
        self.e2.insert(0, self.ctrlrValues['remotePort'])
974
        rowCount+=1
975

    
976
        # Field for Controller Type
977
        Label(master, text="Controller Type:").grid(row=rowCount, sticky=E)
978
        controllerType = self.ctrlrValues['controllerType']
979
        self.o1 = OptionMenu(master, self.var, "Remote Controller", "In-Band Controller", "OpenFlow Reference", "OVS Controller")
980
        self.o1.grid(row=rowCount, column=1, sticky=W)
981
        if controllerType == 'ref':
982
            self.var.set("OpenFlow Reference")
983
        elif controllerType == 'inband':
984
            self.var.set("In-Band Controller")
985
        elif controllerType == 'remote':
986
            self.var.set("Remote Controller")
987
        else:
988
            self.var.set("OVS Controller")
989
        rowCount+=1
990

    
991
        # Field for Controller Protcol
992
        Label(master, text="Protocol:").grid(row=rowCount, sticky=E)
993
        if 'controllerProtocol' in self.ctrlrValues:
994
            controllerProtocol = self.ctrlrValues['controllerProtocol']
995
        else:
996
            controllerProtocol = 'tcp'
997
        self.protcol = OptionMenu(master, self.protcolvar, "TCP", "SSL")
998
        self.protcol.grid(row=rowCount, column=1, sticky=W)
999
        if controllerProtocol == 'ssl':
1000
            self.protcolvar.set("SSL")
1001
        else:
1002
            self.protcolvar.set("TCP")
1003
        rowCount+=1
1004

    
1005
        # Field for Remove Controller IP
1006
        remoteFrame= LabelFrame(master, text='Remote/In-Band Controller', padx=5, pady=5)
1007
        remoteFrame.grid(row=rowCount, column=0, columnspan=2, sticky=W)
1008

    
1009
        Label(remoteFrame, text="IP Address:").grid(row=0, sticky=E)
1010
        self.e1 = Entry(remoteFrame)
1011
        self.e1.grid(row=0, column=1)
1012
        self.e1.insert(0, self.ctrlrValues['remoteIP'])
1013
        rowCount+=1
1014

    
1015
        return self.hostnameEntry # initial focus
1016

    
1017
    def apply(self):
1018
        self.result = { 'hostname': self.hostnameEntry.get(),
1019
                        'remoteIP': self.e1.get(),
1020
                        'remotePort': int(self.e2.get())}
1021

    
1022
        controllerType = self.var.get()
1023
        if controllerType == 'Remote Controller':
1024
            self.result['controllerType'] = 'remote'
1025
        elif controllerType == 'In-Band Controller':
1026
            self.result['controllerType'] = 'inband'
1027
        elif controllerType == 'OpenFlow Reference':
1028
            self.result['controllerType'] = 'ref'
1029
        else:
1030
            self.result['controllerType'] = 'ovsc'
1031
        controllerProtocol = self.protcolvar.get()
1032
        if controllerProtocol == 'SSL':
1033
            self.result['controllerProtocol'] = 'ssl'
1034
        else:
1035
            self.result['controllerProtocol'] = 'tcp'
1036

    
1037
class ToolTip(object):
1038

    
1039
    def __init__(self, widget):
1040
        self.widget = widget
1041
        self.tipwindow = None
1042
        self.id = None
1043
        self.x = self.y = 0
1044

    
1045
    def showtip(self, text):
1046
        "Display text in tooltip window"
1047
        self.text = text
1048
        if self.tipwindow or not self.text:
1049
            return
1050
        x, y, cx, cy = self.widget.bbox("insert")
1051
        x = x + self.widget.winfo_rootx() + 27
1052
        y = y + cy + self.widget.winfo_rooty() +27
1053
        self.tipwindow = tw = Toplevel(self.widget)
1054
        tw.wm_overrideredirect(1)
1055
        tw.wm_geometry("+%d+%d" % (x, y))
1056
        try:
1057
            # For Mac OS
1058
            tw.tk.call("::tk::unsupported::MacWindowStyle",
1059
                       "style", tw._w,
1060
                       "help", "noActivates")
1061
        except TclError:
1062
            pass
1063
        label = Label(tw, text=self.text, justify=LEFT,
1064
                      background="#ffffe0", relief=SOLID, borderwidth=1,
1065
                      font=("tahoma", "8", "normal"))
1066
        label.pack(ipadx=1)
1067

    
1068
    def hidetip(self):
1069
        tw = self.tipwindow
1070
        self.tipwindow = None
1071
        if tw:
1072
            tw.destroy()
1073

    
1074
class MiniEdit( Frame ):
1075

    
1076
    "A simple network editor for Mininet."
1077

    
1078
    def __init__( self, parent=None, cheight=600, cwidth=1000 ):
1079

    
1080
        self.defaultIpBase='10.0.0.0/8'
1081

    
1082
        self.nflowDefaults = {'nflowTarget':'',
1083
                              'nflowTimeout':'600',
1084
                              'nflowAddId':'0'}
1085
        self.sflowDefaults = {'sflowTarget':'',
1086
                              'sflowSampling':'400',
1087
                              'sflowHeader':'128',
1088
                              'sflowPolling':'30'}
1089

    
1090
        self.appPrefs={
1091
            "ipBase": self.defaultIpBase,
1092
            "startCLI": "0",
1093
            "terminalType": 'xterm',
1094
            "switchType": 'ovs',
1095
            "dpctl": '',
1096
            'sflow':self.sflowDefaults,
1097
            'netflow':self.nflowDefaults,
1098
            'openFlowVersions':{'ovsOf10':'1',
1099
                                'ovsOf11':'0',
1100
                                'ovsOf12':'0',
1101
                                'ovsOf13':'0'}
1102

    
1103
        }
1104

    
1105

    
1106
        Frame.__init__( self, parent )
1107
        self.action = None
1108
        self.appName = 'MiniEdit'
1109
        self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
1110

    
1111
        # Style
1112
        self.font = ( 'Geneva', 9 )
1113
        self.smallFont = ( 'Geneva', 7 )
1114
        self.bg = 'white'
1115

    
1116
        # Title
1117
        self.top = self.winfo_toplevel()
1118
        self.top.title( self.appName )
1119

    
1120
        # Menu bar
1121
        self.createMenubar()
1122

    
1123
        # Editing canvas
1124
        self.cheight, self.cwidth = cheight, cwidth
1125
        self.cframe, self.canvas = self.createCanvas()
1126

    
1127
        # Toolbar
1128
        self.controllers = {}
1129

    
1130
        # Toolbar
1131
        self.images = miniEditImages()
1132
        self.buttons = {}
1133
        self.active = None
1134
        self.tools = ( 'Select', 'Host', 'Switch', 'LegacySwitch', 'LegacyRouter', 'NetLink', 'Controller' )
1135
        self.customColors = { 'Switch': 'darkGreen', 'Host': 'blue' }
1136
        self.toolbar = self.createToolbar()
1137

    
1138
        # Layout
1139
        self.toolbar.grid( column=0, row=0, sticky='nsew')
1140
        self.cframe.grid( column=1, row=0 )
1141
        self.columnconfigure( 1, weight=1 )
1142
        self.rowconfigure( 0, weight=1 )
1143
        self.pack( expand=True, fill='both' )
1144

    
1145
        # About box
1146
        self.aboutBox = None
1147

    
1148
        # Initialize node data
1149
        self.nodeBindings = self.createNodeBindings()
1150
        self.nodePrefixes = { 'LegacyRouter': 'r', 'LegacySwitch': 's', 'Switch': 's', 'Host': 'h' , 'Controller': 'c'}
1151
        self.widgetToItem = {}
1152
        self.itemToWidget = {}
1153

    
1154
        # Initialize link tool
1155
        self.link = self.linkWidget = None
1156

    
1157
        # Selection support
1158
        self.selection = None
1159

    
1160
        # Keyboard bindings
1161
        self.bind( '<Control-q>', lambda event: self.quit() )
1162
        self.bind( '<KeyPress-Delete>', self.deleteSelection )
1163
        self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
1164
        self.focus()
1165

    
1166
        self.hostPopup = Menu(self.top, tearoff=0)
1167
        self.hostPopup.add_command(label='Host Options', font=self.font)
1168
        self.hostPopup.add_separator()
1169
        self.hostPopup.add_command(label='Properties', font=self.font, command=self.hostDetails )
1170

    
1171
        self.hostRunPopup = Menu(self.top, tearoff=0)
1172
        self.hostRunPopup.add_command(label='Host Options', font=self.font)
1173
        self.hostRunPopup.add_separator()
1174
        self.hostRunPopup.add_command(label='Terminal', font=self.font, command=self.xterm )
1175

    
1176
        self.legacyRouterRunPopup = Menu(self.top, tearoff=0)
1177
        self.legacyRouterRunPopup.add_command(label='Router Options', font=self.font)
1178
        self.legacyRouterRunPopup.add_separator()
1179
        self.legacyRouterRunPopup.add_command(label='Terminal', font=self.font, command=self.xterm )
1180

    
1181
        self.switchPopup = Menu(self.top, tearoff=0)
1182
        self.switchPopup.add_command(label='Switch Options', font=self.font)
1183
        self.switchPopup.add_separator()
1184
        self.switchPopup.add_command(label='Properties', font=self.font, command=self.switchDetails )
1185

    
1186
        self.switchRunPopup = Menu(self.top, tearoff=0)
1187
        self.switchRunPopup.add_command(label='Switch Options', font=self.font)
1188
        self.switchRunPopup.add_separator()
1189
        self.switchRunPopup.add_command(label='List bridge details', font=self.font, command=self.listBridge )
1190

    
1191
        self.linkPopup = Menu(self.top, tearoff=0)
1192
        self.linkPopup.add_command(label='Link Options', font=self.font)
1193
        self.linkPopup.add_separator()
1194
        self.linkPopup.add_command(label='Properties', font=self.font, command=self.linkDetails )
1195

    
1196
        self.linkRunPopup = Menu(self.top, tearoff=0)
1197
        self.linkRunPopup.add_command(label='Link Options', font=self.font)
1198
        self.linkRunPopup.add_separator()
1199
        self.linkRunPopup.add_command(label='Link Up', font=self.font, command=self.linkUp )
1200
        self.linkRunPopup.add_command(label='Link Down', font=self.font, command=self.linkDown )
1201

    
1202
        self.controllerPopup = Menu(self.top, tearoff=0)
1203
        self.controllerPopup.add_command(label='Controller Options', font=self.font)
1204
        self.controllerPopup.add_separator()
1205
        self.controllerPopup.add_command(label='Properties', font=self.font, command=self.controllerDetails )
1206

    
1207

    
1208
        # Event handling initalization
1209
        self.linkx = self.linky = self.linkItem = None
1210
        self.lastSelection = None
1211

    
1212
        # Model initialization
1213
        self.links = {}
1214
        self.hostOpts = {}
1215
        self.switchOpts = {}
1216
        self.hostCount = 0
1217
        self.switchCount = 0
1218
        self.controllerCount = 0
1219
        self.net = None
1220

    
1221
        # Close window gracefully
1222
        Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
1223

    
1224
    def quit( self ):
1225
        "Stop our network, if any, then quit."
1226
        self.stop()
1227
        Frame.quit( self )
1228

    
1229
    def createMenubar( self ):
1230
        "Create our menu bar."
1231

    
1232
        font = self.font
1233

    
1234
        mbar = Menu( self.top, font=font )
1235
        self.top.configure( menu=mbar )
1236

    
1237

    
1238
        fileMenu = Menu( mbar, tearoff=False )
1239
        mbar.add_cascade( label="File", font=font, menu=fileMenu )
1240
        fileMenu.add_command( label="New", font=font, command=self.newTopology )
1241
        fileMenu.add_command( label="Open", font=font, command=self.loadTopology )
1242
        fileMenu.add_command( label="Save", font=font, command=self.saveTopology )
1243
        fileMenu.add_command( label="Export Level 2 Script", font=font, command=self.exportScript )
1244
        fileMenu.add_separator()
1245
        fileMenu.add_command( label='Quit', command=self.quit, font=font )
1246

    
1247
        editMenu = Menu( mbar, tearoff=False )
1248
        mbar.add_cascade( label="Edit", font=font, menu=editMenu )
1249
        editMenu.add_command( label="Cut", font=font,
1250
                              command=lambda: self.deleteSelection( None ) )
1251
        editMenu.add_command( label="Preferences", font=font, command=self.prefDetails)
1252

    
1253
        runMenu = Menu( mbar, tearoff=False )
1254
        mbar.add_cascade( label="Run", font=font, menu=runMenu )
1255
        runMenu.add_command( label="Run", font=font, command=self.doRun )
1256
        runMenu.add_command( label="Stop", font=font, command=self.doStop )
1257
        fileMenu.add_separator()
1258
        runMenu.add_command( label='Show OVS Summary', font=font, command=self.ovsShow )
1259
        runMenu.add_command( label='Root Terminal', font=font, command=self.rootTerminal )
1260

    
1261
        # Application menu
1262
        appMenu = Menu( mbar, tearoff=False )
1263
        mbar.add_cascade( label="Help", font=font, menu=appMenu )
1264
        appMenu.add_command( label='About MiniEdit', command=self.about,
1265
                             font=font)
1266
    # Canvas
1267

    
1268
    def createCanvas( self ):
1269
        "Create and return our scrolling canvas frame."
1270
        f = Frame( self )
1271

    
1272
        canvas = Canvas( f, width=self.cwidth, height=self.cheight,
1273
                         bg=self.bg )
1274

    
1275
        # Scroll bars
1276
        xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
1277
        ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
1278
        canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
1279

    
1280
        # Resize box
1281
        resize = Label( f, bg='white' )
1282

    
1283
        # Layout
1284
        canvas.grid( row=0, column=1, sticky='nsew')
1285
        ybar.grid( row=0, column=2, sticky='ns')
1286
        xbar.grid( row=1, column=1, sticky='ew' )
1287
        resize.grid( row=1, column=2, sticky='nsew' )
1288

    
1289
        # Resize behavior
1290
        f.rowconfigure( 0, weight=1 )
1291
        f.columnconfigure( 1, weight=1 )
1292
        f.grid( row=0, column=0, sticky='nsew' )
1293
        f.bind( '<Configure>', lambda event: self.updateScrollRegion() )
1294

    
1295
        # Mouse bindings
1296
        canvas.bind( '<ButtonPress-1>', self.clickCanvas )
1297
        canvas.bind( '<B1-Motion>', self.dragCanvas )
1298
        canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
1299

    
1300
        return f, canvas
1301

    
1302
    def updateScrollRegion( self ):
1303
        "Update canvas scroll region to hold everything."
1304
        bbox = self.canvas.bbox( 'all' )
1305
        if bbox is not None:
1306
            self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
1307
                                   bbox[ 3 ] ) )
1308

    
1309
    def canvasx( self, x_root ):
1310
        "Convert root x coordinate to canvas coordinate."
1311
        c = self.canvas
1312
        return c.canvasx( x_root ) - c.winfo_rootx()
1313

    
1314
    def canvasy( self, y_root ):
1315
        "Convert root y coordinate to canvas coordinate."
1316
        c = self.canvas
1317
        return c.canvasy( y_root ) - c.winfo_rooty()
1318

    
1319
    # Toolbar
1320

    
1321
    def activate( self, toolName ):
1322
        "Activate a tool and press its button."
1323
        # Adjust button appearance
1324
        if self.active:
1325
            self.buttons[ self.active ].configure( relief='raised' )
1326
        self.buttons[ toolName ].configure( relief='sunken' )
1327
        # Activate dynamic bindings
1328
        self.active = toolName
1329

    
1330

    
1331
    def createToolTip(self, widget, text):
1332
        toolTip = ToolTip(widget)
1333
        def enter(event):
1334
            toolTip.showtip(text)
1335
        def leave(event):
1336
            toolTip.hidetip()
1337
        widget.bind('<Enter>', enter)
1338
        widget.bind('<Leave>', leave)
1339

    
1340
    def createToolbar( self ):
1341
        "Create and return our toolbar frame."
1342

    
1343
        toolbar = Frame( self )
1344

    
1345
        # Tools
1346
        for tool in self.tools:
1347
            cmd = ( lambda t=tool: self.activate( t ) )
1348
            b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
1349
            if tool in self.images:
1350
                b.config( height=35, image=self.images[ tool ] )
1351
                self.createToolTip(b, str(tool))
1352
                # b.config( compound='top' )
1353
            b.pack( fill='x' )
1354
            self.buttons[ tool ] = b
1355
        self.activate( self.tools[ 0 ] )
1356

    
1357
        # Spacer
1358
        Label( toolbar, text='' ).pack()
1359

    
1360
        # Commands
1361
        for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
1362
            doCmd = getattr( self, 'do' + cmd )
1363
            b = Button( toolbar, text=cmd, font=self.smallFont,
1364
                        fg=color, command=doCmd )
1365
            b.pack( fill='x', side='bottom' )
1366

    
1367
        return toolbar
1368

    
1369
    def doRun( self ):
1370
        "Run command."
1371
        self.activate( 'Select' )
1372
        for tool in self.tools:
1373
            self.buttons[ tool ].config( state='disabled' )
1374
        self.start()
1375

    
1376
    def doStop( self ):
1377
        "Stop command."
1378
        self.stop()
1379
        for tool in self.tools:
1380
            self.buttons[ tool ].config( state='normal' )
1381

    
1382
    def addNode( self, node, nodeNum, x, y, name=None):
1383
        "Add a new node to our canvas."
1384
        if 'Switch' == node:
1385
            self.switchCount += 1
1386
        if 'Host' == node:
1387
            self.hostCount += 1
1388
        if 'Controller' == node:
1389
            self.controllerCount += 1
1390
        if name is None:
1391
            name = self.nodePrefixes[ node ] + nodeNum
1392
        self.addNamedNode(node, name, x, y)
1393

    
1394
    def addNamedNode( self, node, name, x, y):
1395
        "Add a new node to our canvas."
1396
        icon = self.nodeIcon( node, name )
1397
        item = self.canvas.create_window( x, y, anchor='c', window=icon,
1398
                                          tags=node )
1399
        self.widgetToItem[ icon ] = item
1400
        self.itemToWidget[ item ] = icon
1401
        icon.links = {}
1402

    
1403
    def convertJsonUnicode(self, input):
1404
        "Some part of Mininet don't like Unicode"
1405
        if isinstance(input, dict):
1406
            return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
1407
        elif isinstance(input, list):
1408
            return [self.convertJsonUnicode(element) for element in input]
1409
        elif isinstance(input, unicode):
1410
            return input.encode('utf-8')
1411
        else:
1412
            return input
1413

    
1414
    def loadTopology( self ):
1415
        "Load command."
1416
        c = self.canvas
1417

    
1418
        myFormats = [
1419
            ('Mininet Topology','*.mn'),
1420
            ('All Files','*'),
1421
        ]
1422
        f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
1423
        if f == None:
1424
            return
1425
        self.newTopology()
1426
        loadedTopology = self.convertJsonUnicode(json.load(f))
1427

    
1428
        # Load application preferences
1429
        if 'application' in loadedTopology:
1430
            self.appPrefs = dict(self.appPrefs.items() + loadedTopology['application'].items())
1431
            if "ovsOf10" not in self.appPrefs["openFlowVersions"]:
1432
                self.appPrefs["openFlowVersions"]["ovsOf10"] = '0'
1433
            if "ovsOf11" not in self.appPrefs["openFlowVersions"]:
1434
                self.appPrefs["openFlowVersions"]["ovsOf11"] = '0'
1435
            if "ovsOf12" not in self.appPrefs["openFlowVersions"]:
1436
                self.appPrefs["openFlowVersions"]["ovsOf12"] = '0'
1437
            if "ovsOf13" not in self.appPrefs["openFlowVersions"]:
1438
                self.appPrefs["openFlowVersions"]["ovsOf13"] = '0'
1439
            if "sflow" not in self.appPrefs:
1440
                self.appPrefs["sflow"] = self.sflowDefaults
1441
            if "netflow" not in self.appPrefs:
1442
                self.appPrefs["netflow"] = self.nflowDefaults
1443

    
1444
        # Load controllers
1445
        if 'controllers' in loadedTopology:
1446
            if loadedTopology['version'] == '1':
1447
                # This is old location of controller info
1448
                hostname = 'c0'
1449
                self.controllers = {}
1450
                self.controllers[hostname] = loadedTopology['controllers']['c0']
1451
                self.controllers[hostname]['hostname'] = hostname
1452
                self.addNode('Controller', 0, float(30), float(30), name=hostname)
1453
                icon = self.findWidgetByName(hostname)
1454
                icon.bind('<Button-3>', self.do_controllerPopup )
1455
            else:
1456
                controllers = loadedTopology['controllers']
1457
                for controller in controllers:
1458
                    hostname = controller['opts']['hostname']
1459
                    x = controller['x']
1460
                    y = controller['y']
1461
                    self.addNode('Controller', 0, float(x), float(y), name=hostname)
1462
                    self.controllers[hostname] = controller['opts']
1463
                    icon = self.findWidgetByName(hostname)
1464
                    icon.bind('<Button-3>', self.do_controllerPopup )
1465

    
1466

    
1467
        # Load hosts
1468
        hosts = loadedTopology['hosts']
1469
        for host in hosts:
1470
            nodeNum = host['number']
1471
            hostname = 'h'+nodeNum
1472
            if 'hostname' in host['opts']:
1473
                hostname = host['opts']['hostname']
1474
            else:
1475
                host['opts']['hostname'] = hostname
1476
            if 'nodeNum' not in host['opts']:
1477
                host['opts']['nodeNum'] = int(nodeNum)
1478
            x = host['x']
1479
            y = host['y']
1480
            self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
1481

    
1482
            # Fix JSON converting tuple to list when saving
1483
            if 'privateDirectory' in host['opts']:
1484
                newDirList = []
1485
                for privateDir in host['opts']['privateDirectory']:
1486
                    if isinstance( privateDir, list ):
1487
                        newDirList.append((privateDir[0],privateDir[1]))
1488
                    else:
1489
                        newDirList.append(privateDir)
1490
                host['opts']['privateDirectory'] = newDirList
1491
            self.hostOpts[hostname] = host['opts']
1492
            icon = self.findWidgetByName(hostname)
1493
            icon.bind('<Button-3>', self.do_hostPopup )
1494

    
1495
        # Load switches
1496
        switches = loadedTopology['switches']
1497
        for switch in switches:
1498
            nodeNum = switch['number']
1499
            hostname = 's'+nodeNum
1500
            if 'controllers' not in switch['opts']:
1501
                switch['opts']['controllers'] = []
1502
            if 'switchType' not in switch['opts']:
1503
                switch['opts']['switchType'] = 'default'
1504
            if 'hostname' in switch['opts']:
1505
                hostname = switch['opts']['hostname']
1506
            else:
1507
                switch['opts']['hostname'] = hostname
1508
            if 'nodeNum' not in switch['opts']:
1509
                switch['opts']['nodeNum'] = int(nodeNum)
1510
            x = switch['x']
1511
            y = switch['y']
1512
            if switch['opts']['switchType'] == "legacyRouter":
1513
                self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
1514
                icon = self.findWidgetByName(hostname)
1515
                icon.bind('<Button-3>', self.do_legacyRouterPopup )
1516
            elif switch['opts']['switchType'] == "legacySwitch":
1517
                self.addNode('LegacySwitch', nodeNum, float(x), float(y), name=hostname)
1518
                icon = self.findWidgetByName(hostname)
1519
                icon.bind('<Button-3>', self.do_legacySwitchPopup )
1520
            else:
1521
                self.addNode('Switch', nodeNum, float(x), float(y), name=hostname)
1522
                icon = self.findWidgetByName(hostname)
1523
                icon.bind('<Button-3>', self.do_switchPopup )
1524
            self.switchOpts[hostname] = switch['opts']
1525

    
1526
            # create links to controllers
1527
            if int(loadedTopology['version']) > 1:
1528
                controllers = self.switchOpts[hostname]['controllers']
1529
                for controller in controllers:
1530
                    dest = self.findWidgetByName(controller)
1531
                    dx, dy = self.canvas.coords( self.widgetToItem[ dest ] )
1532
                    self.link = self.canvas.create_line(float(x),
1533
                                                        float(y),
1534
                                                        dx,
1535
                                                        dy,
1536
                                                        width=4,
1537
                                                        fill='red',
1538
                                                        dash=(6, 4, 2, 4),
1539
                                                        tag='link' )
1540
                    c.itemconfig(self.link, tags=c.gettags(self.link)+('control',))
1541
                    self.addLink( icon, dest, linktype='control' )
1542
                    self.createControlLinkBindings()
1543
                    self.link = self.linkWidget = None
1544
            else:
1545
                dest = self.findWidgetByName('c0')
1546
                dx, dy = self.canvas.coords( self.widgetToItem[ dest ] )
1547
                self.link = self.canvas.create_line(float(x),
1548
                                                    float(y),
1549
                                                    dx,
1550
                                                    dy,
1551
                                                    width=4,
1552
                                                    fill='red',
1553
                                                    dash=(6, 4, 2, 4),
1554
                                                    tag='link' )
1555
                c.itemconfig(self.link, tags=c.gettags(self.link)+('control',))
1556
                self.addLink( icon, dest, linktype='control' )
1557
                self.createControlLinkBindings()
1558
                self.link = self.linkWidget = None
1559

    
1560
        # Load links
1561
        links = loadedTopology['links']
1562
        for link in links:
1563
            srcNode = link['src']
1564
            src = self.findWidgetByName(srcNode)
1565
            sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
1566

    
1567
            destNode = link['dest']
1568
            dest = self.findWidgetByName(destNode)
1569
            dx, dy = self.canvas.coords( self.widgetToItem[ dest]  )
1570

    
1571
            self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
1572
                                             fill='blue', tag='link' )
1573
            c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
1574
            self.addLink( src, dest, linkopts=link['opts'] )
1575
            self.createDataLinkBindings()
1576
            self.link = self.linkWidget = None
1577

    
1578
        f.close()
1579

    
1580
    def findWidgetByName( self, name ):
1581
        for widget in self.widgetToItem:
1582
            if name ==  widget[ 'text' ]:
1583
                return widget
1584

    
1585
    def newTopology( self ):
1586
        "New command."
1587
        for widget in self.widgetToItem.keys():
1588
            self.deleteItem( self.widgetToItem[ widget ] )
1589
        self.hostCount = 0
1590
        self.switchCount = 0
1591
        self.controllerCount = 0
1592
        self.links = {}
1593
        self.hostOpts = {}
1594
        self.switchOpts = {}
1595
        self.controllers = {}
1596
        self.appPrefs["ipBase"]= self.defaultIpBase
1597

    
1598
    def saveTopology( self ):
1599
        "Save command."
1600
        myFormats = [
1601
            ('Mininet Topology','*.mn'),
1602
            ('All Files','*'),
1603
        ]
1604

    
1605
        savingDictionary = {}
1606
        fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
1607
        if len(fileName ) > 0:
1608
            # Save Application preferences
1609
            savingDictionary['version'] = '2'
1610

    
1611
            # Save Switches and Hosts
1612
            hostsToSave = []
1613
            switchesToSave = []
1614
            controllersToSave = []
1615
            for widget in self.widgetToItem:
1616
                name = widget[ 'text' ]
1617
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1618
                x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
1619
                if 'Switch' in tags or 'LegacySwitch' in tags or 'LegacyRouter' in tags:
1620
                    nodeNum = self.switchOpts[name]['nodeNum']
1621
                    nodeToSave = {'number':str(nodeNum),
1622
                                  'x':str(x1),
1623
                                  'y':str(y1),
1624
                                  'opts':self.switchOpts[name] }
1625
                    switchesToSave.append(nodeToSave)
1626
                elif 'Host' in tags:
1627
                    nodeNum = self.hostOpts[name]['nodeNum']
1628
                    nodeToSave = {'number':str(nodeNum),
1629
                                  'x':str(x1),
1630
                                  'y':str(y1),
1631
                                  'opts':self.hostOpts[name] }
1632
                    hostsToSave.append(nodeToSave)
1633
                elif 'Controller' in tags:
1634
                    nodeToSave = {'x':str(x1),
1635
                                  'y':str(y1),
1636
                                  'opts':self.controllers[name] }
1637
                    controllersToSave.append(nodeToSave)
1638
                else:
1639
                    raise Exception( "Cannot create mystery node: " + name )
1640
            savingDictionary['hosts'] = hostsToSave
1641
            savingDictionary['switches'] = switchesToSave
1642
            savingDictionary['controllers'] = controllersToSave
1643

    
1644
            # Save Links
1645
            linksToSave = []
1646
            for link in self.links.values():
1647
                src = link['src']
1648
                dst = link['dest']
1649
                linkopts = link['linkOpts']
1650

    
1651
                srcName, dstName = src[ 'text' ], dst[ 'text' ]
1652
                linkToSave = {'src':srcName,
1653
                              'dest':dstName,
1654
                              'opts':linkopts}
1655
                if link['type'] == 'data':
1656
                    linksToSave.append(linkToSave)
1657
            savingDictionary['links'] = linksToSave
1658

    
1659
            # Save Application preferences
1660
            savingDictionary['application'] = self.appPrefs
1661

    
1662
            try:
1663
                f = open(fileName, 'wb')
1664
                f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
1665
            except Exception as er:
1666
                print er
1667
            finally:
1668
                f.close()
1669

    
1670
    def exportScript( self ):
1671
        "Export command."
1672
        myFormats = [
1673
            ('Mininet Custom Topology','*.py'),
1674
            ('All Files','*'),
1675
        ]
1676

    
1677
        fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
1678
        if len(fileName ) > 0:
1679
            #print "Now saving under %s" % fileName
1680
            f = open(fileName, 'wb')
1681

    
1682
            f.write("#!/usr/bin/python\n")
1683
            f.write("\n")
1684
            f.write("from mininet.net import Mininet\n")
1685
            f.write("from mininet.node import Controller, RemoteController, OVSController\n")
1686
            f.write("from mininet.node import CPULimitedHost, Host, Node\n")
1687
            f.write("from mininet.node import OVSKernelSwitch, UserSwitch\n")
1688
            if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
1689
                f.write("from mininet.node import IVSSwitch\n")
1690
            f.write("from mininet.cli import CLI\n")
1691
            f.write("from mininet.log import setLogLevel, info\n")
1692
            f.write("from mininet.link import TCLink, Intf\n")
1693
            f.write("from subprocess import call\n")
1694

    
1695
            inBandCtrl = False
1696
            for widget in self.widgetToItem:
1697
                name = widget[ 'text' ]
1698
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1699

    
1700
                if 'Controller' in tags:
1701
                    opts = self.controllers[name]
1702
                    controllerType = opts['controllerType']
1703
                    if controllerType == 'inband':
1704
                        inBandCtrl = True
1705

    
1706
            if inBandCtrl == True:
1707
                f.write("\n")
1708
                f.write("class InbandController( RemoteController ):\n")
1709
                f.write("\n")
1710
                f.write("    def checkListening( self ):\n")
1711
                f.write("        \"Overridden to do nothing.\"\n")
1712
                f.write("        return\n")
1713

    
1714
            f.write("\n")
1715
            f.write("def myNetwork():\n")
1716
            f.write("\n")
1717
            f.write("    net = Mininet( topo=None,\n")
1718
            if len(self.appPrefs['dpctl']) > 0:
1719
                f.write("                   listenPort="+self.appPrefs['dpctl']+",\n")
1720
            f.write("                   build=False,\n")
1721
            f.write("                   ipBase='"+self.appPrefs['ipBase']+"')\n")
1722
            f.write("\n")
1723
            f.write("    info( '*** Adding controller\\n' )\n")
1724
            for widget in self.widgetToItem:
1725
                name = widget[ 'text' ]
1726
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1727

    
1728
                if 'Controller' in tags:
1729
                    opts = self.controllers[name]
1730
                    controllerType = opts['controllerType']
1731
                    if 'controllerProtocol' in opts:
1732
                        controllerProtocol = opts['controllerProtocol']
1733
                    else:
1734
                        controllerProtocol = 'tcp'
1735
                    controllerIP = opts['remoteIP']
1736
                    controllerPort = opts['remotePort']
1737

    
1738

    
1739
                    f.write("    "+name+"=net.addController(name='"+name+"',\n")
1740

    
1741
                    if controllerType == 'remote':
1742
                        f.write("                      controller=RemoteController,\n")
1743
                        f.write("                      ip='"+controllerIP+"',\n")
1744
                    elif controllerType == 'inband':
1745
                        f.write("                      controller=InbandController,\n")
1746
                        f.write("                      ip='"+controllerIP+"',\n")
1747
                    elif controllerType == 'ovsc':
1748
                        f.write("                      controller=OVSController,\n")
1749
                    else:
1750
                        f.write("                      controller=Controller,\n")
1751

    
1752
                    f.write("                      protocol='"+controllerProtocol+"',\n")
1753
                    f.write("                      port="+str(controllerPort)+")\n")
1754
                    f.write("\n")
1755

    
1756
            # Save Switches and Hosts
1757
            f.write("    info( '*** Add switches\\n')\n")
1758
            for widget in self.widgetToItem:
1759
                name = widget[ 'text' ]
1760
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1761
                if 'LegacyRouter' in tags:
1762
                    f.write("    "+name+" = net.addHost('"+name+"', cls=Node, ip='0.0.0.0')\n")
1763
                    f.write("    "+name+".cmd('sysctl -w net.ipv4.ip_forward=1')\n")
1764
                if 'LegacySwitch' in tags:
1765
                    f.write("    "+name+" = net.addSwitch('"+name+"', cls=OVSKernelSwitch, failMode='standalone')\n")
1766
                if 'Switch' in tags:
1767
                    opts = self.switchOpts[name]
1768
                    nodeNum = opts['nodeNum']
1769
                    f.write("    "+name+" = net.addSwitch('"+name+"'")
1770
                    if opts['switchType'] == 'default':
1771
                        if self.appPrefs['switchType'] == 'ivs':
1772
                            f.write(", cls=IVSSwitch")
1773
                        elif self.appPrefs['switchType'] == 'user':
1774
                            f.write(", cls=UserSwitch")
1775
                        elif self.appPrefs['switchType'] == 'userns':
1776
                            f.write(", cls=UserSwitch, inNamespace=True")
1777
                        else:
1778
                            f.write(", cls=OVSKernelSwitch")
1779
                    elif opts['switchType'] == 'ivs':
1780
                        f.write(", cls=IVSSwitch")
1781
                    elif opts['switchType'] == 'user':
1782
                        f.write(", cls=UserSwitch")
1783
                    elif opts['switchType'] == 'userns':
1784
                        f.write(", cls=UserSwitch, inNamespace=True")
1785
                    else:
1786
                        f.write(", cls=OVSKernelSwitch")
1787
                    if 'dpctl' in opts:
1788
                        f.write(", listenPort="+opts['dpctl'])
1789
                    if 'dpid' in opts:
1790
                        f.write(", dpid='"+opts['dpid']+"'")
1791
                    f.write(")\n")
1792
                    if 'externalInterfaces' in opts:
1793
                        for extInterface in opts['externalInterfaces']:
1794
                            f.write("    Intf( '"+extInterface+"', node="+name+" )\n")
1795

    
1796
            f.write("\n")
1797
            f.write("    info( '*** Add hosts\\n')\n")
1798
            for widget in self.widgetToItem:
1799
                name = widget[ 'text' ]
1800
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1801
                if 'Host' in tags:
1802
                    opts = self.hostOpts[name]
1803
                    ip = None
1804
                    defaultRoute = None
1805
                    if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
1806
                        defaultRoute = "'via "+opts['defaultRoute']+"'"
1807
                    else:
1808
                        defaultRoute = 'None'
1809
                    if 'ip' in opts and len(opts['ip']) > 0:
1810
                        ip = opts['ip']
1811
                    else:
1812
                        nodeNum = self.hostOpts[name]['nodeNum']
1813
                        ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
1814
                        ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)
1815

    
1816
                    if 'cores' in opts or 'cpu' in opts:
1817
                        f.write("    "+name+" = net.addHost('"+name+"', cls=CPULimitedHost, ip='"+ip+"', defaultRoute="+defaultRoute+")\n")
1818
                        if 'cores' in opts:
1819
                            f.write("    "+name+".setCPUs(cores='"+opts['cores']+"')\n")
1820
                        if 'cpu' in opts:
1821
                            f.write("    "+name+".setCPUFrac(f="+str(opts['cpu'])+", sched='"+opts['sched']+"')\n")
1822
                    else:
1823
                        f.write("    "+name+" = net.addHost('"+name+"', cls=Host, ip='"+ip+"', defaultRoute="+defaultRoute+")\n")
1824
                    if 'externalInterfaces' in opts:
1825
                        for extInterface in opts['externalInterfaces']:
1826
                            f.write("    Intf( '"+extInterface+"', node="+name+" )\n")
1827
            f.write("\n")
1828

    
1829
            # Save Links
1830
            f.write("    info( '*** Add links\\n')\n")
1831
            for key,linkDetail in self.links.iteritems():
1832
                tags = self.canvas.gettags(key)
1833
                if 'data' in tags:
1834
                    optsExist = False
1835
                    src = linkDetail['src']
1836
                    dst = linkDetail['dest']
1837
                    linkopts = linkDetail['linkOpts']
1838
                    srcName, dstName = src[ 'text' ], dst[ 'text' ]
1839
                    bw = ''
1840
                    # delay = ''
1841
                    # loss = ''
1842
                    # max_queue_size = ''
1843
                    linkOpts = "{"
1844
                    if 'bw' in linkopts:
1845
                        bw =  linkopts['bw']
1846
                        linkOpts = linkOpts + "'bw':"+str(bw)
1847
                        optsExist = True
1848
                    if 'delay' in linkopts:
1849
                        # delay =  linkopts['delay']
1850
                        if optsExist:
1851
                            linkOpts = linkOpts + ","
1852
                        linkOpts = linkOpts + "'delay':'"+linkopts['delay']+"'"
1853
                        optsExist = True
1854
                    if 'loss' in linkopts:
1855
                        if optsExist:
1856
                            linkOpts = linkOpts + ","
1857
                        linkOpts = linkOpts + "'loss':"+str(linkopts['loss'])
1858
                        optsExist = True
1859
                    if 'max_queue_size' in linkopts:
1860
                        if optsExist:
1861
                            linkOpts = linkOpts + ","
1862
                        linkOpts = linkOpts + "'max_queue_size':"+str(linkopts['max_queue_size'])
1863
                        optsExist = True
1864
                    if 'jitter' in linkopts:
1865
                        if optsExist:
1866
                            linkOpts = linkOpts + ","
1867
                        linkOpts = linkOpts + "'jitter':'"+linkopts['jitter']+"'"
1868
                        optsExist = True
1869
                    if 'speedup' in linkopts:
1870
                        if optsExist:
1871
                            linkOpts = linkOpts + ","
1872
                        linkOpts = linkOpts + "'speedup':"+str(linkopts['speedup'])
1873
                        optsExist = True
1874

    
1875
                    linkOpts = linkOpts + "}"
1876
                    if optsExist:
1877
                        f.write("    "+srcName+dstName+" = "+linkOpts+"\n")
1878
                    f.write("    net.addLink("+srcName+", "+dstName)
1879
                    if optsExist:
1880
                        f.write(", cls=TCLink , **"+srcName+dstName)
1881
                    f.write(")\n")
1882

    
1883
            f.write("\n")
1884
            f.write("    info( '*** Starting network\\n')\n")
1885
            f.write("    net.build()\n")
1886

    
1887
            f.write("    info( '*** Starting controllers\\n')\n")
1888
            f.write("    for controller in net.controllers:\n")
1889
            f.write("        controller.start()\n")
1890
            f.write("\n")
1891

    
1892
            f.write("    info( '*** Starting switches\\n')\n")
1893
            for widget in self.widgetToItem:
1894
                name = widget[ 'text' ]
1895
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1896
                if 'Switch' in tags or 'LegacySwitch' in tags:
1897
                    opts = self.switchOpts[name]
1898
                    ctrlList = ",".join(opts['controllers'])
1899
                    f.write("    net.get('"+name+"').start(["+ctrlList+"])\n")
1900

    
1901
            f.write("\n")
1902

    
1903
            f.write("    info( '*** Post configure switches and hosts\\n')\n")
1904
            for widget in self.widgetToItem:
1905
                name = widget[ 'text' ]
1906
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1907
                if 'Switch' in tags:
1908
                    opts = self.switchOpts[name]
1909
                    if opts['switchType'] == 'default':
1910
                        if self.appPrefs['switchType'] == 'user':
1911
                            if 'switchIP' in opts:
1912
                                if len(opts['switchIP']) > 0:
1913
                                    f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1914
                        elif self.appPrefs['switchType'] == 'userns':
1915
                            if 'switchIP' in opts:
1916
                                if len(opts['switchIP']) > 0:
1917
                                    f.write("    "+name+".cmd('ifconfig lo "+opts['switchIP']+"')\n")
1918
                        elif self.appPrefs['switchType'] == 'ovs':
1919
                            if 'switchIP' in opts:
1920
                                if len(opts['switchIP']) > 0:
1921
                                    f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1922
                    elif opts['switchType'] == 'user':
1923
                        if 'switchIP' in opts:
1924
                            if len(opts['switchIP']) > 0:
1925
                                f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1926
                    elif opts['switchType'] == 'userns':
1927
                        if 'switchIP' in opts:
1928
                            if len(opts['switchIP']) > 0:
1929
                                f.write("    "+name+".cmd('ifconfig lo "+opts['switchIP']+"')\n")
1930
                    elif opts['switchType'] == 'ovs':
1931
                        if 'switchIP' in opts:
1932
                            if len(opts['switchIP']) > 0:
1933
                                f.write("    "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1934
            for widget in self.widgetToItem:
1935
                name = widget[ 'text' ]
1936
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1937
                if 'Host' in tags:
1938
                    opts = self.hostOpts[name]
1939
                    # Attach vlan interfaces
1940
                    if 'vlanInterfaces' in opts:
1941
                        for vlanInterface in opts['vlanInterfaces']:
1942
                            f.write("    "+name+".cmd('vconfig add "+name+"-eth0 "+vlanInterface[1]+"')\n")
1943
                            f.write("    "+name+".cmd('ifconfig "+name+"-eth0."+vlanInterface[1]+" "+vlanInterface[0]+"')\n")
1944
                    # Run User Defined Start Command
1945
                    if 'startCommand' in opts:
1946
                        f.write("    "+name+".cmdPrint('"+opts['startCommand']+"')\n")
1947
                if 'Switch' in tags:
1948
                    opts = self.switchOpts[name]
1949
                    # Run User Defined Start Command
1950
                    if 'startCommand' in opts:
1951
                        f.write("    "+name+".cmdPrint('"+opts['startCommand']+"')\n")
1952

    
1953
            # Configure NetFlow
1954
            nflowValues = self.appPrefs['netflow']
1955
            if len(nflowValues['nflowTarget']) > 0:
1956
                nflowEnabled = False
1957
                nflowSwitches = ''
1958
                for widget in self.widgetToItem:
1959
                    name = widget[ 'text' ]
1960
                    tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1961

    
1962
                    if 'Switch' in tags:
1963
                        opts = self.switchOpts[name]
1964
                        if 'netflow' in opts:
1965
                            if opts['netflow'] == '1':
1966
                                nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
1967
                                nflowEnabled=True
1968
                if nflowEnabled:
1969
                    nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '+ 'target=\\\"'+nflowValues['nflowTarget']+'\\\" '+ 'active-timeout='+nflowValues['nflowTimeout']
1970
                    if nflowValues['nflowAddId'] == '1':
1971
                        nflowCmd = nflowCmd + ' add_id_to_interface=true'
1972
                    else:
1973
                        nflowCmd = nflowCmd + ' add_id_to_interface=false'
1974
                    f.write("    \n")
1975
                    f.write("    call('"+nflowCmd+nflowSwitches+"', shell=True)\n")
1976

    
1977
            # Configure sFlow
1978
            sflowValues = self.appPrefs['sflow']
1979
            if len(sflowValues['sflowTarget']) > 0:
1980
                sflowEnabled = False
1981
                sflowSwitches = ''
1982
                for widget in self.widgetToItem:
1983
                    name = widget[ 'text' ]
1984
                    tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1985

    
1986
                    if 'Switch' in tags:
1987
                        opts = self.switchOpts[name]
1988
                        if 'sflow' in opts:
1989
                            if opts['sflow'] == '1':
1990
                                sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
1991
                                sflowEnabled=True
1992
                if sflowEnabled:
1993
                    sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
1994
                    f.write("    \n")
1995
                    f.write("    call('"+sflowCmd+sflowSwitches+"', shell=True)\n")
1996

    
1997
            f.write("\n")
1998
            f.write("    CLI(net)\n")
1999
            for widget in self.widgetToItem:
2000
                name = widget[ 'text' ]
2001
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2002
                if 'Host' in tags:
2003
                    opts = self.hostOpts[name]
2004
                    # Run User Defined Stop Command
2005
                    if 'stopCommand' in opts:
2006
                        f.write("    "+name+".cmdPrint('"+opts['stopCommand']+"')\n")
2007
                if 'Switch' in tags:
2008
                    opts = self.switchOpts[name]
2009
                    # Run User Defined Stop Command
2010
                    if 'stopCommand' in opts:
2011
                        f.write("    "+name+".cmdPrint('"+opts['stopCommand']+"')\n")
2012

    
2013
            f.write("    net.stop()\n")
2014
            f.write("\n")
2015
            f.write("if __name__ == '__main__':\n")
2016
            f.write("    setLogLevel( 'info' )\n")
2017
            f.write("    myNetwork()\n")
2018
            f.write("\n")
2019

    
2020

    
2021
            f.close()
2022

    
2023

    
2024
    # Generic canvas handler
2025
    #
2026
    # We could have used bindtags, as in nodeIcon, but
2027
    # the dynamic approach used here
2028
    # may actually require less code. In any case, it's an
2029
    # interesting introspection-based alternative to bindtags.
2030

    
2031
    def canvasHandle( self, eventName, event ):
2032
        "Generic canvas event handler"
2033
        if self.active is None:
2034
            return
2035
        toolName = self.active
2036
        handler = getattr( self, eventName + toolName, None )
2037
        if handler is not None:
2038
            handler( event )
2039

    
2040
    def clickCanvas( self, event ):
2041
        "Canvas click handler."
2042
        self.canvasHandle( 'click', event )
2043

    
2044
    def dragCanvas( self, event ):
2045
        "Canvas drag handler."
2046
        self.canvasHandle( 'drag', event )
2047

    
2048
    def releaseCanvas( self, event ):
2049
        "Canvas mouse up handler."
2050
        self.canvasHandle( 'release', event )
2051

    
2052
    # Currently the only items we can select directly are
2053
    # links. Nodes are handled by bindings in the node icon.
2054

    
2055
    def findItem( self, x, y ):
2056
        "Find items at a location in our canvas."
2057
        items = self.canvas.find_overlapping( x, y, x, y )
2058
        if len( items ) == 0:
2059
            return None
2060
        else:
2061
            return items[ 0 ]
2062

    
2063
    # Canvas bindings for Select, Host, Switch and Link tools
2064

    
2065
    def clickSelect( self, event ):
2066
        "Select an item."
2067
        self.selectItem( self.findItem( event.x, event.y ) )
2068

    
2069
    def deleteItem( self, item ):
2070
        "Delete an item."
2071
        # Don't delete while network is running
2072
        if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
2073
            return
2074
        # Delete from model
2075
        if item in self.links:
2076
            self.deleteLink( item )
2077
        if item in self.itemToWidget:
2078
            self.deleteNode( item )
2079
        # Delete from view
2080
        self.canvas.delete( item )
2081

    
2082
    def deleteSelection( self, _event ):
2083
        "Delete the selected item."
2084
        if self.selection is not None:
2085
            self.deleteItem( self.selection )
2086
        self.selectItem( None )
2087

    
2088
    def nodeIcon( self, node, name ):
2089
        "Create a new node icon."
2090
        icon = Button( self.canvas, image=self.images[ node ],
2091
                       text=name, compound='top' )
2092
        # Unfortunately bindtags wants a tuple
2093
        bindtags = [ str( self.nodeBindings ) ]
2094
        bindtags += list( icon.bindtags() )
2095
        icon.bindtags( tuple( bindtags ) )
2096
        return icon
2097

    
2098
    def newNode( self, node, event ):
2099
        "Add a new node to our canvas."
2100
        c = self.canvas
2101
        x, y = c.canvasx( event.x ), c.canvasy( event.y )
2102
        name = self.nodePrefixes[ node ]
2103
        if 'Switch' == node:
2104
            self.switchCount += 1
2105
            name = self.nodePrefixes[ node ] + str( self.switchCount )
2106
            self.switchOpts[name] = {}
2107
            self.switchOpts[name]['nodeNum']=self.switchCount
2108
            self.switchOpts[name]['hostname']=name
2109
            self.switchOpts[name]['switchType']='default'
2110
            self.switchOpts[name]['controllers']=[]
2111
        if 'LegacyRouter' == node:
2112
            self.switchCount += 1
2113
            name = self.nodePrefixes[ node ] + str( self.switchCount )
2114
            self.switchOpts[name] = {}
2115
            self.switchOpts[name]['nodeNum']=self.switchCount
2116
            self.switchOpts[name]['hostname']=name
2117
            self.switchOpts[name]['switchType']='legacyRouter'
2118
        if 'LegacySwitch' == node:
2119
            self.switchCount += 1
2120
            name = self.nodePrefixes[ node ] + str( self.switchCount )
2121
            self.switchOpts[name] = {}
2122
            self.switchOpts[name]['nodeNum']=self.switchCount
2123
            self.switchOpts[name]['hostname']=name
2124
            self.switchOpts[name]['switchType']='legacySwitch'
2125
            self.switchOpts[name]['controllers']=[]
2126
        if 'Host' == node:
2127
            self.hostCount += 1
2128
            name = self.nodePrefixes[ node ] + str( self.hostCount )
2129
            self.hostOpts[name] = {'sched':'host'}
2130
            self.hostOpts[name]['nodeNum']=self.hostCount
2131
            self.hostOpts[name]['hostname']=name
2132
        if 'Controller' == node:
2133
            name = self.nodePrefixes[ node ] + str( self.controllerCount )
2134
            ctrlr = { 'controllerType': 'ref',
2135
                      'hostname': name,
2136
                      'controllerProtocol': 'tcp',
2137
                      'remoteIP': '127.0.0.1',
2138
                      'remotePort': 6633}
2139
            self.controllers[name] = ctrlr
2140
            # We want to start controller count at 0
2141
            self.controllerCount += 1
2142

    
2143
        icon = self.nodeIcon( node, name )
2144
        item = self.canvas.create_window( x, y, anchor='c', window=icon,
2145
                                          tags=node )
2146
        self.widgetToItem[ icon ] = item
2147
        self.itemToWidget[ item ] = icon
2148
        self.selectItem( item )
2149
        icon.links = {}
2150
        if 'Switch' == node:
2151
            icon.bind('<Button-3>', self.do_switchPopup )
2152
        if 'LegacyRouter' == node:
2153
            icon.bind('<Button-3>', self.do_legacyRouterPopup )
2154
        if 'LegacySwitch' == node:
2155
            icon.bind('<Button-3>', self.do_legacySwitchPopup )
2156
        if 'Host' == node:
2157
            icon.bind('<Button-3>', self.do_hostPopup )
2158
        if 'Controller' == node:
2159
            icon.bind('<Button-3>', self.do_controllerPopup )
2160

    
2161
    def clickController( self, event ):
2162
        "Add a new Controller to our canvas."
2163
        self.newNode( 'Controller', event )
2164

    
2165
    def clickHost( self, event ):
2166
        "Add a new host to our canvas."
2167
        self.newNode( 'Host', event )
2168

    
2169
    def clickLegacyRouter( self, event ):
2170
        "Add a new switch to our canvas."
2171
        self.newNode( 'LegacyRouter', event )
2172

    
2173
    def clickLegacySwitch( self, event ):
2174
        "Add a new switch to our canvas."
2175
        self.newNode( 'LegacySwitch', event )
2176

    
2177
    def clickSwitch( self, event ):
2178
        "Add a new switch to our canvas."
2179
        self.newNode( 'Switch', event )
2180

    
2181
    def dragNetLink( self, event ):
2182
        "Drag a link's endpoint to another node."
2183
        if self.link is None:
2184
            return
2185
        # Since drag starts in widget, we use root coords
2186
        x = self.canvasx( event.x_root )
2187
        y = self.canvasy( event.y_root )
2188
        c = self.canvas
2189
        c.coords( self.link, self.linkx, self.linky, x, y )
2190

    
2191
    def releaseNetLink( self, _event ):
2192
        "Give up on the current link."
2193
        if self.link is not None:
2194
            self.canvas.delete( self.link )
2195
        self.linkWidget = self.linkItem = self.link = None
2196

    
2197
    # Generic node handlers
2198

    
2199
    def createNodeBindings( self ):
2200
        "Create a set of bindings for nodes."
2201
        bindings = {
2202
            '<ButtonPress-1>': self.clickNode,
2203
            '<B1-Motion>': self.dragNode,
2204
            '<ButtonRelease-1>': self.releaseNode,
2205
            '<Enter>': self.enterNode,
2206
            '<Leave>': self.leaveNode
2207
        }
2208
        l = Label()  # lightweight-ish owner for bindings
2209
        for event, binding in bindings.items():
2210
            l.bind( event, binding )
2211
        return l
2212

    
2213
    def selectItem( self, item ):
2214
        "Select an item and remember old selection."
2215
        self.lastSelection = self.selection
2216
        self.selection = item
2217

    
2218
    def enterNode( self, event ):
2219
        "Select node on entry."
2220
        self.selectNode( event )
2221

    
2222
    def leaveNode( self, _event ):
2223
        "Restore old selection on exit."
2224
        self.selectItem( self.lastSelection )
2225

    
2226
    def clickNode( self, event ):
2227
        "Node click handler."
2228
        if self.active is 'NetLink':
2229
            self.startLink( event )
2230
        else:
2231
            self.selectNode( event )
2232
        return 'break'
2233

    
2234
    def dragNode( self, event ):
2235
        "Node drag handler."
2236
        if self.active is 'NetLink':
2237
            self.dragNetLink( event )
2238
        else:
2239
            self.dragNodeAround( event )
2240

    
2241
    def releaseNode( self, event ):
2242
        "Node release handler."
2243
        if self.active is 'NetLink':
2244
            self.finishLink( event )
2245

    
2246
    # Specific node handlers
2247

    
2248
    def selectNode( self, event ):
2249
        "Select the node that was clicked on."
2250
        item = self.widgetToItem.get( event.widget, None )
2251
        self.selectItem( item )
2252

    
2253
    def dragNodeAround( self, event ):
2254
        "Drag a node around on the canvas."
2255
        c = self.canvas
2256
        # Convert global to local coordinates;
2257
        # Necessary since x, y are widget-relative
2258
        x = self.canvasx( event.x_root )
2259
        y = self.canvasy( event.y_root )
2260
        w = event.widget
2261
        # Adjust node position
2262
        item = self.widgetToItem[ w ]
2263
        c.coords( item, x, y )
2264
        # Adjust link positions
2265
        for dest in w.links:
2266
            link = w.links[ dest ]
2267
            item = self.widgetToItem[ dest ]
2268
            x1, y1 = c.coords( item )
2269
            c.coords( link, x, y, x1, y1 )
2270
        self.updateScrollRegion()
2271

    
2272
    def createControlLinkBindings( self ):
2273
        "Create a set of bindings for nodes."
2274
        # Link bindings
2275
        # Selection still needs a bit of work overall
2276
        # Callbacks ignore event
2277

    
2278
        def select( _event, link=self.link ):
2279
            "Select item on mouse entry."
2280
            self.selectItem( link )
2281

    
2282
        def highlight( _event, link=self.link ):
2283
            "Highlight item on mouse entry."
2284
            self.selectItem( link )
2285
            self.canvas.itemconfig( link, fill='green' )
2286

    
2287
        def unhighlight( _event, link=self.link ):
2288
            "Unhighlight item on mouse exit."
2289
            self.canvas.itemconfig( link, fill='red' )
2290
            #self.selectItem( None )
2291

    
2292
        self.canvas.tag_bind( self.link, '<Enter>', highlight )
2293
        self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
2294
        self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
2295

    
2296
    def createDataLinkBindings( self ):
2297
        "Create a set of bindings for nodes."
2298
        # Link bindings
2299
        # Selection still needs a bit of work overall
2300
        # Callbacks ignore event
2301

    
2302
        def select( _event, link=self.link ):
2303
            "Select item on mouse entry."
2304
            self.selectItem( link )
2305

    
2306
        def highlight( _event, link=self.link ):
2307
            "Highlight item on mouse entry."
2308
            self.selectItem( link )
2309
            self.canvas.itemconfig( link, fill='green' )
2310

    
2311
        def unhighlight( _event, link=self.link ):
2312
            "Unhighlight item on mouse exit."
2313
            self.canvas.itemconfig( link, fill='blue' )
2314
            #self.selectItem( None )
2315

    
2316
        self.canvas.tag_bind( self.link, '<Enter>', highlight )
2317
        self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
2318
        self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
2319
        self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
2320

    
2321

    
2322
    def startLink( self, event ):
2323
        "Start a new link."
2324
        if event.widget not in self.widgetToItem:
2325
            # Didn't click on a node
2326
            return
2327

    
2328
        w = event.widget
2329
        item = self.widgetToItem[ w ]
2330
        x, y = self.canvas.coords( item )
2331
        self.link = self.canvas.create_line( x, y, x, y, width=4,
2332
                                             fill='blue', tag='link' )
2333
        self.linkx, self.linky = x, y
2334
        self.linkWidget = w
2335
        self.linkItem = item
2336

    
2337

    
2338
    def finishLink( self, event ):
2339
        "Finish creating a link"
2340
        if self.link is None:
2341
            return
2342
        source = self.linkWidget
2343
        c = self.canvas
2344
        # Since we dragged from the widget, use root coords
2345
        x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
2346
        target = self.findItem( x, y )
2347
        dest = self.itemToWidget.get( target, None )
2348
        if ( source is None or dest is None or source == dest
2349
                or dest in source.links or source in dest.links ):
2350
            self.releaseNetLink( event )
2351
            return
2352
        # For now, don't allow hosts to be directly linked
2353
        stags = self.canvas.gettags( self.widgetToItem[ source ] )
2354
        dtags = self.canvas.gettags( target )
2355
        if (('Host' in stags and 'Host' in dtags) or
2356
           ('Controller' in dtags and 'LegacyRouter' in stags) or
2357
           ('Controller' in stags and 'LegacyRouter' in dtags) or
2358
           ('Controller' in dtags and 'LegacySwitch' in stags) or
2359
           ('Controller' in stags and 'LegacySwitch' in dtags) or
2360
           ('Controller' in dtags and 'Host' in stags) or
2361
           ('Controller' in stags and 'Host' in dtags) or
2362
           ('Controller' in stags and 'Controller' in dtags)):
2363
            self.releaseNetLink( event )
2364
            return
2365

    
2366
        # Set link type
2367
        linkType='data'
2368
        if 'Controller' in stags or 'Controller' in dtags:
2369
            linkType='control'
2370
            c.itemconfig(self.link, dash=(6, 4, 2, 4), fill='red')
2371
            self.createControlLinkBindings()
2372
        else:
2373
            linkType='data'
2374
            self.createDataLinkBindings()
2375
        c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
2376

    
2377
        x, y = c.coords( target )
2378
        c.coords( self.link, self.linkx, self.linky, x, y )
2379
        self.addLink( source, dest, linktype=linkType )
2380
        if linkType == 'control':
2381
            controllerName = ''
2382
            switchName = ''
2383
            if 'Controller' in stags:
2384
                controllerName = source[ 'text' ]
2385
                switchName = dest[ 'text' ]
2386
            else:
2387
                controllerName = dest[ 'text' ]
2388
                switchName = source[ 'text' ]
2389

    
2390
            self.switchOpts[switchName]['controllers'].append(controllerName)
2391

    
2392
        # We're done
2393
        self.link = self.linkWidget = None
2394

    
2395
    # Menu handlers
2396

    
2397
    def about( self ):
2398
        "Display about box."
2399
        about = self.aboutBox
2400
        if about is None:
2401
            bg = 'white'
2402
            about = Toplevel( bg='white' )
2403
            about.title( 'About' )
2404
            info = self.appName + ': a simple network editor for MiniNet'
2405
            version = 'MiniEdit '+MINIEDIT_VERSION
2406
            author = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
2407
            enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
2408
            www = 'http://gregorygee.wordpress.com/category/miniedit/'
2409
            line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
2410
            line2 = Label( about, text=version, font='Helvetica 9', bg=bg )
2411
            line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
2412
            line4 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
2413
            line5 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
2414
            line5.insert(0, www)
2415
            line5.configure(state='readonly')
2416
            line1.pack( padx=20, pady=10 )
2417
            line2.pack(pady=10 )
2418
            line3.pack(pady=10 )
2419
            line4.pack(pady=10 )
2420
            line5.pack(pady=10 )
2421
            hide = ( lambda about=about: about.withdraw() )
2422
            self.aboutBox = about
2423
            # Hide on close rather than destroying window
2424
            Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
2425
        # Show (existing) window
2426
        about.deiconify()
2427

    
2428
    def createToolImages( self ):
2429
        "Create toolbar (and icon) images."
2430

    
2431
    def checkIntf( self, intf ):
2432
        "Make sure intf exists and is not configured."
2433
        if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
2434
            showerror(title="Error",
2435
                      message='External interface ' +intf + ' does not exist! Skipping.')
2436
            return False
2437
        ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
2438
        if ips:
2439
            showerror(title="Error",
2440
                      message= intf + ' has an IP address and is probably in use! Skipping.' )
2441
            return False
2442
        return True
2443

    
2444
    def hostDetails( self, _ignore=None ):
2445
        if ( self.selection is None or
2446
             self.net is not None or
2447
             self.selection not in self.itemToWidget ):
2448
            return
2449
        widget = self.itemToWidget[ self.selection ]
2450
        name = widget[ 'text' ]
2451
        tags = self.canvas.gettags( self.selection )
2452
        if 'Host' not in tags:
2453
            return
2454

    
2455
        prefDefaults = self.hostOpts[name]
2456
        hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults)
2457
        self.master.wait_window(hostBox.top)
2458
        if hostBox.result:
2459
            newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
2460
            newHostOpts['sched'] = hostBox.result['sched']
2461
            if len(hostBox.result['startCommand']) > 0:
2462
                newHostOpts['startCommand'] = hostBox.result['startCommand']
2463
            if len(hostBox.result['stopCommand']) > 0:
2464
                newHostOpts['stopCommand'] = hostBox.result['stopCommand']
2465
            if len(hostBox.result['cpu']) > 0:
2466
                newHostOpts['cpu'] = float(hostBox.result['cpu'])
2467
            if len(hostBox.result['cores']) > 0:
2468
                newHostOpts['cores'] = hostBox.result['cores']
2469
            if len(hostBox.result['hostname']) > 0:
2470
                newHostOpts['hostname'] = hostBox.result['hostname']
2471
                name = hostBox.result['hostname']
2472
                widget[ 'text' ] = name
2473
            if len(hostBox.result['defaultRoute']) > 0:
2474
                newHostOpts['defaultRoute'] = hostBox.result['defaultRoute']
2475
            if len(hostBox.result['ip']) > 0:
2476
                newHostOpts['ip'] = hostBox.result['ip']
2477
            if len(hostBox.result['externalInterfaces']) > 0:
2478
                newHostOpts['externalInterfaces'] = hostBox.result['externalInterfaces']
2479
            if len(hostBox.result['vlanInterfaces']) > 0:
2480
                newHostOpts['vlanInterfaces'] = hostBox.result['vlanInterfaces']
2481
            if len(hostBox.result['privateDirectory']) > 0:
2482
                newHostOpts['privateDirectory'] = hostBox.result['privateDirectory']
2483
            self.hostOpts[name] = newHostOpts
2484
            print 'New host details for ' + name + ' = ' + str(newHostOpts)
2485

    
2486
    def switchDetails( self, _ignore=None ):
2487
        if ( self.selection is None or
2488
             self.net is not None or
2489
             self.selection not in self.itemToWidget ):
2490
            return
2491
        widget = self.itemToWidget[ self.selection ]
2492
        name = widget[ 'text' ]
2493
        tags = self.canvas.gettags( self.selection )
2494
        if 'Switch' not in tags:
2495
            return
2496

    
2497
        prefDefaults = self.switchOpts[name]
2498
        switchBox = SwitchDialog(self, title='Switch Details', prefDefaults=prefDefaults)
2499
        self.master.wait_window(switchBox.top)
2500
        if switchBox.result:
2501
            newSwitchOpts = {'nodeNum':self.switchOpts[name]['nodeNum']}
2502
            newSwitchOpts['switchType'] = switchBox.result['switchType']
2503
            newSwitchOpts['controllers'] = self.switchOpts[name]['controllers']
2504
            if len(switchBox.result['startCommand']) > 0:
2505
                newSwitchOpts['startCommand'] = switchBox.result['startCommand']
2506
            if len(switchBox.result['stopCommand']) > 0:
2507
                newSwitchOpts['stopCommand'] = switchBox.result['stopCommand']
2508
            if len(switchBox.result['dpctl']) > 0:
2509
                newSwitchOpts['dpctl'] = switchBox.result['dpctl']
2510
            if len(switchBox.result['dpid']) > 0:
2511
                newSwitchOpts['dpid'] = switchBox.result['dpid']
2512
            if len(switchBox.result['hostname']) > 0:
2513
                newSwitchOpts['hostname'] = switchBox.result['hostname']
2514
                name = switchBox.result['hostname']
2515
                widget[ 'text' ] = name
2516
            if len(switchBox.result['externalInterfaces']) > 0:
2517
                newSwitchOpts['externalInterfaces'] = switchBox.result['externalInterfaces']
2518
            newSwitchOpts['switchIP'] = switchBox.result['switchIP']
2519
            newSwitchOpts['sflow'] = switchBox.result['sflow']
2520
            newSwitchOpts['netflow'] = switchBox.result['netflow']
2521
            self.switchOpts[name] = newSwitchOpts
2522
            print 'New switch details for ' + name + ' = ' + str(newSwitchOpts)
2523

    
2524
    def linkUp( self ):
2525
        if ( self.selection is None or
2526
             self.net is None):
2527
            return
2528
        link = self.selection
2529
        linkDetail =  self.links[link]
2530
        src = linkDetail['src']
2531
        dst = linkDetail['dest']
2532
        srcName, dstName = src[ 'text' ], dst[ 'text' ]
2533
        self.net.configLinkStatus(srcName, dstName, 'up')
2534
        self.canvas.itemconfig(link, dash=())
2535

    
2536
    def linkDown( self ):
2537
        if ( self.selection is None or
2538
             self.net is None):
2539
            return
2540
        link = self.selection
2541
        linkDetail =  self.links[link]
2542
        src = linkDetail['src']
2543
        dst = linkDetail['dest']
2544
        srcName, dstName = src[ 'text' ], dst[ 'text' ]
2545
        self.net.configLinkStatus(srcName, dstName, 'down')
2546
        self.canvas.itemconfig(link, dash=(4, 4))
2547

    
2548
    def linkDetails( self, _ignore=None ):
2549
        if ( self.selection is None or
2550
             self.net is not None):
2551
            return
2552
        link = self.selection
2553

    
2554
        linkDetail =  self.links[link]
2555
        # src = linkDetail['src']
2556
        # dest = linkDetail['dest']
2557
        linkopts = linkDetail['linkOpts']
2558
        linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
2559
        if linkBox.result is not None:
2560
            linkDetail['linkOpts'] = linkBox.result
2561
            print 'New link details = ' + str(linkBox.result)
2562

    
2563
    def prefDetails( self ):
2564
        prefDefaults = self.appPrefs
2565
        prefBox = PrefsDialog(self, title='Preferences', prefDefaults=prefDefaults)
2566
        print 'New Prefs = ' + str(prefBox.result)
2567
        if prefBox.result:
2568
            self.appPrefs = prefBox.result
2569

    
2570

    
2571
    def controllerDetails( self ):
2572
        if ( self.selection is None or
2573
             self.net is not None or
2574
             self.selection not in self.itemToWidget ):
2575
            return
2576
        widget = self.itemToWidget[ self.selection ]
2577
        name = widget[ 'text' ]
2578
        tags = self.canvas.gettags( self.selection )
2579
        oldName = name
2580
        if 'Controller' not in tags:
2581
            return
2582

    
2583
        ctrlrBox = ControllerDialog(self, title='Controller Details', ctrlrDefaults=self.controllers[name])
2584
        if ctrlrBox.result:
2585
            #print 'Controller is ' + ctrlrBox.result[0]
2586
            if len(ctrlrBox.result['hostname']) > 0:
2587
                name = ctrlrBox.result['hostname']
2588
                widget[ 'text' ] = name
2589
            else:
2590
                ctrlrBox.result['hostname'] = name
2591
            self.controllers[name] = ctrlrBox.result
2592
            print 'New controller details for ' + name + ' = ' + str(self.controllers[name])
2593
            # Find references to controller and change name
2594
            if oldName != name:
2595
                for widget in self.widgetToItem:
2596
                    switchName = widget[ 'text' ]
2597
                    tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2598
                    if 'Switch' in tags:
2599
                        switch = self.switchOpts[switchName]
2600
                        if oldName in switch['controllers']:
2601
                            switch['controllers'].remove(oldName)
2602
                            switch['controllers'].append(name)
2603

    
2604

    
2605
    def listBridge( self, _ignore=None ):
2606
        if ( self.selection is None or
2607
             self.net is None or
2608
             self.selection not in self.itemToWidget ):
2609
            return
2610
        name = self.itemToWidget[ self.selection ][ 'text' ]
2611
        tags = self.canvas.gettags( self.selection )
2612

    
2613
        if name not in self.net.nameToNode:
2614
            return
2615
        if 'Switch' in tags or 'LegacySwitch' in tags:
2616
            call(["xterm -T 'Bridge Details' -sb -sl 2000 -e 'ovs-vsctl list bridge " + name + "; read -p \"Press Enter to close\"' &"], shell=True)
2617

    
2618
    def ovsShow( self, _ignore=None ):
2619
        call(["xterm -T 'OVS Summary' -sb -sl 2000 -e 'ovs-vsctl show; read -p \"Press Enter to close\"' &"], shell=True)
2620

    
2621
    def rootTerminal( self, _ignore=None ):
2622
        call(["xterm -T 'Root Terminal' -sb -sl 2000 &"], shell=True)
2623

    
2624
    # Model interface
2625
    #
2626
    # Ultimately we will either want to use a topo or
2627
    # mininet object here, probably.
2628

    
2629
    def addLink( self, source, dest, linktype='data', linkopts=None ):
2630
        "Add link to model."
2631
        if linkopts is None:
2632
            linkopts = {}
2633
        source.links[ dest ] = self.link
2634
        dest.links[ source ] = self.link
2635
        self.links[ self.link ] = {'type' :linktype,
2636
                                   'src':source,
2637
                                   'dest':dest,
2638
                                   'linkOpts':linkopts}
2639

    
2640
    def deleteLink( self, link ):
2641
        "Delete link from model."
2642
        pair = self.links.get( link, None )
2643
        if pair is not None:
2644
            source=pair['src']
2645
            dest=pair['dest']
2646
            del source.links[ dest ]
2647
            del dest.links[ source ]
2648
            stags = self.canvas.gettags( self.widgetToItem[ source ] )
2649
            # dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
2650
            ltags = self.canvas.gettags( link )
2651

    
2652
            if 'control' in ltags:
2653
                controllerName = ''
2654
                switchName = ''
2655
                if 'Controller' in stags:
2656
                    controllerName = source[ 'text' ]
2657
                    switchName = dest[ 'text' ]
2658
                else:
2659
                    controllerName = dest[ 'text' ]
2660
                    switchName = source[ 'text' ]
2661

    
2662
                if controllerName in self.switchOpts[switchName]['controllers']:
2663
                    self.switchOpts[switchName]['controllers'].remove(controllerName)
2664

    
2665

    
2666
        if link is not None:
2667
            del self.links[ link ]
2668

    
2669
    def deleteNode( self, item ):
2670
        "Delete node (and its links) from model."
2671

    
2672
        widget = self.itemToWidget[ item ]
2673
        tags = self.canvas.gettags(item)
2674
        if 'Controller' in tags:
2675
            # remove from switch controller lists
2676
            for serachwidget in self.widgetToItem:
2677
                name = serachwidget[ 'text' ]
2678
                tags = self.canvas.gettags( self.widgetToItem[ serachwidget ] )
2679
                if 'Switch' in tags:
2680
                    if widget['text'] in self.switchOpts[name]['controllers']:
2681
                        self.switchOpts[name]['controllers'].remove(widget['text'])
2682

    
2683
        for link in widget.links.values():
2684
            # Delete from view and model
2685
            self.deleteItem( link )
2686
        del self.itemToWidget[ item ]
2687
        del self.widgetToItem[ widget ]
2688

    
2689
    def buildNodes( self, net):
2690
        # Make nodes
2691
        print "Getting Hosts and Switches."
2692
        for widget in self.widgetToItem:
2693
            name = widget[ 'text' ]
2694
            tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2695
            #print name+' has '+str(tags)
2696

    
2697
            if 'Switch' in tags:
2698
                opts = self.switchOpts[name]
2699
                #print str(opts)
2700

    
2701
                # Create the correct switch class
2702
                switchClass = customOvs
2703
                switchParms={}
2704
                if 'dpctl' in opts:
2705
                    switchParms['listenPort']=int(opts['dpctl'])
2706
                if 'dpid' in opts:
2707
                    switchParms['dpid']=opts['dpid']
2708
                if opts['switchType'] == 'default':
2709
                    if self.appPrefs['switchType'] == 'ivs':
2710
                        switchClass = IVSSwitch
2711
                    elif self.appPrefs['switchType'] == 'user':
2712
                        switchClass = CustomUserSwitch
2713
                    elif self.appPrefs['switchType'] == 'userns':
2714
                        switchParms['inNamespace'] = True
2715
                        switchClass = CustomUserSwitch
2716
                    else:
2717
                        switchClass = customOvs
2718
                elif opts['switchType'] == 'user':
2719
                    switchClass = CustomUserSwitch
2720
                elif opts['switchType'] == 'userns':
2721
                    switchClass = CustomUserSwitch
2722
                    switchParms['inNamespace'] = True
2723
                elif opts['switchType'] == 'ivs':
2724
                    switchClass = IVSSwitch
2725
                else:
2726
                    switchClass = customOvs
2727

    
2728
                if switchClass == customOvs:
2729
                    # Set OpenFlow versions
2730
                    self.openFlowVersions = []
2731
                    if self.appPrefs['openFlowVersions']['ovsOf10'] == '1':
2732
                        self.openFlowVersions.append('OpenFlow10')
2733
                    if self.appPrefs['openFlowVersions']['ovsOf11'] == '1':
2734
                        self.openFlowVersions.append('OpenFlow11')
2735
                    if self.appPrefs['openFlowVersions']['ovsOf12'] == '1':
2736
                        self.openFlowVersions.append('OpenFlow12')
2737
                    if self.appPrefs['openFlowVersions']['ovsOf13'] == '1':
2738
                        self.openFlowVersions.append('OpenFlow13')
2739
                    protoList = ",".join(self.openFlowVersions)
2740
                    switchParms['protocols'] = protoList
2741
                newSwitch = net.addSwitch( name , cls=switchClass, **switchParms)
2742

    
2743
                # Some post startup config
2744
                if switchClass == CustomUserSwitch:
2745
                    if 'switchIP' in opts:
2746
                        if len(opts['switchIP']) > 0:
2747
                            newSwitch.setSwitchIP(opts['switchIP'])
2748
                if switchClass == customOvs:
2749
                    if 'switchIP' in opts:
2750
                        if len(opts['switchIP']) > 0:
2751
                            newSwitch.setSwitchIP(opts['switchIP'])
2752

    
2753
                # Attach external interfaces
2754
                if 'externalInterfaces' in opts:
2755
                    for extInterface in opts['externalInterfaces']:
2756
                        if self.checkIntf(extInterface):
2757
                            Intf( extInterface, node=newSwitch )
2758

    
2759
            elif 'LegacySwitch' in tags:
2760
                newSwitch = net.addSwitch( name , cls=LegacySwitch)
2761
            elif 'LegacyRouter' in tags:
2762
                newSwitch = net.addHost( name , cls=LegacyRouter)
2763
            elif 'Host' in tags:
2764
                opts = self.hostOpts[name]
2765
                #print str(opts)
2766
                ip = None
2767
                defaultRoute = None
2768
                if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
2769
                    defaultRoute = 'via '+opts['defaultRoute']
2770
                if 'ip' in opts and len(opts['ip']) > 0:
2771
                    ip = opts['ip']
2772
                else:
2773
                    nodeNum = self.hostOpts[name]['nodeNum']
2774
                    ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
2775
                    ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)
2776

    
2777
                # Create the correct host class
2778
                if 'cores' in opts or 'cpu' in opts:
2779
                    if 'privateDirectory' in opts:
2780
                        hostCls = partial( CPULimitedHost,
2781
                                           privateDirs=opts['privateDirectory'] )
2782
                    else:
2783
                        hostCls=CPULimitedHost
2784
                else:
2785
                    if 'privateDirectory' in opts:
2786
                        hostCls = partial( Host,
2787
                                           privateDirs=opts['privateDirectory'] )
2788
                    else:
2789
                        hostCls=Host
2790
                print hostCls
2791
                newHost = net.addHost( name,
2792
                                       cls=hostCls,
2793
                                       ip=ip,
2794
                                       defaultRoute=defaultRoute
2795
                                      )
2796

    
2797
                # Set the CPULimitedHost specific options
2798
                if 'cores' in opts:
2799
                    newHost.setCPUs(cores = opts['cores'])
2800
                if 'cpu' in opts:
2801
                    newHost.setCPUFrac(f=opts['cpu'], sched=opts['sched'])
2802

    
2803
                # Attach external interfaces
2804
                if 'externalInterfaces' in opts:
2805
                    for extInterface in opts['externalInterfaces']:
2806
                        if self.checkIntf(extInterface):
2807
                            Intf( extInterface, node=newHost )
2808
                if 'vlanInterfaces' in opts:
2809
                    if len(opts['vlanInterfaces']) > 0:
2810
                        print 'Checking that OS is VLAN prepared'
2811
                        self.pathCheck('vconfig', moduleName='vlan package')
2812
                        moduleDeps( add='8021q' )
2813
            elif 'Controller' in tags:
2814
                opts = self.controllers[name]
2815

    
2816
                # Get controller info from panel
2817
                controllerType = opts['controllerType']
2818
                if 'controllerProtocol' in opts:
2819
                    controllerProtocol = opts['controllerProtocol']
2820
                else:
2821
                    controllerProtocol = 'tcp'
2822
                    opts['controllerProtocol'] = 'tcp'
2823
                controllerIP = opts['remoteIP']
2824
                controllerPort = opts['remotePort']
2825

    
2826
                # Make controller
2827
                print 'Getting controller selection:'+controllerType
2828
                if controllerType == 'remote':
2829
                    net.addController(name=name,
2830
                                      controller=RemoteController,
2831
                                      ip=controllerIP,
2832
                                      protocol=controllerProtocol,
2833
                                      port=controllerPort)
2834
                elif controllerType == 'inband':
2835
                    net.addController(name=name,
2836
                                      controller=InbandController,
2837
                                      ip=controllerIP,
2838
                                      protocol=controllerProtocol,
2839
                                      port=controllerPort)
2840
                elif controllerType == 'ovsc':
2841
                    net.addController(name=name,
2842
                                      controller=OVSController,
2843
                                      protocol=controllerProtocol,
2844
                                      port=controllerPort)
2845
                else:
2846
                    net.addController(name=name,
2847
                                      controller=Controller,
2848
                                      protocol=controllerProtocol,
2849
                                      port=controllerPort)
2850

    
2851
            else:
2852
                raise Exception( "Cannot create mystery node: " + name )
2853

    
2854
    def pathCheck( self, *args, **kwargs ):
2855
        "Make sure each program in *args can be found in $PATH."
2856
        moduleName = kwargs.get( 'moduleName', 'it' )
2857
        for arg in args:
2858
            if not quietRun( 'which ' + arg ):
2859
                showerror(title="Error",
2860
                      message= 'Cannot find required executable %s.\n' % arg +
2861
                       'Please make sure that %s is installed ' % moduleName +
2862
                       'and available in your $PATH.' )
2863

    
2864
    def buildLinks( self, net):
2865
        # Make links
2866
        print "Getting Links."
2867
        for key,link in self.links.iteritems():
2868
            tags = self.canvas.gettags(key)
2869
            if 'data' in tags:
2870
                src=link['src']
2871
                dst=link['dest']
2872
                linkopts=link['linkOpts']
2873
                srcName, dstName = src[ 'text' ], dst[ 'text' ]
2874
                srcNode, dstNode = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
2875
                if linkopts:
2876
                    net.addLink(srcNode, dstNode, cls=TCLink, **linkopts)
2877
                else:
2878
                    #print str(srcNode)
2879
                    #print str(dstNode)
2880
                    net.addLink(srcNode, dstNode)
2881
                self.canvas.itemconfig(key, dash=())
2882

    
2883

    
2884
    def build( self ):
2885
        print "Build network based on our topology."
2886

    
2887
        dpctl = None
2888
        if len(self.appPrefs['dpctl']) > 0:
2889
            dpctl = int(self.appPrefs['dpctl'])
2890
        net = Mininet( topo=None,
2891
                       listenPort=dpctl,
2892
                       build=False,
2893
                       ipBase=self.appPrefs['ipBase'] )
2894

    
2895
        self.buildNodes(net)
2896
        self.buildLinks(net)
2897

    
2898
        # Build network (we have to do this separately at the moment )
2899
        net.build()
2900

    
2901
        return net
2902

    
2903

    
2904
    def postStartSetup( self ):
2905

    
2906
        # Setup host details
2907
        for widget in self.widgetToItem:
2908
            name = widget[ 'text' ]
2909
            tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2910
            if 'Host' in tags:
2911
                newHost = self.net.get(name)
2912
                opts = self.hostOpts[name]
2913
                # Attach vlan interfaces
2914
                if 'vlanInterfaces' in opts:
2915
                    for vlanInterface in opts['vlanInterfaces']:
2916
                        print 'adding vlan interface '+vlanInterface[1]
2917
                        newHost.cmdPrint('ifconfig '+name+'-eth0.'+vlanInterface[1]+' '+vlanInterface[0])
2918
                # Run User Defined Start Command
2919
                if 'startCommand' in opts:
2920
                    newHost.cmdPrint(opts['startCommand'])
2921
            if 'Switch' in tags:
2922
                newNode = self.net.get(name)
2923
                opts = self.switchOpts[name]
2924
                # Run User Defined Start Command
2925
                if 'startCommand' in opts:
2926
                    newNode.cmdPrint(opts['startCommand'])
2927

    
2928

    
2929
        # Configure NetFlow
2930
        nflowValues = self.appPrefs['netflow']
2931
        if len(nflowValues['nflowTarget']) > 0:
2932
            nflowEnabled = False
2933
            nflowSwitches = ''
2934
            for widget in self.widgetToItem:
2935
                name = widget[ 'text' ]
2936
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2937

    
2938
                if 'Switch' in tags:
2939
                    opts = self.switchOpts[name]
2940
                    if 'netflow' in opts:
2941
                        if opts['netflow'] == '1':
2942
                            print name+' has Netflow enabled'
2943
                            nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
2944
                            nflowEnabled=True
2945
            if nflowEnabled:
2946
                nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '+ 'target=\\\"'+nflowValues['nflowTarget']+'\\\" '+ 'active-timeout='+nflowValues['nflowTimeout']
2947
                if nflowValues['nflowAddId'] == '1':
2948
                    nflowCmd = nflowCmd + ' add_id_to_interface=true'
2949
                else:
2950
                    nflowCmd = nflowCmd + ' add_id_to_interface=false'
2951
                print 'cmd = '+nflowCmd+nflowSwitches
2952
                call(nflowCmd+nflowSwitches, shell=True)
2953

    
2954
            else:
2955
                print 'No switches with Netflow'
2956
        else:
2957
            print 'No NetFlow targets specified.'
2958

    
2959
        # Configure sFlow
2960
        sflowValues = self.appPrefs['sflow']
2961
        if len(sflowValues['sflowTarget']) > 0:
2962
            sflowEnabled = False
2963
            sflowSwitches = ''
2964
            for widget in self.widgetToItem:
2965
                name = widget[ 'text' ]
2966
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2967

    
2968
                if 'Switch' in tags:
2969
                    opts = self.switchOpts[name]
2970
                    if 'sflow' in opts:
2971
                        if opts['sflow'] == '1':
2972
                            print name+' has sflow enabled'
2973
                            sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
2974
                            sflowEnabled=True
2975
            if sflowEnabled:
2976
                sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
2977
                print 'cmd = '+sflowCmd+sflowSwitches
2978
                call(sflowCmd+sflowSwitches, shell=True)
2979

    
2980
            else:
2981
                print 'No switches with sflow'
2982
        else:
2983
            print 'No sFlow targets specified.'
2984

    
2985
        ## NOTE: MAKE SURE THIS IS LAST THING CALLED
2986
        # Start the CLI if enabled
2987
        if self.appPrefs['startCLI'] == '1':
2988
            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")
2989
            CLI(self.net)
2990

    
2991
    def start( self ):
2992
        "Start network."
2993
        if self.net is None:
2994
            self.net = self.build()
2995

    
2996
            # Since I am going to inject per switch controllers.
2997
            # I can't call net.start().  I have to replicate what it
2998
            # does and add the controller options.
2999
            #self.net.start()
3000
            info( '**** Starting %s controllers\n' % len( self.net.controllers ) )
3001
            for controller in self.net.controllers:
3002
                info( str(controller) + ' ')
3003
                controller.start()
3004
            info('\n')
3005
            info( '**** Starting %s switches\n' % len( self.net.switches ) )
3006
            #for switch in self.net.switches:
3007
            #    info( switch.name + ' ')
3008
            #    switch.start( self.net.controllers )
3009
            for widget in self.widgetToItem:
3010
                name = widget[ 'text' ]
3011
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
3012
                if 'Switch' in tags:
3013
                    opts = self.switchOpts[name]
3014
                    switchControllers = []
3015
                    for ctrl in opts['controllers']:
3016
                        switchControllers.append(self.net.get(ctrl))
3017
                    info( name + ' ')
3018
                    # Figure out what controllers will manage this switch
3019
                    self.net.get(name).start( switchControllers )
3020
                if 'LegacySwitch' in tags:
3021
                    self.net.get(name).start( [] )
3022
                    info( name + ' ')
3023
            info('\n')
3024

    
3025
            self.postStartSetup()
3026

    
3027
    def stop( self ):
3028
        "Stop network."
3029
        if self.net is not None:
3030
            # Stop host details
3031
            for widget in self.widgetToItem:
3032
                name = widget[ 'text' ]
3033
                tags = self.canvas.gettags( self.widgetToItem[ widget ] )
3034
                if 'Host' in tags:
3035
                    newHost = self.net.get(name)
3036
                    opts = self.hostOpts[name]
3037
                    # Run User Defined Stop Command
3038
                    if 'stopCommand' in opts:
3039
                        newHost.cmdPrint(opts['stopCommand'])
3040
                if 'Switch' in tags:
3041
                    newNode = self.net.get(name)
3042
                    opts = self.switchOpts[name]
3043
                    # Run User Defined Stop Command
3044
                    if 'stopCommand' in opts:
3045
                        newNode.cmdPrint(opts['stopCommand'])
3046

    
3047
            self.net.stop()
3048
        cleanUpScreens()
3049
        self.net = None
3050

    
3051
    def do_linkPopup(self, event):
3052
        # display the popup menu
3053
        if self.net is None:
3054
            try:
3055
                self.linkPopup.tk_popup(event.x_root, event.y_root, 0)
3056
            finally:
3057
                # make sure to release the grab (Tk 8.0a1 only)
3058
                self.linkPopup.grab_release()
3059
        else:
3060
            try:
3061
                self.linkRunPopup.tk_popup(event.x_root, event.y_root, 0)
3062
            finally:
3063
                # make sure to release the grab (Tk 8.0a1 only)
3064
                self.linkRunPopup.grab_release()
3065

    
3066
    def do_controllerPopup(self, event):
3067
        # display the popup menu
3068
        if self.net is None:
3069
            try:
3070
                self.controllerPopup.tk_popup(event.x_root, event.y_root, 0)
3071
            finally:
3072
                # make sure to release the grab (Tk 8.0a1 only)
3073
                self.controllerPopup.grab_release()
3074

    
3075
    def do_legacyRouterPopup(self, event):
3076
        # display the popup menu
3077
        if self.net is not None:
3078
            try:
3079
                self.legacyRouterRunPopup.tk_popup(event.x_root, event.y_root, 0)
3080
            finally:
3081
                # make sure to release the grab (Tk 8.0a1 only)
3082
                self.legacyRouterRunPopup.grab_release()
3083

    
3084
    def do_hostPopup(self, event):
3085
        # display the popup menu
3086
        if self.net is None:
3087
            try:
3088
                self.hostPopup.tk_popup(event.x_root, event.y_root, 0)
3089
            finally:
3090
                # make sure to release the grab (Tk 8.0a1 only)
3091
                self.hostPopup.grab_release()
3092
        else:
3093
            try:
3094
                self.hostRunPopup.tk_popup(event.x_root, event.y_root, 0)
3095
            finally:
3096
                # make sure to release the grab (Tk 8.0a1 only)
3097
                self.hostRunPopup.grab_release()
3098

    
3099
    def do_legacySwitchPopup(self, event):
3100
        # display the popup menu
3101
        if self.net is not None:
3102
            try:
3103
                self.switchRunPopup.tk_popup(event.x_root, event.y_root, 0)
3104
            finally:
3105
                # make sure to release the grab (Tk 8.0a1 only)
3106
                self.switchRunPopup.grab_release()
3107

    
3108
    def do_switchPopup(self, event):
3109
        # display the popup menu
3110
        if self.net is None:
3111
            try:
3112
                self.switchPopup.tk_popup(event.x_root, event.y_root, 0)
3113
            finally:
3114
                # make sure to release the grab (Tk 8.0a1 only)
3115
                self.switchPopup.grab_release()
3116
        else:
3117
            try:
3118
                self.switchRunPopup.tk_popup(event.x_root, event.y_root, 0)
3119
            finally:
3120
                # make sure to release the grab (Tk 8.0a1 only)
3121
                self.switchRunPopup.grab_release()
3122

    
3123
    def xterm( self, _ignore=None ):
3124
        "Make an xterm when a button is pressed."
3125
        if ( self.selection is None or
3126
             self.net is None or
3127
             self.selection not in self.itemToWidget ):
3128
            return
3129
        name = self.itemToWidget[ self.selection ][ 'text' ]
3130
        if name not in self.net.nameToNode:
3131
            return
3132
        term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
3133
        if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
3134
            self.net.terms += term
3135
        else:
3136
            self.net.terms.append(term)
3137

    
3138
    def iperf( self, _ignore=None ):
3139
        "Make an xterm when a button is pressed."
3140
        if ( self.selection is None or
3141
             self.net is None or
3142
             self.selection not in self.itemToWidget ):
3143
            return
3144
        name = self.itemToWidget[ self.selection ][ 'text' ]
3145
        if name not in self.net.nameToNode:
3146
            return
3147
        self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
3148

    
3149
    ### BELOW HERE IS THE TOPOLOGY IMPORT CODE ###
3150

    
3151
    def parseArgs( self ):
3152
        """Parse command-line args and return options object.
3153
           returns: opts parse options dict"""
3154

    
3155
        if '--custom' in sys.argv:
3156
            index = sys.argv.index( '--custom' )
3157
            if len( sys.argv ) > index + 1:
3158
                filename = sys.argv[ index + 1 ]
3159
                self.parseCustomFile( filename )
3160
            else:
3161
                raise Exception( 'Custom file name not found' )
3162

    
3163
        desc = ( "The %prog utility creates Mininet network from the\n"
3164
                 "command line. It can create parametrized topologies,\n"
3165
                 "invoke the Mininet CLI, and run tests." )
3166

    
3167
        usage = ( '%prog [options]\n'
3168
                  '(type %prog -h for details)' )
3169

    
3170
        opts = OptionParser( description=desc, usage=usage )
3171

    
3172
        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
3173
        addDictOption( opts, LINKS, LINKDEF, 'link' )
3174

    
3175
        opts.add_option( '--custom', type='string', default=None,
3176
                         help='read custom topo and node params from .py' +
3177
                         'file' )
3178

    
3179
        self.options, self.args = opts.parse_args()
3180
        # We don't accept extra arguments after the options
3181
        if self.args:
3182
            opts.print_help()
3183
            exit()
3184

    
3185
    def setCustom( self, name, value ):
3186
        "Set custom parameters for MininetRunner."
3187
        if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
3188
            # Update dictionaries
3189
            param = name.upper()
3190
            globals()[ param ].update( value )
3191
        elif name == 'validate':
3192
            # Add custom validate function
3193
            self.validate = value
3194
        else:
3195
            # Add or modify global variable or class
3196
            globals()[ name ] = value
3197

    
3198
    def parseCustomFile( self, fileName ):
3199
        "Parse custom file and add params before parsing cmd-line options."
3200
        customs = {}
3201
        if os.path.isfile( fileName ):
3202
            execfile( fileName, customs, customs )
3203
            for name, val in customs.iteritems():
3204
                self.setCustom( name, val )
3205
        else:
3206
            raise Exception( 'could not find custom file: %s' % fileName )
3207

    
3208
    def importTopo( self ):
3209
        print 'topo='+self.options.topo
3210
        if self.options.topo == 'none':
3211
            return
3212
        self.newTopology()
3213
        topo = buildTopo( TOPOS, self.options.topo )
3214
        link = customConstructor( LINKS, self.options.link )
3215
        importNet = Mininet(topo=topo, build=False, link=link)
3216
        importNet.build()
3217

    
3218
        c = self.canvas
3219
        rowIncrement = 100
3220
        currentY = 100
3221

    
3222
        # Add Controllers
3223
        print 'controllers:'+str(len(importNet.controllers))
3224
        for controller in importNet.controllers:
3225
            name = controller.name
3226
            x = self.controllerCount*100+100
3227
            self.addNode('Controller', self.controllerCount,
3228
                 float(x), float(currentY), name=name)
3229
            icon = self.findWidgetByName(name)
3230
            icon.bind('<Button-3>', self.do_controllerPopup )
3231
            ctrlr = { 'controllerType': 'ref',
3232
                      'hostname': name,
3233
                      'controllerProtocol': controller.protocol,
3234
                      'remoteIP': controller.ip,
3235
                      'remotePort': controller.port}
3236
            self.controllers[name] = ctrlr
3237

    
3238

    
3239

    
3240
        currentY = currentY + rowIncrement
3241

    
3242
        # Add switches
3243
        print 'switches:'+str(len(importNet.switches))
3244
        columnCount = 0
3245
        for switch in importNet.switches:
3246
            name = switch.name
3247
            self.switchOpts[name] = {}
3248
            self.switchOpts[name]['nodeNum']=self.switchCount
3249
            self.switchOpts[name]['hostname']=name
3250
            self.switchOpts[name]['switchType']='default'
3251
            self.switchOpts[name]['controllers']=[]
3252

    
3253
            x = columnCount*100+100
3254
            self.addNode('Switch', self.switchCount,
3255
                 float(x), float(currentY), name=name)
3256
            icon = self.findWidgetByName(name)
3257
            icon.bind('<Button-3>', self.do_switchPopup )
3258
            # Now link to controllers
3259
            for controller in importNet.controllers:
3260
                self.switchOpts[name]['controllers'].append(controller.name)
3261
                dest = self.findWidgetByName(controller.name)
3262
                dx, dy = c.coords( self.widgetToItem[ dest ] )
3263
                self.link = c.create_line(float(x),
3264
                                          float(currentY),
3265
                                          dx,
3266
                                          dy,
3267
                                          width=4,
3268
                                          fill='red',
3269
                                          dash=(6, 4, 2, 4),
3270
                                          tag='link' )
3271
                c.itemconfig(self.link, tags=c.gettags(self.link)+('control',))
3272
                self.addLink( icon, dest, linktype='control' )
3273
                self.createControlLinkBindings()
3274
                self.link = self.linkWidget = None
3275
            if columnCount == 9:
3276
                columnCount = 0
3277
                currentY = currentY + rowIncrement
3278
            else:
3279
                columnCount =columnCount+1
3280

    
3281

    
3282
        currentY = currentY + rowIncrement
3283
        # Add hosts
3284
        print 'hosts:'+str(len(importNet.hosts))
3285
        columnCount = 0
3286
        for host in importNet.hosts:
3287
            name = host.name
3288
            self.hostOpts[name] = {'sched':'host'}
3289
            self.hostOpts[name]['nodeNum']=self.hostCount
3290
            self.hostOpts[name]['hostname']=name
3291
            self.hostOpts[name]['ip']=host.IP()
3292

    
3293
            x = columnCount*100+100
3294
            self.addNode('Host', self.hostCount,
3295
                 float(x), float(currentY), name=name)
3296
            icon = self.findWidgetByName(name)
3297
            icon.bind('<Button-3>', self.do_hostPopup )
3298
            if columnCount == 9:
3299
                columnCount = 0
3300
                currentY = currentY + rowIncrement
3301
            else:
3302
                columnCount =columnCount+1
3303

    
3304
        print 'links:'+str(len(topo.links()))
3305
        #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
3306
        for link in topo.links():
3307
            print str(link)
3308
            srcNode = link[0]
3309
            src = self.findWidgetByName(srcNode)
3310
            sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
3311

    
3312
            destNode = link[1]
3313
            dest = self.findWidgetByName(destNode)
3314
            dx, dy = self.canvas.coords( self.widgetToItem[ dest]  )
3315

    
3316
            params = topo.linkInfo( srcNode, destNode )
3317
            print 'Link Parameters='+str(params)
3318

    
3319
            self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
3320
                                             fill='blue', tag='link' )
3321
            c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
3322
            self.addLink( src, dest, linkopts=params )
3323
            self.createDataLinkBindings()
3324
            self.link = self.linkWidget = None
3325

    
3326
        importNet.stop()
3327

    
3328
def miniEditImages():
3329
    "Create and return images for MiniEdit."
3330

    
3331
    # Image data. Git will be unhappy. However, the alternative
3332
    # is to keep track of separate binary files, which is also
3333
    # unappealing.
3334

    
3335
    return {
3336
        'Select': BitmapImage(
3337
            file='/usr/include/X11/bitmaps/left_ptr' ),
3338

    
3339
        'Switch': PhotoImage( data=r"""
3340
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
3371
            """),
3372

    
3373
        'LegacySwitch': PhotoImage( data=r"""
3374
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
3405
4BE2eIRYeHAEIBwBP0Y4Qn41YWRSCQgAOw==
3406
            """),
3407

    
3408
        'LegacyRouter': PhotoImage( data=r"""
3409
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
3438
gGPLHwLwcMIo12Qxu0ABAQA7
3439
            """),
3440

    
3441
        'Controller': PhotoImage( data=r"""
3442
            R0lGODlhMAAwAPcAAAEBAWfNAYWFhcfHx+3t6/f390lJUaWlpfPz8/Hx72lpaZGRke/v77m5uc0B
3443
            AeHh4e/v7WNjY3t7e5eXlyMjI4mJidPT0+3t7f///09PT7Ozs/X19fHx8ZWTk8HBwX9/fwAAAAAA
3444
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3445
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3446
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3447
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3448
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3449
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3450
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3451
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3452
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3453
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3454
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3455
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAwADAA
3456
            Bwj/AAEIHEiwoMGDCBMqXMiwocOHECNKnEixosWLGAEIeMCxo8ePHwVkBGABg8mTKFOmtDByAIYN
3457
            MGPCRCCzQIENNzEMGOkBAwIKQIMKpYCgKAIHCDB4GNkAA4OnUJ9++CDhQ1QGFzA0GKkBA4GvYMOK
3458
            BYtBA1cNaNOqXcuWq8q3b81m7Cqzbk2bMMu6/Tl0qFEEAZLKxdj1KlSqVA3rnet1rOOwiwmznUzZ
3459
            LdzLJgdfpIv3pmebN2Pm1GyRbocNp1PLNMDaAM3Im1/alQk4gO28pCt2RdCBt+/eRg8IP1AUdmmf
3460
            f5MrL56bYlcOvaP7Xo6Ag3HdGDho3869u/YE1507t+3AgLz58ujPMwg/sTBUCAzgy49PH0LW5u0x
3461
            XFiwvz////5dcJ9bjxVIAHsSdUXAAgs2yOCDDn6FYEQaFGDgYxNCpEFfHHKIX4IDhCjiiCSS+CGF
3462
            FlCmogYpcnVABTDGKGOMAlRQYwUHnKjhAjX2aOOPN8LImgAL6PiQBhLMqCSNAThQgQRGOqRBBD1W
3463
            aaOVAggnQARRNqRBBxmEKeaYZIrZQZcMKbDiigqM5OabcMYp55x01ilnQAA7
3464
            """),
3465

    
3466
        'Host': PhotoImage( data=r"""
3467
            R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
3468
            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
3469
            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
3470
            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
3471
            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
3472
            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
3473
            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
3474
            mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
3475
            ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
3476
            M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
3477
            AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
3478
            /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
3479
            zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
3480
            mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
3481
            ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
3482
            MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
3483
            AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
3484
            ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
3485
            AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
3486
            RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
3487
            ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
3488
            BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
3489
            HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
3490
            p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
3491
            C8cSBBAQADs=
3492
        """ ),
3493

    
3494
        'OldSwitch': PhotoImage( data=r"""
3495
            R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
3496
            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
3497
            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
3498
            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
3499
            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz