Statistics
| Branch: | Tag: | Revision:

mininet / examples / miniedit.py @ 18aab5b7

History | View | Annotate | Download (150 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
# For now, tolerate long lines and long module
17
# pylint: disable=line-too-long,too-many-lines
18

    
19
MINIEDIT_VERSION = '2.2.0.1'
20

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

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

    
44
# someday: from ttk import *
45

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

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

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

    
87

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

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

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

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

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

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

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

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

    
137
class customOvs(OVSSwitch):
138
    "Customized OVS switch"
139

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

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

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

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

    
160
class PrefsDialog(tkSimpleDialog.Dialog):
161
    "Preferences dialog"
162

    
163
    def __init__(self, parent, title, prefDefaults):
164

    
165
        self.prefValues = prefDefaults
166

    
167
        tkSimpleDialog.Dialog.__init__(self, parent, title)
168

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

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

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

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

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

    
217

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
314
        # initial focus
315
        return self.ipEntry
316

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

    
324
        ovsOf10 = str(self.ovsOf10.get())
325
        ovsOf11 = str(self.ovsOf11.get())
326
        ovsOf12 = str(self.ovsOf12.get())
327
        ovsOf13 = str(self.ovsOf13.get())
328

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

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

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

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

    
389

    
390
class CustomDialog(object):
391

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

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

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

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

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

    
412
    def body(self, master):
413
        self.rootFrame = master
414

    
415
    def apply(self):
416
        self.top.destroy()
417

    
418
    def cancelAction(self):
419
        self.top.destroy()
420

    
421
    def okAction(self):
422
        self.apply()
423
        self.top.destroy()
424

    
425
class HostDialog(CustomDialog):
426

    
427
    def __init__(self, master, title, prefDefaults):
428

    
429
        self.prefValues = prefDefaults
430
        self.result = None
431

    
432
        CustomDialog.__init__(self, master, title)
433

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
564

    
565
    def addDirectory( self ):
566
        self.mountTableFrame.addRow()
567

    
568
    def addVlanInterface( self ):
569
        self.vlanTableFrame.addRow()
570

    
571
    def addInterface( self ):
572
        self.tableFrame.addRow()
573

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

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

    
607
class SwitchDialog(CustomDialog):
608

    
609
    def __init__(self, master, title, prefDefaults):
610

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
736
    def addInterface( self ):
737
        self.tableFrame.addRow()
738

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

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

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

    
766

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

    
793

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

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

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

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

    
813
        # reset the view
814
        canvas.xview_moveto(0)
815
        canvas.yview_moveto(0)
816

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

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

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

    
839
        return
840

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

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

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

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

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

    
879
class LinkDialog(tkSimpleDialog.Dialog):
880

    
881
    def __init__(self, parent, title, linkDefaults):
882

    
883
        self.linkValues = linkDefaults
884

    
885
        tkSimpleDialog.Dialog.__init__(self, parent, title)
886

    
887
    def body(self, master):
888

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

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

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

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

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

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

    
928
        return self.e1 # initial focus
929

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

    
945
class ControllerDialog(tkSimpleDialog.Dialog):
946

    
947
    def __init__(self, parent, title, ctrlrDefaults=None):
948

    
949
        if ctrlrDefaults:
950
            self.ctrlrValues = ctrlrDefaults
951

    
952
        tkSimpleDialog.Dialog.__init__(self, parent, title)
953

    
954
    def body(self, master):
955

    
956
        self.var = StringVar(master)
957
        self.protcolvar = StringVar(master)
958

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

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

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

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

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

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

    
1013
        return self.hostnameEntry # initial focus
1014

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

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

    
1035
class ToolTip(object):
1036

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

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

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

    
1072
class MiniEdit( Frame ):
1073

    
1074
    "A simple network editor for Mininet."
1075

    
1076
    def __init__( self, parent=None, cheight=600, cwidth=1000 ):
1077

    
1078
        self.defaultIpBase='10.0.0.0/8'
1079

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

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

    
1101
        }
1102

    
1103

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

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

    
1114
        # Title
1115
        self.top = self.winfo_toplevel()
1116
        self.top.title( self.appName )
1117

    
1118
        # Menu bar
1119
        self.createMenubar()
1120

    
1121
        # Editing canvas
1122
        self.cheight, self.cwidth = cheight, cwidth
1123
        self.cframe, self.canvas = self.createCanvas()
1124

    
1125
        # Toolbar
1126
        self.controllers = {}
1127

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

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

    
1143
        # About box
1144
        self.aboutBox = None
1145

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

    
1152
        # Initialize link tool
1153
        self.link = self.linkWidget = None
1154

    
1155
        # Selection support
1156
        self.selection = None
1157

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

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

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

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

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

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

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

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

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

    
1205

    
1206
        # Event handling initalization
1207
        self.linkx = self.linky = self.linkItem = None
1208
        self.lastSelection = None
1209

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

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

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

    
1227
    def createMenubar( self ):
1228
        "Create our menu bar."
1229

    
1230
        font = self.font
1231

    
1232
        mbar = Menu( self.top, font=font )
1233
        self.top.configure( menu=mbar )
1234

    
1235

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

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

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

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

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

    
1270
        canvas = Canvas( f, width=self.cwidth, height=self.cheight,
1271
                         bg=self.bg )
1272

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

    
1278
        # Resize box
1279
        resize = Label( f, bg='white' )
1280

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

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

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

    
1298
        return f, canvas
1299

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

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

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

    
1317
    # Toolbar
1318

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

    
1328

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

    
1338
    def createToolbar( self ):
1339
        "Create and return our toolbar frame."
1340

    
1341
        toolbar = Frame( self )
1342

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

    
1355
        # Spacer
1356
        Label( toolbar, text='' ).pack()
1357

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

    
1365
        return toolbar
1366

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

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

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

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

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

    
1412
    def loadTopology( self ):
1413
        "Load command."
1414
        c = self.canvas
1415

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

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

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

    
1464

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

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

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

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

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

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

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

    
1576
        f.close
1577

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

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

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

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

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

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

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

    
1657
            # Save Application preferences
1658
            savingDictionary['application'] = self.appPrefs
1659

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

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

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

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

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

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

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

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

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

    
1736

    
1737
                    f.write("    "+name+"=net.addController(name='"+name+"',\n")
1738

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

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

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

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

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

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

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

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

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

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

    
1899
            f.write("\n")
1900

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

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

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

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

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

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

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

    
2018

    
2019
            f.close()
2020

    
2021

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

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

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

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

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

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

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

    
2061
    # Canvas bindings for Select, Host, Switch and Link tools
2062

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2195
    # Generic node handlers
2196

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

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

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

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

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

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

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

    
2244
    # Specific node handlers
2245

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

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

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

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

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

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

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

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

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

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

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

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

    
2319

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

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

    
2335

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

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

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

    
2388
            self.switchOpts[switchName]['controllers'].append(controllerName)
2389

    
2390
        # We're done
2391
        self.link = self.linkWidget = None
2392

    
2393
    # Menu handlers
2394

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

    
2426
    def createToolImages( self ):
2427
        "Create toolbar (and icon) images."
2428

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

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

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

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

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

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

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

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

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

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

    
2568

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

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

    
2602

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

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

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

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

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

    
2627
    def addLink( self, source, dest, linktype='data', linkopts={} ):
2628
        "Add link to model."
2629
        source.links[ dest ] = self.link
2630
        dest.links[ source ] = self.link
2631
        self.links[ self.link ] = {'type' :linktype,
2632
                                   'src':source,
2633
                                   'dest':dest,
2634
                                   'linkOpts':linkopts}
2635

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

    
2648
            if 'control' in ltags:
2649
                controllerName = ''
2650
                switchName = ''
2651
                if 'Controller' in stags:
2652
                    controllerName = source[ 'text' ]
2653
                    switchName = dest[ 'text' ]
2654
                else:
2655
                    controllerName = dest[ 'text' ]
2656
                    switchName = source[ 'text' ]
2657

    
2658
                if controllerName in self.switchOpts[switchName]['controllers']:
2659
                    self.switchOpts[switchName]['controllers'].remove(controllerName)
2660

    
2661

    
2662
        if link is not None:
2663
            del self.links[ link ]
2664

    
2665
    def deleteNode( self, item ):
2666
        "Delete node (and its links) from model."
2667

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

    
2679
        for link in widget.links.values():
2680
            # Delete from view and model
2681
            self.deleteItem( link )
2682
        del self.itemToWidget[ item ]
2683
        del self.widgetToItem[ widget ]
2684

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

    
2693
            if 'Switch' in tags:
2694
                opts = self.switchOpts[name]
2695
                #print str(opts)
2696

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

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

    
2739
                # Some post startup config
2740
                if switchClass == CustomUserSwitch:
2741
                    if 'switchIP' in opts:
2742
                        if len(opts['switchIP']) > 0:
2743
                            newSwitch.setSwitchIP(opts['switchIP'])
2744
                if switchClass == customOvs:
2745
                    if 'switchIP' in opts:
2746
                        if len(opts['switchIP']) > 0:
2747
                            newSwitch.setSwitchIP(opts['switchIP'])
2748

    
2749
                # Attach external interfaces
2750
                if 'externalInterfaces' in opts:
2751
                    for extInterface in opts['externalInterfaces']:
2752
                        if self.checkIntf(extInterface):
2753
                            Intf( extInterface, node=newSwitch )
2754

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

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

    
2793
                # Set the CPULimitedHost specific options
2794
                if 'cores' in opts:
2795
                    newHost.setCPUs(cores = opts['cores'])
2796
                if 'cpu' in opts:
2797
                    newHost.setCPUFrac(f=opts['cpu'], sched=opts['sched'])
2798

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

    
2812
                # Get controller info from panel
2813
                controllerType = opts['controllerType']
2814
                if 'controllerProtocol' in opts:
2815
                    controllerProtocol = opts['controllerProtocol']
2816
                else:
2817
                    controllerProtocol = 'tcp'
2818
                    opts['controllerProtocol'] = 'tcp'
2819
                controllerIP = opts['remoteIP']
2820
                controllerPort = opts['remotePort']
2821

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

    
2847
            else:
2848
                raise Exception( "Cannot create mystery node: " + name )
2849

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

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

    
2879

    
2880
    def build( self ):
2881
        print "Build network based on our topology."
2882

    
2883
        dpctl = None
2884
        if len(self.appPrefs['dpctl']) > 0:
2885
            dpctl = int(self.appPrefs['dpctl'])
2886
        net = Mininet( topo=None,
2887
                       listenPort=dpctl,
2888
                       build=False,
2889
                       ipBase=self.appPrefs['ipBase'] )
2890

    
2891
        self.buildNodes(net)
2892
        self.buildLinks(net)
2893

    
2894
        # Build network (we have to do this separately at the moment )
2895
        net.build()
2896

    
2897
        return net
2898

    
2899

    
2900
    def postStartSetup( self ):
2901

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

    
2924

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

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

    
2950
            else:
2951
                print 'No switches with Netflow'
2952
        else:
2953
            print 'No NetFlow targets specified.'
2954

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

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

    
2976
            else:
2977
                print 'No switches with sflow'
2978
        else:
2979
            print 'No sFlow targets specified.'
2980

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

    
2987
    def start( self ):
2988
        "Start network."
2989
        if self.net is None:
2990
            self.net = self.build()
2991

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

    
3021
            self.postStartSetup()
3022

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

    
3043
            self.net.stop()
3044
        cleanUpScreens()
3045
        self.net = None
3046

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

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

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

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

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

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

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

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

    
3145
    """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
3146

    
3147
    def parseArgs( self ):
3148
        """Parse command-line args and return options object.
3149
           returns: opts parse options dict"""
3150

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

    
3159
        desc = ( "The %prog utility creates Mininet network from the\n"
3160
                 "command line. It can create parametrized topologies,\n"
3161
                 "invoke the Mininet CLI, and run tests." )
3162

    
3163
        usage = ( '%prog [options]\n'
3164
                  '(type %prog -h for details)' )
3165

    
3166
        opts = OptionParser( description=desc, usage=usage )
3167

    
3168
        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
3169
        addDictOption( opts, LINKS, LINKDEF, 'link' )
3170

    
3171
        opts.add_option( '--custom', type='string', default=None,
3172
                         help='read custom topo and node params from .py' +
3173
                         'file' )
3174

    
3175
        self.options, self.args = opts.parse_args()
3176
        # We don't accept extra arguments after the options
3177
        if self.args:
3178
            opts.print_help()
3179
            exit()
3180

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

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

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

    
3214
        c = self.canvas
3215
        rowIncrement = 100
3216
        currentY = 100
3217

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

    
3234

    
3235

    
3236
        currentY = currentY + rowIncrement
3237

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

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

    
3277

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

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

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

    
3308
            destNode = link[1]
3309
            dest = self.findWidgetByName(destNode)
3310
            dx, dy = self.canvas.coords( self.widgetToItem[ dest]  )
3311

    
3312
            params = topo.linkInfo( srcNode, destNode )
3313
            print 'Link Parameters='+str(params)
3314

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

    
3322
        importNet.stop()
3323

    
3324
def miniEditImages():
3325
    "Create and return images for MiniEdit."
3326

    
3327
    # Image data. Git will be unhappy. However, the alternative
3328
    # is to keep track of separate binary files, which is also
3329
    # unappealing.
3330

    
3331
    return {
3332
        'Select': BitmapImage(
3333
            file='/usr/include/X11/bitmaps/left_ptr' ),
3334

    
3335
        'Switch': PhotoImage( data=r"""
3336
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
3367
            """),
3368

    
3369
        'LegacySwitch': PhotoImage( data=r"""
3370
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
3401
4BE2eIRYeHAEIBwBP0Y4Qn41YWRSCQgAOw==
3402
            """),
3403

    
3404
        'LegacyRouter': PhotoImage( data=r"""
3405
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
3434
gGPLHwLwcMIo12Qxu0ABAQA7
3435
            """),
3436

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

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

    
3490
        'OldSwitch': PhotoImage( data=r"""
3491
            R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
3492
            mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
3493
            Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
3494
            M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
3495
            AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
3496
            /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
3497
            zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
3498
<