blob: 5ca619135fa67bd7af4cf793e54650ab799fda42 [file] [log] [blame]
carlosmscabralf40ecd12013-02-01 18:15:58 -02001#!/usr/bin/python
2
3"""
Caio Elias47b243c2014-11-23 19:25:34 -02004MiniEdit: a simple network editor for Mininet
carlosmscabralf40ecd12013-02-01 18:15:58 -02005
Caio Elias47b243c2014-11-23 19:25:34 -02006This is a simple demonstration of how one might build a
7GUI application using Mininet as the network model.
carlosmscabralf40ecd12013-02-01 18:15:58 -02008
Caio Elias47b243c2014-11-23 19:25:34 -02009Bob Lantz, April 2010
10Gregory Gee, July 2013
carlosmscabralf40ecd12013-02-01 18:15:58 -020011
Caio Elias47b243c2014-11-23 19:25:34 -020012Controller icon from http://semlabs.co.uk/
13OpenFlow icon from https://www.opennetworking.org/
carlosmscabralf40ecd12013-02-01 18:15:58 -020014"""
carlosmscabralf40ecd12013-02-01 18:15:58 -020015
Caio Elias47b243c2014-11-23 19:25:34 -020016MINIEDIT_VERSION = '2.2.0.1'
17
18from optparse import OptionParser
19from Tkinter import *
20from ttk import Notebook
21from tkMessageBox import showinfo, showerror, showwarning
22from subprocess import call
23import tkFont
24import csv
25import tkFileDialog
26import tkSimpleDialog
27import re
28import json
29from distutils.version import StrictVersion
30import os
31import sys
32from functools import partial
33
34if 'PYTHONPATH' in os.environ:
35 sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
carlosmscabralf40ecd12013-02-01 18:15:58 -020036
37# someday: from ttk import *
38
Caio Elias47b243c2014-11-23 19:25:34 -020039from mininet.log import info, error, debug, output, setLogLevel
40from mininet.net import Mininet, VERSION
41from mininet.util import ipStr, netParse, ipAdd, quietRun
42from mininet.util import buildTopo
43from mininet.util import custom, customConstructor
carlosmscabralf40ecd12013-02-01 18:15:58 -020044from mininet.term import makeTerm, cleanUpScreens
Caio Elias47b243c2014-11-23 19:25:34 -020045from mininet.node import Controller, RemoteController, NOX, OVSController
46from mininet.node import CPULimitedHost, Host, Node
47from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch
48from mininet.link import TCLink, Intf, Link
49from mininet.cli import CLI
50from mininet.moduledeps import moduleDeps, pathCheck
51from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
52from mininet.topolib import TreeTopo
53
54print 'MiniEdit running against MiniNet '+VERSION
55MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
56if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
57 from mininet.node import IVSSwitch
58
59TOPODEF = 'none'
60TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
61 'linear': LinearTopo,
62 'reversed': SingleSwitchReversedTopo,
63 'single': SingleSwitchTopo,
64 'none': None,
65 'tree': TreeTopo }
66LINKDEF = 'default'
67LINKS = { 'default': Link,
68 'tc': TCLink }
69HOSTDEF = 'proc'
70HOSTS = { 'proc': Host,
71 'rt': custom( CPULimitedHost, sched='rt' ),
72 'cfs': custom( CPULimitedHost, sched='cfs' ) }
73
74class LegacyRouter( Node ):
75
76 def __init__( self, name, inNamespace=True, **params ):
77 Node.__init__( self, name, inNamespace, **params )
78
79 def config( self, **_params ):
80 if self.intfs:
81 self.setParam( _params, 'setIP', ip='0.0.0.0' )
82 r = Node.config( self, **_params )
83 self.cmd('sysctl -w net.ipv4.ip_forward=1')
84 return r
85
86class CustomDialog(object):
87
88 # TODO: Fix button placement and Title and window focus lock
89 def __init__(self, master, title):
90 self.top=Toplevel(master)
91
92 self.bodyFrame = Frame(self.top)
93 self.bodyFrame.grid(row=0, column=0, sticky='nswe')
94 self.body(self.bodyFrame)
95
96 #return self.b # initial focus
97 buttonFrame = Frame(self.top, relief='ridge', bd=3, bg='lightgrey')
98 buttonFrame.grid(row=1 , column=0, sticky='nswe')
99
100 okButton = Button(buttonFrame, width=8, text='OK', relief='groove',
101 bd=4, command=self.okAction)
102 okButton.grid(row=0, column=0, sticky=E)
103
104 canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
105 bd=4, command=self.cancelAction)
106 canlceButton.grid(row=0, column=1, sticky=W)
107
108 def body(self, master):
109 self.rootFrame = master
110
111 def apply(self):
112 self.top.destroy()
113
114 def cancelAction(self):
115 self.top.destroy()
116
117 def okAction(self):
118 self.apply()
119 self.top.destroy()
120
121class HostDialog(CustomDialog):
122
123 def __init__(self, master, title, prefDefaults):
124
125 self.prefValues = prefDefaults
126 self.result = None
127
128 CustomDialog.__init__(self, master, title)
129
130 def body(self, master):
131 self.rootFrame = master
132 n = Notebook(self.rootFrame)
133 self.propFrame = Frame(n)
134 self.vlanFrame = Frame(n)
135 self.interfaceFrame = Frame(n)
136 self.mountFrame = Frame(n)
137 n.add(self.propFrame, text='Properties')
138 n.add(self.vlanFrame, text='VLAN Interfaces')
139 n.add(self.interfaceFrame, text='External Interfaces')
140 n.add(self.mountFrame, text='Private Directories')
141 n.pack()
142
143 ### TAB 1
144 # Field for Hostname
145 Label(self.propFrame, text="Hostname:").grid(row=0, sticky=E)
146 self.hostnameEntry = Entry(self.propFrame)
147 self.hostnameEntry.grid(row=0, column=1)
148 if 'hostname' in self.prefValues:
149 self.hostnameEntry.insert(0, self.prefValues['hostname'])
150
151 # Field for Switch IP
152 Label(self.propFrame, text="IP Address:").grid(row=1, sticky=E)
153 self.ipEntry = Entry(self.propFrame)
154 self.ipEntry.grid(row=1, column=1)
155 if 'ip' in self.prefValues:
156 self.ipEntry.insert(0, self.prefValues['ip'])
157
158 # Field for default route
159 Label(self.propFrame, text="Default Route:").grid(row=2, sticky=E)
160 self.routeEntry = Entry(self.propFrame)
161 self.routeEntry.grid(row=2, column=1)
162 if 'defaultRoute' in self.prefValues:
163 self.routeEntry.insert(0, self.prefValues['defaultRoute'])
164
165 # Field for CPU
166 Label(self.propFrame, text="Amount CPU:").grid(row=3, sticky=E)
167 self.cpuEntry = Entry(self.propFrame)
168 self.cpuEntry.grid(row=3, column=1)
169 if 'cpu' in self.prefValues:
170 self.cpuEntry.insert(0, str(self.prefValues['cpu']))
171 # Selection of Scheduler
172 if 'sched' in self.prefValues:
173 sched = self.prefValues['sched']
174 else:
175 sched = 'host'
176 self.schedVar = StringVar(self.propFrame)
177 self.schedOption = OptionMenu(self.propFrame, self.schedVar, "host", "cfs", "rt")
178 self.schedOption.grid(row=3, column=2, sticky=W)
179 self.schedVar.set(sched)
180
181 # Selection of Cores
182 Label(self.propFrame, text="Cores:").grid(row=4, sticky=E)
183 self.coreEntry = Entry(self.propFrame)
184 self.coreEntry.grid(row=4, column=1)
185 if 'cores' in self.prefValues:
186 self.coreEntry.insert(1, self.prefValues['cores'])
187
188 # Start command
189 Label(self.propFrame, text="Start Command:").grid(row=5, sticky=E)
190 self.startEntry = Entry(self.propFrame)
191 self.startEntry.grid(row=5, column=1, sticky='nswe', columnspan=3)
192 if 'startCommand' in self.prefValues:
193 self.startEntry.insert(0, str(self.prefValues['startCommand']))
194 # Stop command
195 Label(self.propFrame, text="Stop Command:").grid(row=6, sticky=E)
196 self.stopEntry = Entry(self.propFrame)
197 self.stopEntry.grid(row=6, column=1, sticky='nswe', columnspan=3)
198 if 'stopCommand' in self.prefValues:
199 self.stopEntry.insert(0, str(self.prefValues['stopCommand']))
200
201 ### TAB 2
202 # External Interfaces
203 self.externalInterfaces = 0
204 Label(self.interfaceFrame, text="External Interface:").grid(row=0, column=0, sticky=E)
205 self.b = Button( self.interfaceFrame, text='Add', command=self.addInterface)
206 self.b.grid(row=0, column=1)
207
208 self.interfaceFrame = VerticalScrolledTable(self.interfaceFrame, rows=0, columns=1, title='External Interfaces')
209 self.interfaceFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
210 self.tableFrame = self.interfaceFrame.interior
211 self.tableFrame.addRow(value=['Interface Name'], readonly=True)
212
213 # Add defined interfaces
214 externalInterfaces = []
215 if 'externalInterfaces' in self.prefValues:
216 externalInterfaces = self.prefValues['externalInterfaces']
217
218 for externalInterface in externalInterfaces:
219 self.tableFrame.addRow(value=[externalInterface])
220
221 ### TAB 3
222 # VLAN Interfaces
223 self.vlanInterfaces = 0
224 Label(self.vlanFrame, text="VLAN Interface:").grid(row=0, column=0, sticky=E)
225 self.vlanButton = Button( self.vlanFrame, text='Add', command=self.addVlanInterface)
226 self.vlanButton.grid(row=0, column=1)
227
228 self.vlanFrame = VerticalScrolledTable(self.vlanFrame, rows=0, columns=2, title='VLAN Interfaces')
229 self.vlanFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
230 self.vlanTableFrame = self.vlanFrame.interior
231 self.vlanTableFrame.addRow(value=['IP Address','VLAN ID'], readonly=True)
232
233 vlanInterfaces = []
234 if 'vlanInterfaces' in self.prefValues:
235 vlanInterfaces = self.prefValues['vlanInterfaces']
236 for vlanInterface in vlanInterfaces:
237 self.vlanTableFrame.addRow(value=vlanInterface)
238
239 ### TAB 4
240 # Private Directories
241 self.privateDirectories = 0
242 Label(self.mountFrame, text="Private Directory:").grid(row=0, column=0, sticky=E)
243 self.mountButton = Button( self.mountFrame, text='Add', command=self.addDirectory)
244 self.mountButton.grid(row=0, column=1)
245
246 self.mountFrame = VerticalScrolledTable(self.mountFrame, rows=0, columns=2, title='Directories')
247 self.mountFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
248 self.mountTableFrame = self.mountFrame.interior
249 self.mountTableFrame.addRow(value=['Mount','Persistent Directory'], readonly=True)
250
251 directoryList = []
252 if 'privateDirectory' in self.prefValues:
253 directoryList = self.prefValues['privateDirectory']
254 for privateDir in directoryList:
255 if isinstance( privateDir, tuple ):
256 self.mountTableFrame.addRow(value=privateDir)
257 else:
258 self.mountTableFrame.addRow(value=[privateDir,''])
carlosmscabralf40ecd12013-02-01 18:15:58 -0200259
260
Caio Elias47b243c2014-11-23 19:25:34 -0200261 def addDirectory( self ):
262 self.mountTableFrame.addRow()
carlosmscabralf40ecd12013-02-01 18:15:58 -0200263
Caio Elias47b243c2014-11-23 19:25:34 -0200264 def addVlanInterface( self ):
265 self.vlanTableFrame.addRow()
266
267 def addInterface( self ):
268 self.tableFrame.addRow()
269
270 def apply(self):
271 externalInterfaces = []
272 for row in range(self.tableFrame.rows):
273 if (len(self.tableFrame.get(row, 0)) > 0 and
274 row > 0):
275 externalInterfaces.append(self.tableFrame.get(row, 0))
276 vlanInterfaces = []
277 for row in range(self.vlanTableFrame.rows):
278 if (len(self.vlanTableFrame.get(row, 0)) > 0 and
279 len(self.vlanTableFrame.get(row, 1)) > 0 and
280 row > 0):
281 vlanInterfaces.append([self.vlanTableFrame.get(row, 0), self.vlanTableFrame.get(row, 1)])
282 privateDirectories = []
283 for row in range(self.mountTableFrame.rows):
284 if (len(self.mountTableFrame.get(row, 0)) > 0 and row > 0):
285 if(len(self.mountTableFrame.get(row, 1)) > 0):
286 privateDirectories.append((self.mountTableFrame.get(row, 0), self.mountTableFrame.get(row, 1)))
287 else:
288 privateDirectories.append(self.mountTableFrame.get(row, 0))
289
290 results = {'cpu': self.cpuEntry.get(),
291 'cores':self.coreEntry.get(),
292 'sched':self.schedVar.get(),
293 'hostname':self.hostnameEntry.get(),
294 'ip':self.ipEntry.get(),
295 'defaultRoute':self.routeEntry.get(),
296 'startCommand':self.startEntry.get(),
297 'stopCommand':self.stopEntry.get(),
298 'privateDirectory':privateDirectories,
299 'externalInterfaces':externalInterfaces,
300 'vlanInterfaces':vlanInterfaces}
301 self.result = results
302
303
304class VerticalScrolledTable(LabelFrame):
305 """A pure Tkinter scrollable frame that actually works!
306
307 * Use the 'interior' attribute to place widgets inside the scrollable frame
308 * Construct and pack/place/grid normally
309 * This frame only allows vertical scrolling
carlosmscabralf40ecd12013-02-01 18:15:58 -0200310
Caio Elias47b243c2014-11-23 19:25:34 -0200311 """
312 def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
313 LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
carlosmscabralf40ecd12013-02-01 18:15:58 -0200314
Caio Elias47b243c2014-11-23 19:25:34 -0200315 # create a canvas object and a vertical scrollbar for scrolling it
316 vscrollbar = Scrollbar(self, orient=VERTICAL)
317 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
318 canvas = Canvas(self, bd=0, highlightthickness=0,
319 yscrollcommand=vscrollbar.set)
320 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
321 vscrollbar.config(command=canvas.yview)
322
323 # reset the view
324 canvas.xview_moveto(0)
325 canvas.yview_moveto(0)
326
327 # create a frame inside the canvas which will be scrolled with it
328 self.interior = interior = TableFrame(canvas, rows=rows, columns=columns)
329 interior_id = canvas.create_window(0, 0, window=interior,
330 anchor=NW)
331
332 # track changes to the canvas and frame width and sync them,
333 # also updating the scrollbar
334 def _configure_interior(event):
335 # update the scrollbars to match the size of the inner frame
336 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
337 canvas.config(scrollregion="0 0 %s %s" % size)
338 if interior.winfo_reqwidth() != canvas.winfo_width():
339 # update the canvas's width to fit the inner frame
340 canvas.config(width=interior.winfo_reqwidth())
341 interior.bind('<Configure>', _configure_interior)
342
343 def _configure_canvas(event):
344 if interior.winfo_reqwidth() != canvas.winfo_width():
345 # update the inner frame's width to fill the canvas
346 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
347 canvas.bind('<Configure>', _configure_canvas)
348
349 return
350
351class TableFrame(Frame):
352 def __init__(self, parent, rows=2, columns=2):
353
354 Frame.__init__(self, parent, background="black")
355 self._widgets = []
356 self.rows = rows
357 self.columns = columns
358 for row in range(rows):
359 current_row = []
360 for column in range(columns):
361 label = Entry(self, borderwidth=0)
362 label.grid(row=row, column=column, sticky="wens", padx=1, pady=1)
363 current_row.append(label)
364 self._widgets.append(current_row)
365
366 def set(self, row, column, value):
367 widget = self._widgets[row][column]
368 widget.insert(0, value)
369
370 def get(self, row, column):
371 widget = self._widgets[row][column]
372 return widget.get()
373
374 def addRow( self, value=None, readonly=False ):
375 #print "Adding row " + str(self.rows +1)
376 current_row = []
377 for column in range(self.columns):
378 label = Entry(self, borderwidth=0)
379 label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
380 if value is not None:
381 label.insert(0, value[column])
382 if (readonly == True):
383 label.configure(state='readonly')
384 current_row.append(label)
385 self._widgets.append(current_row)
386 self.update_idletasks()
387 self.rows += 1
388
389class LinkDialog(tkSimpleDialog.Dialog):
390
391 def __init__(self, parent, title, linkDefaults):
392
393 self.linkValues = linkDefaults
394
395 tkSimpleDialog.Dialog.__init__(self, parent, title)
396
397 def body(self, master):
398
399 self.var = StringVar(master)
400 Label(master, text="Bandwidth:").grid(row=0, sticky=E)
401 self.e1 = Entry(master)
402 self.e1.grid(row=0, column=1)
403 Label(master, text="Mbit").grid(row=0, column=2, sticky=W)
404 if 'bw' in self.linkValues:
405 self.e1.insert(0,str(self.linkValues['bw']))
406
407 Label(master, text="Delay:").grid(row=1, sticky=E)
408 self.e2 = Entry(master)
409 self.e2.grid(row=1, column=1)
410 if 'delay' in self.linkValues:
411 self.e2.insert(0, self.linkValues['delay'])
412
413 Label(master, text="Loss:").grid(row=2, sticky=E)
414 self.e3 = Entry(master)
415 self.e3.grid(row=2, column=1)
416 Label(master, text="%").grid(row=2, column=2, sticky=W)
417 if 'loss' in self.linkValues:
418 self.e3.insert(0, str(self.linkValues['loss']))
419
420 Label(master, text="Max Queue size:").grid(row=3, sticky=E)
421 self.e4 = Entry(master)
422 self.e4.grid(row=3, column=1)
423 if 'max_queue_size' in self.linkValues:
424 self.e4.insert(0, str(self.linkValues['max_queue_size']))
425
426 Label(master, text="Jitter:").grid(row=4, sticky=E)
427 self.e5 = Entry(master)
428 self.e5.grid(row=4, column=1)
429 if 'jitter' in self.linkValues:
430 self.e5.insert(0, self.linkValues['jitter'])
431
432 Label(master, text="Speedup:").grid(row=5, sticky=E)
433 self.e6 = Entry(master)
434 self.e6.grid(row=5, column=1)
435 if 'speedup' in self.linkValues:
436 self.e6.insert(0, str(self.linkValues['speedup']))
437
438 return self.e1 # initial focus
439
440 def apply(self):
441 self.result = {}
442 if (len(self.e1.get()) > 0):
443 self.result['bw'] = int(self.e1.get())
444 if (len(self.e2.get()) > 0):
445 self.result['delay'] = self.e2.get()
446 if (len(self.e3.get()) > 0):
447 self.result['loss'] = int(self.e3.get())
448 if (len(self.e4.get()) > 0):
449 self.result['max_queue_size'] = int(self.e4.get())
450 if (len(self.e5.get()) > 0):
451 self.result['jitter'] = self.e5.get()
452 if (len(self.e6.get()) > 0):
453 self.result['speedup'] = int(self.e6.get())
454
455class ControllerDialog(tkSimpleDialog.Dialog):
456
457 def __init__(self, parent, title, ctrlrDefaults=None):
458
459 if ctrlrDefaults:
460 self.ctrlrValues = ctrlrDefaults
461
462 tkSimpleDialog.Dialog.__init__(self, parent, title)
463
464 def body(self, master):
465
466 self.var = StringVar(master)
467 self.protcolvar = StringVar(master)
468
469 rowCount=0
470 # Field for Hostname
471 Label(master, text="Name:").grid(row=rowCount, sticky=E)
472 self.hostnameEntry = Entry(master)
473 self.hostnameEntry.grid(row=rowCount, column=1)
474 self.hostnameEntry.insert(0, self.ctrlrValues['hostname'])
475 rowCount+=1
476
477 # Field for Remove Controller Port
478 Label(master, text="Controller Port:").grid(row=rowCount, sticky=E)
479 self.e2 = Entry(master)
480 self.e2.grid(row=rowCount, column=1)
481 self.e2.insert(0, self.ctrlrValues['remotePort'])
482 rowCount+=1
483
484 # Field for Controller Type
485 Label(master, text="Controller Type:").grid(row=rowCount, sticky=E)
486 controllerType = self.ctrlrValues['controllerType']
487 self.o1 = OptionMenu(master, self.var, "Remote Controller", "In-Band Controller", "OpenFlow Reference", "OVS Controller")
488 self.o1.grid(row=rowCount, column=1, sticky=W)
489 if controllerType == 'ref':
490 self.var.set("OpenFlow Reference")
491 elif controllerType == 'inband':
492 self.var.set("In-Band Controller")
493 elif controllerType == 'remote':
494 self.var.set("Remote Controller")
495 else:
496 self.var.set("OVS Controller")
497 rowCount+=1
498
499 # Field for Controller Protcol
500 Label(master, text="Protocol:").grid(row=rowCount, sticky=E)
501 if 'controllerProtocol' in self.ctrlrValues:
502 controllerProtocol = self.ctrlrValues['controllerProtocol']
503 else:
504 controllerProtocol = 'tcp'
505 self.protcol = OptionMenu(master, self.protcolvar, "TCP", "SSL")
506 self.protcol.grid(row=rowCount, column=1, sticky=W)
507 if controllerProtocol == 'ssl':
508 self.protcolvar.set("SSL")
509 else:
510 self.protcolvar.set("TCP")
511 rowCount+=1
512
513 # Field for Remove Controller IP
514 remoteFrame= LabelFrame(master, text='Remote/In-Band Controller', padx=5, pady=5)
515 remoteFrame.grid(row=rowCount, column=0, columnspan=2, sticky=W)
516
517 Label(remoteFrame, text="IP Address:").grid(row=0, sticky=E)
518 self.e1 = Entry(remoteFrame)
519 self.e1.grid(row=0, column=1)
520 self.e1.insert(0, self.ctrlrValues['remoteIP'])
521 rowCount+=1
522
523 return self.hostnameEntry # initial focus
524
525 def apply(self):
526 self.result = { 'hostname': self.hostnameEntry.get(),
527 'remoteIP': self.e1.get(),
528 'remotePort': int(self.e2.get())}
529
530 controllerType = self.var.get()
531 if controllerType == 'Remote Controller':
532 self.result['controllerType'] = 'remote'
533 elif controllerType == 'In-Band Controller':
534 self.result['controllerType'] = 'inband'
535 elif controllerType == 'OpenFlow Reference':
536 self.result['controllerType'] = 'ref'
537 else:
538 self.result['controllerType'] = 'ovsc'
539 controllerProtocol = self.protcolvar.get()
540 if controllerProtocol == 'SSL':
541 self.result['controllerProtocol'] = 'ssl'
542 else:
543 self.result['controllerProtocol'] = 'tcp'
544
545class ToolTip(object):
546
547 def __init__(self, widget):
548 self.widget = widget
549 self.tipwindow = None
550 self.id = None
551 self.x = self.y = 0
552
553 def showtip(self, text):
554 "Display text in tooltip window"
555 self.text = text
556 if self.tipwindow or not self.text:
557 return
558 x, y, cx, cy = self.widget.bbox("insert")
559 x = x + self.widget.winfo_rootx() + 27
560 y = y + cy + self.widget.winfo_rooty() +27
561 self.tipwindow = tw = Toplevel(self.widget)
562 tw.wm_overrideredirect(1)
563 tw.wm_geometry("+%d+%d" % (x, y))
564 try:
565 # For Mac OS
566 tw.tk.call("::tk::unsupported::MacWindowStyle",
567 "style", tw._w,
568 "help", "noActivates")
569 except TclError:
570 pass
571 label = Label(tw, text=self.text, justify=LEFT,
572 background="#ffffe0", relief=SOLID, borderwidth=1,
573 font=("tahoma", "8", "normal"))
574 label.pack(ipadx=1)
575
576 def hidetip(self):
577 tw = self.tipwindow
578 self.tipwindow = None
579 if tw:
580 tw.destroy()
carlosmscabralf40ecd12013-02-01 18:15:58 -0200581
582class MiniEdit( Frame ):
583
584 "A simple network editor for Mininet."
585
Caio Elias47b243c2014-11-23 19:25:34 -0200586 def __init__( self, parent=None, cheight=600, cwidth=1000 ):
587
588 self.defaultIpBase='10.0.0.0/8'
589
590 self.nflowDefaults = {'nflowTarget':'',
591 'nflowTimeout':'600',
592 'nflowAddId':'0'}
593 self.sflowDefaults = {'sflowTarget':'',
594 'sflowSampling':'400',
595 'sflowHeader':'128',
596 'sflowPolling':'30'}
597
598 self.appPrefs={
599 "ipBase": self.defaultIpBase,
600 "startCLI": "0",
601 "terminalType": 'xterm',
602 "switchType": 'ovs',
603 "dpctl": '',
604 'sflow':self.sflowDefaults,
605 'netflow':self.nflowDefaults,
606 'openFlowVersions':{'ovsOf10':'1',
607 'ovsOf11':'0',
608 'ovsOf12':'0',
609 'ovsOf13':'0'}
610
611 }
612
carlosmscabralf40ecd12013-02-01 18:15:58 -0200613
614 Frame.__init__( self, parent )
615 self.action = None
Caio Elias47b243c2014-11-23 19:25:34 -0200616 self.appName = 'MiniEdit'
617 self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
carlosmscabralf40ecd12013-02-01 18:15:58 -0200618
619 # Style
620 self.font = ( 'Geneva', 9 )
621 self.smallFont = ( 'Geneva', 7 )
622 self.bg = 'white'
623
624 # Title
625 self.top = self.winfo_toplevel()
626 self.top.title( self.appName )
627
628 # Menu bar
629 self.createMenubar()
630
631 # Editing canvas
632 self.cheight, self.cwidth = cheight, cwidth
633 self.cframe, self.canvas = self.createCanvas()
634
635 # Toolbar
Caio Elias47b243c2014-11-23 19:25:34 -0200636 self.controllers = {}
637
638 # Toolbar
carlosmscabralf40ecd12013-02-01 18:15:58 -0200639 self.images = miniEditImages()
640 self.buttons = {}
641 self.active = None
Caio Elias47b243c2014-11-23 19:25:34 -0200642 self.tools = ( 'Select', 'Host', 'LegacyRouter', 'NetLink' )
643 self.customColors = { 'LegacyRouter': 'darkGreen', 'Host': 'blue' }
carlosmscabralf40ecd12013-02-01 18:15:58 -0200644 self.toolbar = self.createToolbar()
645
646 # Layout
647 self.toolbar.grid( column=0, row=0, sticky='nsew')
648 self.cframe.grid( column=1, row=0 )
649 self.columnconfigure( 1, weight=1 )
650 self.rowconfigure( 0, weight=1 )
651 self.pack( expand=True, fill='both' )
652
653 # About box
654 self.aboutBox = None
655
656 # Initialize node data
657 self.nodeBindings = self.createNodeBindings()
Caio Elias47b243c2014-11-23 19:25:34 -0200658 self.nodePrefixes = { 'LegacyRouter': 'r', 'Host': 'h'}
carlosmscabralf40ecd12013-02-01 18:15:58 -0200659 self.widgetToItem = {}
660 self.itemToWidget = {}
661
662 # Initialize link tool
663 self.link = self.linkWidget = None
664
665 # Selection support
666 self.selection = None
667
668 # Keyboard bindings
669 self.bind( '<Control-q>', lambda event: self.quit() )
670 self.bind( '<KeyPress-Delete>', self.deleteSelection )
671 self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
672 self.focus()
673
Caio Elias47b243c2014-11-23 19:25:34 -0200674 #Mouse bindings
675
676 self.bind( '<Button-1>', lambda event: self.clearPopups )
677
678 self.hostPopup = Menu(self.top, tearoff=0)
679 self.hostPopup.add_command(label='Host Options', font=self.font)
680 self.hostPopup.add_separator()
681 self.hostPopup.add_command(label='Properties', font=self.font, command=self.hostDetails )
682
683 self.legacyRouterPopup = Menu(self.top, tearoff=0)
684 self.legacyRouterPopup.add_command(label='Router Options', font=self.font)
685
686 self.linkPopup = Menu(self.top, tearoff=0)
687 self.linkPopup.add_command(label='Link Options', font=self.font)
688
carlosmscabralf40ecd12013-02-01 18:15:58 -0200689 # Event handling initalization
690 self.linkx = self.linky = self.linkItem = None
691 self.lastSelection = None
692
693 # Model initialization
694 self.links = {}
Caio Elias47b243c2014-11-23 19:25:34 -0200695 self.hostOpts = {}
696 self.switchOpts = {}
697 self.hostCount = 0
698 self.switchCount = 0
699 self.controllerCount = 0
carlosmscabralf40ecd12013-02-01 18:15:58 -0200700 self.net = None
701
702 # Close window gracefully
703 Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
704
705 def quit( self ):
706 "Stop our network, if any, then quit."
707 self.stop()
708 Frame.quit( self )
709
Caio Elias47b243c2014-11-23 19:25:34 -0200710 def createMenubar( self ): # MODIFICADO - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200711 "Create our menu bar."
712
713 font = self.font
714
715 mbar = Menu( self.top, font=font )
716 self.top.configure( menu=mbar )
717
Caio Elias47b243c2014-11-23 19:25:34 -0200718
719 #fileMenu = Menu( mbar, tearoff=False )
720 #mbar.add_cascade( label="File", font=font, menu=fileMenu )
721 #fileMenu.add_command( label="New", font=font, command=self.newTopology )
722 #fileMenu.add_command( label="Open", font=font, command=self.loadTopology )
723 #fileMenu.add_command( label="Save", font=font, command=self.saveTopology )
724 #fileMenu.add_command( label="Export Level 2 Script", font=font, command=self.exportScript )
725 #fileMenu.add_separator()
726 #fileMenu.add_command( label='Quit', command=self.quit, font=font )
727
728 #editMenu.add_command( label="Preferences", font=font, command=self.prefDetails)
729
730 #runMenu = Menu( mbar, tearoff=False )
731 #mbar.add_cascade( label="Run", font=font, menu=runMenu )
732 #runMenu.add_command( label="Run", font=font, command=self.doRun )
733 #runMenu.add_command( label="Stop", font=font, command=self.doStop )
734 #fileMenu.add_separator()
735 #runMenu.add_command( label='Show OVS Summary', font=font, command=self.ovsShow )
736 #runMenu.add_command( label='Root Terminal', font=font, command=self.rootTerminal )
737
carlosmscabralf40ecd12013-02-01 18:15:58 -0200738 # Application menu
739 appMenu = Menu( mbar, tearoff=False )
740 mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
741 appMenu.add_command( label='About Mini-CCNx', command=self.about,
742 font=font)
743 appMenu.add_separator()
744 appMenu.add_command( label='Quit', command=self.quit, font=font )
Caio Elias47b243c2014-11-23 19:25:34 -0200745
carlosmscabralf40ecd12013-02-01 18:15:58 -0200746 editMenu = Menu( mbar, tearoff=False )
747 mbar.add_cascade( label="Edit", font=font, menu=editMenu )
748 editMenu.add_command( label="Cut", font=font,
749 command=lambda: self.deleteSelection( None ) )
750
Caio Elias47b243c2014-11-23 19:25:34 -0200751 # Canvas - TUDO IGUAL - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200752
753 def createCanvas( self ):
754 "Create and return our scrolling canvas frame."
755 f = Frame( self )
756
757 canvas = Canvas( f, width=self.cwidth, height=self.cheight,
758 bg=self.bg )
759
760 # Scroll bars
761 xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
762 ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
763 canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
764
765 # Resize box
766 resize = Label( f, bg='white' )
767
768 # Layout
769 canvas.grid( row=0, column=1, sticky='nsew')
770 ybar.grid( row=0, column=2, sticky='ns')
771 xbar.grid( row=1, column=1, sticky='ew' )
772 resize.grid( row=1, column=2, sticky='nsew' )
773
774 # Resize behavior
775 f.rowconfigure( 0, weight=1 )
776 f.columnconfigure( 1, weight=1 )
777 f.grid( row=0, column=0, sticky='nsew' )
778 f.bind( '<Configure>', lambda event: self.updateScrollRegion() )
779
780 # Mouse bindings
781 canvas.bind( '<ButtonPress-1>', self.clickCanvas )
782 canvas.bind( '<B1-Motion>', self.dragCanvas )
783 canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
784
785 return f, canvas
786
787 def updateScrollRegion( self ):
788 "Update canvas scroll region to hold everything."
789 bbox = self.canvas.bbox( 'all' )
790 if bbox is not None:
791 self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
792 bbox[ 3 ] ) )
793
794 def canvasx( self, x_root ):
795 "Convert root x coordinate to canvas coordinate."
796 c = self.canvas
797 return c.canvasx( x_root ) - c.winfo_rootx()
798
799 def canvasy( self, y_root ):
800 "Convert root y coordinate to canvas coordinate."
801 c = self.canvas
802 return c.canvasy( y_root ) - c.winfo_rooty()
803
804 # Toolbar
805
Caio Elias47b243c2014-11-23 19:25:34 -0200806 def activate( self, toolName ): #IGUAL - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200807 "Activate a tool and press its button."
808 # Adjust button appearance
809 if self.active:
810 self.buttons[ self.active ].configure( relief='raised' )
811 self.buttons[ toolName ].configure( relief='sunken' )
812 # Activate dynamic bindings
813 self.active = toolName
814
Caio Elias47b243c2014-11-23 19:25:34 -0200815
816 def createToolTip(self, widget, text): #NOVA - CRIA HINTS E TIPS
817 toolTip = ToolTip(widget)
818 def enter(event):
819 toolTip.showtip(text)
820 def leave(event):
821 toolTip.hidetip()
822 widget.bind('<Enter>', enter)
823 widget.bind('<Leave>', leave)
824
825 def createToolbar( self ): #MODIFICADO - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200826 "Create and return our toolbar frame."
827
828 toolbar = Frame( self )
829
830 # Tools
831 for tool in self.tools:
832 cmd = ( lambda t=tool: self.activate( t ) )
833 b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
834 if tool in self.images:
Caio Elias47b243c2014-11-23 19:25:34 -0200835 b.config( height=35, image=self.images[ tool ] )
836 self.createToolTip(b, str(tool))
carlosmscabralf40ecd12013-02-01 18:15:58 -0200837 # b.config( compound='top' )
838 b.pack( fill='x' )
839 self.buttons[ tool ] = b
840 self.activate( self.tools[ 0 ] )
841
842 # Spacer
843 Label( toolbar, text='' ).pack()
844
845 # Commands
846 #for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
847 # doCmd = getattr( self, 'do' + cmd )
848 # b = Button( toolbar, text=cmd, font=self.smallFont,
849 # fg=color, command=doCmd )
850 # b.pack( fill='x', side='bottom' )
851
Caio Elias47b243c2014-11-23 19:25:34 -0200852 # abaixo copiado Mini-CCNx para criar botao Generate
853
854 for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200855 doCmd = getattr( self, 'do' + cmd )
856 b = Button( toolbar, text=cmd, font=self.smallFont,
857 fg=color, command=doCmd )
858 b.pack( fill='x', side='bottom' )
859
carlosmscabralf40ecd12013-02-01 18:15:58 -0200860 return toolbar
861
Caio Elias47b243c2014-11-23 19:25:34 -0200862 def doGenerate( self ): #COPIA Mini-CCNx - GERA TEMPLATE
carlosmscabralf40ecd12013-02-01 18:15:58 -0200863 "Generate template."
864 self.activate( 'Select' )
865 for tool in self.tools:
866 self.buttons[ tool ].config( state='disabled' )
867
868 self.buildTemplate()
869
Caio Elias47b243c2014-11-23 19:25:34 -0200870 for tool in self.tools:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200871 self.buttons[ tool ].config( state='normal' )
872
Caio Elias47b243c2014-11-23 19:25:34 -0200873 #def doRun( self ):
874 # "Run command."
875 # self.activate( 'Select' )
876 # for tool in self.tools:
877 # self.buttons[ tool ].config( state='disabled' )
878 # self.start()
879
carlosmscabralf40ecd12013-02-01 18:15:58 -0200880 def doStop( self ):
881 "Stop command."
882 self.stop()
883 for tool in self.tools:
884 self.buttons[ tool ].config( state='normal' )
885
Caio Elias47b243c2014-11-23 19:25:34 -0200886 def buildTemplate( self ): #COPIA Mini-CCNx para criar Template
887 "Generate template"
carlosmscabralf40ecd12013-02-01 18:15:58 -0200888
Caio Elias47b243c2014-11-23 19:25:34 -0200889 template = open(self.template_file, 'w')
890
891 # hosts
892 template.write('[hosts]\n')
893 for widget in self.widgetToItem:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200894 name = widget[ 'text' ]
895 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
Caio Elias47b243c2014-11-23 19:25:34 -0200896 if 'Host' in tags:
897 template.write(name + ':\n')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200898
Caio Elias47b243c2014-11-23 19:25:34 -0200899 # switches/routers
900 template.write('[routers]\n')
901 for widget in self.widgetToItem:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200902 name = widget[ 'text' ]
903 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
Caio Elias47b243c2014-11-23 19:25:34 -0200904 if 'Switch' in tags:
905 template.write(name + ':\n')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200906
907 # Make links
Caio Elias47b243c2014-11-23 19:25:34 -0200908 template.write('[links]\n')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200909 for link in self.links.values():
910 ( src, dst ) = link
911 srcName, dstName = src[ 'text' ], dst[ 'text' ]
912 template.write(srcName + ':' + dstName + '\n')
913
Caio Elias47b243c2014-11-23 19:25:34 -0200914 template.close()
915
916 def addNode( self, node, nodeNum, x, y, name=None):
917 "Add a new node to our canvas."
918 if 'Switch' == node:
919 self.switchCount += 1
920 if 'Host' == node:
921 self.hostCount += 1
922 if 'Controller' == node:
923 self.controllerCount += 1
924 if name is None:
925 name = self.nodePrefixes[ node ] + nodeNum
926 self.addNamedNode(node, name, x, y)
927
928 def addNamedNode( self, node, name, x, y):
929 "Add a new node to our canvas."
930 c = self.canvas
931 icon = self.nodeIcon( node, name )
932 item = self.canvas.create_window( x, y, anchor='c', window=icon,
933 tags=node )
934 self.widgetToItem[ icon ] = item
935 self.itemToWidget[ item ] = icon
936 icon.links = {}
937
938 def convertJsonUnicode(self, input):
939 "Some part of Mininet don't like Unicode"
940 if isinstance(input, dict):
941 return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
942 elif isinstance(input, list):
943 return [self.convertJsonUnicode(element) for element in input]
944 elif isinstance(input, unicode):
945 return input.encode('utf-8')
946 else:
947 return input
948
949 def loadTopology( self ):
950 "Load command."
951 c = self.canvas
952
953 myFormats = [
954 ('Mininet Topology','*.mn'),
955 ('All Files','*'),
956 ]
957 f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
958 if f == None:
959 return
960 self.newTopology()
961 loadedTopology = self.convertJsonUnicode(json.load(f))
962
963 # Load application preferences
964 if 'application' in loadedTopology:
965 self.appPrefs = dict(self.appPrefs.items() + loadedTopology['application'].items())
966 if "ovsOf10" not in self.appPrefs["openFlowVersions"]:
967 self.appPrefs["openFlowVersions"]["ovsOf10"] = '0'
968 if "ovsOf11" not in self.appPrefs["openFlowVersions"]:
969 self.appPrefs["openFlowVersions"]["ovsOf11"] = '0'
970 if "ovsOf12" not in self.appPrefs["openFlowVersions"]:
971 self.appPrefs["openFlowVersions"]["ovsOf12"] = '0'
972 if "ovsOf13" not in self.appPrefs["openFlowVersions"]:
973 self.appPrefs["openFlowVersions"]["ovsOf13"] = '0'
974 if "sflow" not in self.appPrefs:
975 self.appPrefs["sflow"] = self.sflowDefaults
976 if "netflow" not in self.appPrefs:
977 self.appPrefs["netflow"] = self.nflowDefaults
978
979 # Load controllers
980 if ('controllers' in loadedTopology):
981 if (loadedTopology['version'] == '1'):
982 # This is old location of controller info
983 hostname = 'c0'
984 self.controllers = {}
985 self.controllers[hostname] = loadedTopology['controllers']['c0']
986 self.controllers[hostname]['hostname'] = hostname
987 #elf.addNode('Controller', 0, float(30), float(30), name=hostname)
988 icon = self.findWidgetByName(hostname)
989 #con.bind('<Button-3>', self.do_controllerPopup )
990 else:
991 controllers = loadedTopology['controllers']
992 for controller in controllers:
993 hostname = controller['opts']['hostname']
994 x = controller['x']
995 y = controller['y']
996 self.addNode('Controller', 0, float(x), float(y), name=hostname)
997 self.controllers[hostname] = controller['opts']
998 icon = self.findWidgetByName(hostname)
999 #con.bind('<Button-3>', self.do_controllerPopup )
1000
1001
1002 # Load hosts
1003 hosts = loadedTopology['hosts']
1004 for host in hosts:
1005 nodeNum = host['number']
1006 hostname = 'h'+nodeNum
1007 if 'hostname' in host['opts']:
1008 hostname = host['opts']['hostname']
1009 else:
1010 host['opts']['hostname'] = hostname
1011 if 'nodeNum' not in host['opts']:
1012 host['opts']['nodeNum'] = int(nodeNum)
1013 x = host['x']
1014 y = host['y']
1015 self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
1016
1017 # Fix JSON converting tuple to list when saving
1018 if 'privateDirectory' in host['opts']:
1019 newDirList = []
1020 for privateDir in host['opts']['privateDirectory']:
1021 if isinstance( privateDir, list ):
1022 newDirList.append((privateDir[0],privateDir[1]))
1023 else:
1024 newDirList.append(privateDir)
1025 host['opts']['privateDirectory'] = newDirList
1026 self.hostOpts[hostname] = host['opts']
1027 icon = self.findWidgetByName(hostname)
1028 icon.bind('<Button-3>', self.do_hostPopup )
1029
1030 # Load switches
1031 switches = loadedTopology['switches']
1032 for switch in switches:
1033 nodeNum = switch['number']
1034 hostname = 's'+nodeNum
1035 if 'controllers' not in switch['opts']:
1036 switch['opts']['controllers'] = []
1037 if 'switchType' not in switch['opts']:
1038 switch['opts']['switchType'] = 'default'
1039 if 'hostname' in switch['opts']:
1040 hostname = switch['opts']['hostname']
1041 else:
1042 switch['opts']['hostname'] = hostname
1043 if 'nodeNum' not in switch['opts']:
1044 switch['opts']['nodeNum'] = int(nodeNum)
1045 x = switch['x']
1046 y = switch['y']
1047 if switch['opts']['switchType'] == "legacyRouter":
1048 self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
1049 icon = self.findWidgetByName(hostname)
1050 icon.bind('<Button-3>', self.do_legacyRouterPopup )
1051
1052 # Load links
1053 links = loadedTopology['links']
1054 for link in links:
1055 srcNode = link['src']
1056 src = self.findWidgetByName(srcNode)
1057 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
1058
1059 destNode = link['dest']
1060 dest = self.findWidgetByName(destNode)
1061 dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
1062
1063 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
1064 fill='blue', tag='link' )
1065 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
1066 self.addLink( src, dest, linkopts=link['opts'] )
1067 self.createDataLinkBindings()
1068 self.link = self.linkWidget = None
1069
1070 f.close
1071
1072 def findWidgetByName( self, name ):
1073 for widget in self.widgetToItem:
1074 if name == widget[ 'text' ]:
1075 return widget
1076
1077 def newTopology( self ):
1078 "New command."
1079 for widget in self.widgetToItem.keys():
1080 self.deleteItem( self.widgetToItem[ widget ] )
1081 self.hostCount = 0
1082 self.switchCount = 0
1083 self.links = {}
1084 self.hostOpts = {}
1085 self.switchOpts = {}
1086 self.appPrefs["ipBase"]= self.defaultIpBase
1087
1088 def saveTopology( self ):
1089 "Save command."
1090 myFormats = [
1091 ('Mininet Topology','*.mn'),
1092 ('All Files','*'),
1093 ]
1094
1095 savingDictionary = {}
1096 fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
1097 if len(fileName ) > 0:
1098 # Save Application preferences
1099 savingDictionary['version'] = '2'
1100
1101 # Save Switches and Hosts
1102 hostsToSave = []
1103 switchesToSave = []
1104 controllersToSave = []
1105 for widget in self.widgetToItem:
1106 name = widget[ 'text' ]
1107 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1108 x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
1109 if 'LegacyRouter' in tags:
1110 nodeNum = self.switchOpts[name]['nodeNum']
1111 nodeToSave = {'number':str(nodeNum),
1112 'x':str(x1),
1113 'y':str(y1),
1114 'opts':self.switchOpts[name] }
1115 switchesToSave.append(nodeToSave)
1116 elif 'Host' in tags:
1117 nodeNum = self.hostOpts[name]['nodeNum']
1118 nodeToSave = {'number':str(nodeNum),
1119 'x':str(x1),
1120 'y':str(y1),
1121 'opts':self.hostOpts[name] }
1122 hostsToSave.append(nodeToSave)
1123 else:
1124 raise Exception( "Cannot create mystery node: " + name )
1125 savingDictionary['hosts'] = hostsToSave
1126 savingDictionary['switches'] = switchesToSave
1127 savingDictionary['controllers'] = controllersToSave
1128
1129 # Save Links
1130 linksToSave = []
1131 for link in self.links.values():
1132 src = link['src']
1133 dst = link['dest']
1134 linkopts = link['linkOpts']
1135
1136 srcName, dstName = src[ 'text' ], dst[ 'text' ]
1137 linkToSave = {'src':srcName,
1138 'dest':dstName,
1139 'opts':linkopts}
1140 if link['type'] == 'data':
1141 linksToSave.append(linkToSave)
1142 savingDictionary['links'] = linksToSave
1143
1144 # Save Application preferences
1145 savingDictionary['application'] = self.appPrefs
1146
1147 try:
1148 f = open(fileName, 'wb')
1149 f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
1150 except Exception as er:
1151 print er
1152 finally:
1153 f.close()
1154
1155 def exportScript( self ):
1156 "Export command."
1157 myFormats = [
1158 ('Mininet Custom Topology','*.py'),
1159 ('All Files','*'),
1160 ]
1161
1162 fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Export the topology as...")
1163 if len(fileName ) > 0:
1164 #print "Now saving under %s" % fileName
1165 f = open(fileName, 'wb')
1166
1167 f.write("#!/usr/bin/python\n")
1168 f.write("\n")
1169 f.write("from mininet.net import Mininet\n")
1170 f.write("from mininet.node import Controller, RemoteController, OVSController\n")
1171 f.write("from mininet.node import CPULimitedHost, Host, Node\n")
1172 f.write("from mininet.node import OVSKernelSwitch, UserSwitch\n")
1173 if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
1174 f.write("from mininet.node import IVSSwitch\n")
1175 f.write("from mininet.cli import CLI\n")
1176 f.write("from mininet.log import setLogLevel, info\n")
1177 f.write("from mininet.link import TCLink, Intf\n")
1178 f.write("from subprocess import call\n")
1179
1180 inBandCtrl = False
1181 hasLegacySwitch = False
1182 for widget in self.widgetToItem:
1183 name = widget[ 'text' ]
1184 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1185
1186 if 'Controller' in tags:
1187 opts = self.controllers[name]
1188 controllerType = opts['controllerType']
1189 if controllerType == 'inband':
1190 inBandCtrl = True
1191
1192 if inBandCtrl == True:
1193 f.write("\n")
1194 f.write("class InbandController( RemoteController ):\n")
1195 f.write("\n")
1196 f.write(" def checkListening( self ):\n")
1197 f.write(" \"Overridden to do nothing.\"\n")
1198 f.write(" return\n")
1199
1200 f.write("\n")
1201 f.write("def myNetwork():\n")
1202 f.write("\n")
1203 f.write(" net = Mininet( topo=None,\n")
1204 if len(self.appPrefs['dpctl']) > 0:
1205 f.write(" listenPort="+self.appPrefs['dpctl']+",\n")
1206 f.write(" build=False,\n")
1207 f.write(" ipBase='"+self.appPrefs['ipBase']+"')\n")
1208 f.write("\n")
1209 f.write(" info( '*** Adding controller\\n' )\n")
1210 for widget in self.widgetToItem:
1211 name = widget[ 'text' ]
1212 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1213
1214 if 'Controller' in tags:
1215 opts = self.controllers[name]
1216 controllerType = opts['controllerType']
1217 if 'controllerProtocol' in opts:
1218 controllerProtocol = opts['controllerProtocol']
1219 else:
1220 controllerProtocol = 'tcp'
1221 controllerIP = opts['remoteIP']
1222 controllerPort = opts['remotePort']
1223
1224
1225 f.write(" "+name+"=net.addController(name='"+name+"',\n")
1226
1227 if controllerType == 'remote':
1228 f.write(" controller=RemoteController,\n")
1229 f.write(" ip='"+controllerIP+"',\n")
1230 elif controllerType == 'inband':
1231 f.write(" controller=InbandController,\n")
1232 f.write(" ip='"+controllerIP+"',\n")
1233 elif controllerType == 'ovsc':
1234 f.write(" controller=OVSController,\n")
1235 else:
1236 f.write(" controller=Controller,\n")
1237
1238 f.write(" protocol='"+controllerProtocol+"',\n")
1239 f.write(" port="+str(controllerPort)+")\n")
1240 f.write("\n")
1241
1242 # Save Switches and Hosts
1243 f.write(" info( '*** Add switches\\n')\n")
1244 for widget in self.widgetToItem:
1245 name = widget[ 'text' ]
1246 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1247 if 'LegacyRouter' in tags:
1248 f.write(" "+name+" = net.addHost('"+name+"', cls=Node, ip='0.0.0.0')\n")
1249 f.write(" "+name+".cmd('sysctl -w net.ipv4.ip_forward=1')\n")
1250 if 'LegacySwitch' in tags:
1251 f.write(" "+name+" = net.addSwitch('"+name+"', cls=OVSKernelSwitch, failMode='standalone')\n")
1252 if 'Switch' in tags:
1253 opts = self.switchOpts[name]
1254 nodeNum = opts['nodeNum']
1255 f.write(" "+name+" = net.addSwitch('"+name+"'")
1256 if opts['switchType'] == 'default':
1257 if self.appPrefs['switchType'] == 'ivs':
1258 f.write(", cls=IVSSwitch")
1259 elif self.appPrefs['switchType'] == 'user':
1260 f.write(", cls=UserSwitch")
1261 elif self.appPrefs['switchType'] == 'userns':
1262 f.write(", cls=UserSwitch, inNamespace=True")
1263 else:
1264 f.write(", cls=OVSKernelSwitch")
1265 elif opts['switchType'] == 'ivs':
1266 f.write(", cls=IVSSwitch")
1267 elif opts['switchType'] == 'user':
1268 f.write(", cls=UserSwitch")
1269 elif opts['switchType'] == 'userns':
1270 f.write(", cls=UserSwitch, inNamespace=True")
1271 else:
1272 f.write(", cls=OVSKernelSwitch")
1273 if 'dpctl' in opts:
1274 f.write(", listenPort="+opts['dpctl'])
1275 if 'dpid' in opts:
1276 f.write(", dpid='"+opts['dpid']+"'")
1277 f.write(")\n")
1278 if ('externalInterfaces' in opts):
1279 for extInterface in opts['externalInterfaces']:
1280 f.write(" Intf( '"+extInterface+"', node="+name+" )\n")
1281
1282 f.write("\n")
1283 f.write(" info( '*** Add hosts\\n')\n")
1284 for widget in self.widgetToItem:
1285 name = widget[ 'text' ]
1286 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1287 if 'Host' in tags:
1288 opts = self.hostOpts[name]
1289 ip = None
1290 defaultRoute = None
1291 if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
1292 defaultRoute = "'via "+opts['defaultRoute']+"'"
1293 else:
1294 defaultRoute = 'None'
1295 if 'ip' in opts and len(opts['ip']) > 0:
1296 ip = opts['ip']
1297 else:
1298 nodeNum = self.hostOpts[name]['nodeNum']
1299 ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
1300 ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)
1301
1302 if 'cores' in opts or 'cpu' in opts:
1303 f.write(" "+name+" = net.addHost('"+name+"', cls=CPULimitedHost, ip='"+ip+"', defaultRoute="+defaultRoute+")\n")
1304 if 'cores' in opts:
1305 f.write(" "+name+".setCPUs(cores='"+opts['cores']+"')\n")
1306 if 'cpu' in opts:
1307 f.write(" "+name+".setCPUFrac(f="+str(opts['cpu'])+", sched='"+opts['sched']+"')\n")
1308 else:
1309 f.write(" "+name+" = net.addHost('"+name+"', cls=Host, ip='"+ip+"', defaultRoute="+defaultRoute+")\n")
1310 if ('externalInterfaces' in opts):
1311 for extInterface in opts['externalInterfaces']:
1312 f.write(" Intf( '"+extInterface+"', node="+name+" )\n")
1313 f.write("\n")
1314
1315 # Save Links
1316 f.write(" info( '*** Add links\\n')\n")
1317 for key,linkDetail in self.links.iteritems():
1318 tags = self.canvas.gettags(key)
1319 if 'data' in tags:
1320 optsExist = False
1321 src = linkDetail['src']
1322 dst = linkDetail['dest']
1323 linkopts = linkDetail['linkOpts']
1324 srcName, dstName = src[ 'text' ], dst[ 'text' ]
1325 bw = ''
1326 delay = ''
1327 loss = ''
1328 max_queue_size = ''
1329 linkOpts = "{"
1330 if 'bw' in linkopts:
1331 bw = linkopts['bw']
1332 linkOpts = linkOpts + "'bw':"+str(bw)
1333 optsExist = True
1334 if 'delay' in linkopts:
1335 delay = linkopts['delay']
1336 if optsExist:
1337 linkOpts = linkOpts + ","
1338 linkOpts = linkOpts + "'delay':'"+linkopts['delay']+"'"
1339 optsExist = True
1340 if 'loss' in linkopts:
1341 if optsExist:
1342 linkOpts = linkOpts + ","
1343 linkOpts = linkOpts + "'loss':"+str(linkopts['loss'])
1344 optsExist = True
1345 if 'max_queue_size' in linkopts:
1346 if optsExist:
1347 linkOpts = linkOpts + ","
1348 linkOpts = linkOpts + "'max_queue_size':"+str(linkopts['max_queue_size'])
1349 optsExist = True
1350 if 'jitter' in linkopts:
1351 if optsExist:
1352 linkOpts = linkOpts + ","
1353 linkOpts = linkOpts + "'jitter':'"+linkopts['jitter']+"'"
1354 optsExist = True
1355 if 'speedup' in linkopts:
1356 if optsExist:
1357 linkOpts = linkOpts + ","
1358 linkOpts = linkOpts + "'speedup':"+str(linkopts['speedup'])
1359 optsExist = True
1360
1361 linkOpts = linkOpts + "}"
1362 if optsExist:
1363 f.write(" "+srcName+dstName+" = "+linkOpts+"\n")
1364 f.write(" net.addLink("+srcName+", "+dstName)
1365 if optsExist:
1366 f.write(", cls=TCLink , **"+srcName+dstName)
1367 f.write(")\n")
1368
1369 f.write("\n")
1370 f.write(" info( '*** Starting network\\n')\n")
1371 f.write(" net.build()\n")
1372
1373 f.write(" info( '*** Starting controllers\\n')\n")
1374 f.write(" for controller in net.controllers:\n")
1375 f.write(" controller.start()\n")
1376 f.write("\n")
1377
1378 f.write(" info( '*** Starting switches\\n')\n")
1379 for widget in self.widgetToItem:
1380 name = widget[ 'text' ]
1381 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1382 if 'Switch' in tags or 'LegacySwitch' in tags:
1383 opts = self.switchOpts[name]
1384 ctrlList = ",".join(opts['controllers'])
1385 f.write(" net.get('"+name+"').start(["+ctrlList+"])\n")
1386
1387 f.write("\n")
1388
1389 f.write(" info( '*** Post configure switches and hosts\\n')\n")
1390 for widget in self.widgetToItem:
1391 name = widget[ 'text' ]
1392 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1393 if 'Switch' in tags:
1394 opts = self.switchOpts[name]
1395 if opts['switchType'] == 'default':
1396 if self.appPrefs['switchType'] == 'user':
1397 if ('switchIP' in opts):
1398 if (len(opts['switchIP'])>0):
1399 f.write(" "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1400 elif self.appPrefs['switchType'] == 'userns':
1401 if ('switchIP' in opts):
1402 if (len(opts['switchIP'])>0):
1403 f.write(" "+name+".cmd('ifconfig lo "+opts['switchIP']+"')\n")
1404 elif self.appPrefs['switchType'] == 'ovs':
1405 if ('switchIP' in opts):
1406 if (len(opts['switchIP'])>0):
1407 f.write(" "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1408 elif opts['switchType'] == 'user':
1409 if ('switchIP' in opts):
1410 if (len(opts['switchIP'])>0):
1411 f.write(" "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1412 elif opts['switchType'] == 'userns':
1413 if ('switchIP' in opts):
1414 if (len(opts['switchIP'])>0):
1415 f.write(" "+name+".cmd('ifconfig lo "+opts['switchIP']+"')\n")
1416 elif opts['switchType'] == 'ovs':
1417 if ('switchIP' in opts):
1418 if (len(opts['switchIP'])>0):
1419 f.write(" "+name+".cmd('ifconfig "+name+" "+opts['switchIP']+"')\n")
1420 for widget in self.widgetToItem:
1421 name = widget[ 'text' ]
1422 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1423 if 'Host' in tags:
1424 opts = self.hostOpts[name]
1425 # Attach vlan interfaces
1426 if ('vlanInterfaces' in opts):
1427 for vlanInterface in opts['vlanInterfaces']:
1428 f.write(" "+name+".cmd('vconfig add "+name+"-eth0 "+vlanInterface[1]+"')\n")
1429 f.write(" "+name+".cmd('ifconfig "+name+"-eth0."+vlanInterface[1]+" "+vlanInterface[0]+"')\n")
1430 # Run User Defined Start Command
1431 if ('startCommand' in opts):
1432 f.write(" "+name+".cmdPrint('"+opts['startCommand']+"')\n")
1433 if 'Switch' in tags:
1434 opts = self.switchOpts[name]
1435 # Run User Defined Start Command
1436 if ('startCommand' in opts):
1437 f.write(" "+name+".cmdPrint('"+opts['startCommand']+"')\n")
1438
1439 # Configure NetFlow
1440 nflowValues = self.appPrefs['netflow']
1441 if len(nflowValues['nflowTarget']) > 0:
1442 nflowEnabled = False
1443 nflowSwitches = ''
1444 for widget in self.widgetToItem:
1445 name = widget[ 'text' ]
1446 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1447
1448 if 'Switch' in tags:
1449 opts = self.switchOpts[name]
1450 if 'netflow' in opts:
1451 if opts['netflow'] == '1':
1452 nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
1453 nflowEnabled=True
1454 if nflowEnabled:
1455 nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '+ 'target=\\\"'+nflowValues['nflowTarget']+'\\\" '+ 'active-timeout='+nflowValues['nflowTimeout']
1456 if nflowValues['nflowAddId'] == '1':
1457 nflowCmd = nflowCmd + ' add_id_to_interface=true'
1458 else:
1459 nflowCmd = nflowCmd + ' add_id_to_interface=false'
1460 f.write(" \n")
1461 f.write(" call('"+nflowCmd+nflowSwitches+"', shell=True)\n")
1462
1463 # Configure sFlow
1464 sflowValues = self.appPrefs['sflow']
1465 if len(sflowValues['sflowTarget']) > 0:
1466 sflowEnabled = False
1467 sflowSwitches = ''
1468 for widget in self.widgetToItem:
1469 name = widget[ 'text' ]
1470 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1471
1472 if 'Switch' in tags:
1473 opts = self.switchOpts[name]
1474 if 'sflow' in opts:
1475 if opts['sflow'] == '1':
1476 sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
1477 sflowEnabled=True
1478 if sflowEnabled:
1479 sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
1480 f.write(" \n")
1481 f.write(" call('"+sflowCmd+sflowSwitches+"', shell=True)\n")
1482
1483 f.write("\n")
1484 f.write(" CLI(net)\n")
1485 for widget in self.widgetToItem:
1486 name = widget[ 'text' ]
1487 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1488 if 'Host' in tags:
1489 opts = self.hostOpts[name]
1490 # Run User Defined Stop Command
1491 if ('stopCommand' in opts):
1492 f.write(" "+name+".cmdPrint('"+opts['stopCommand']+"')\n")
1493 if 'Switch' in tags:
1494 opts = self.switchOpts[name]
1495 # Run User Defined Stop Command
1496 if ('stopCommand' in opts):
1497 f.write(" "+name+".cmdPrint('"+opts['stopCommand']+"')\n")
1498
1499 f.write(" net.stop()\n")
1500 f.write("\n")
1501 f.write("if __name__ == '__main__':\n")
1502 f.write(" setLogLevel( 'info' )\n")
1503 f.write(" myNetwork()\n")
1504 f.write("\n")
1505
1506
1507 f.close()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001508
1509
1510 # Generic canvas handler
1511 #
1512 # We could have used bindtags, as in nodeIcon, but
1513 # the dynamic approach used here
1514 # may actually require less code. In any case, it's an
1515 # interesting introspection-based alternative to bindtags.
1516
1517 def canvasHandle( self, eventName, event ):
1518 "Generic canvas event handler"
1519 if self.active is None:
1520 return
1521 toolName = self.active
1522 handler = getattr( self, eventName + toolName, None )
1523 if handler is not None:
1524 handler( event )
1525
1526 def clickCanvas( self, event ):
1527 "Canvas click handler."
1528 self.canvasHandle( 'click', event )
1529
1530 def dragCanvas( self, event ):
1531 "Canvas drag handler."
1532 self.canvasHandle( 'drag', event )
1533
1534 def releaseCanvas( self, event ):
1535 "Canvas mouse up handler."
1536 self.canvasHandle( 'release', event )
1537
1538 # Currently the only items we can select directly are
1539 # links. Nodes are handled by bindings in the node icon.
1540
1541 def findItem( self, x, y ):
1542 "Find items at a location in our canvas."
1543 items = self.canvas.find_overlapping( x, y, x, y )
1544 if len( items ) == 0:
1545 return None
1546 else:
1547 return items[ 0 ]
1548
1549 # Canvas bindings for Select, Host, Switch and Link tools
1550
1551 def clickSelect( self, event ):
1552 "Select an item."
1553 self.selectItem( self.findItem( event.x, event.y ) )
1554
1555 def deleteItem( self, item ):
1556 "Delete an item."
1557 # Don't delete while network is running
1558 if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
1559 return
1560 # Delete from model
1561 if item in self.links:
1562 self.deleteLink( item )
1563 if item in self.itemToWidget:
1564 self.deleteNode( item )
1565 # Delete from view
1566 self.canvas.delete( item )
1567
1568 def deleteSelection( self, _event ):
1569 "Delete the selected item."
1570 if self.selection is not None:
1571 self.deleteItem( self.selection )
1572 self.selectItem( None )
1573
Caio Elias47b243c2014-11-23 19:25:34 -02001574 def clearPopups(self):
1575 print 'Entrou funcao clear_popups'
1576
1577 if isHostPopup == True:
1578 print 'Hostpopup = true'
1579 self.hostPopup.unpost
1580 isHostPopup = False
1581 #if isRouterPopup == True
1582 #if isLinkPopup == True
1583
carlosmscabralf40ecd12013-02-01 18:15:58 -02001584 def nodeIcon( self, node, name ):
1585 "Create a new node icon."
1586 icon = Button( self.canvas, image=self.images[ node ],
1587 text=name, compound='top' )
1588 # Unfortunately bindtags wants a tuple
1589 bindtags = [ str( self.nodeBindings ) ]
1590 bindtags += list( icon.bindtags() )
1591 icon.bindtags( tuple( bindtags ) )
1592 return icon
1593
1594 def newNode( self, node, event ):
1595 "Add a new node to our canvas."
1596 c = self.canvas
1597 x, y = c.canvasx( event.x ), c.canvasy( event.y )
Caio Elias47b243c2014-11-23 19:25:34 -02001598 name = self.nodePrefixes[ node ]
1599 if 'Switch' == node:
1600 self.switchCount += 1
1601 name = self.nodePrefixes[ node ] + str( self.switchCount )
1602 self.switchOpts[name] = {}
1603 self.switchOpts[name]['nodeNum']=self.switchCount
1604 self.switchOpts[name]['hostname']=name
1605 self.switchOpts[name]['switchType']='default'
1606 self.switchOpts[name]['controllers']=[]
1607 if 'LegacyRouter' == node:
1608 self.switchCount += 1
1609 name = self.nodePrefixes[ node ] + str( self.switchCount )
1610 self.switchOpts[name] = {}
1611 self.switchOpts[name]['nodeNum']=self.switchCount
1612 self.switchOpts[name]['hostname']=name
1613 self.switchOpts[name]['switchType']='legacyRouter'
1614 if 'LegacySwitch' == node:
1615 self.switchCount += 1
1616 name = self.nodePrefixes[ node ] + str( self.switchCount )
1617 self.switchOpts[name] = {}
1618 self.switchOpts[name]['nodeNum']=self.switchCount
1619 self.switchOpts[name]['hostname']=name
1620 self.switchOpts[name]['switchType']='legacySwitch'
1621 self.switchOpts[name]['controllers']=[]
1622 if 'Host' == node:
1623 self.hostCount += 1
1624 name = self.nodePrefixes[ node ] + str( self.hostCount )
1625 self.hostOpts[name] = {'sched':'host'}
1626 self.hostOpts[name]['nodeNum']=self.hostCount
1627 self.hostOpts[name]['hostname']=name
1628 if 'Controller' == node:
1629 name = self.nodePrefixes[ node ] + str( self.controllerCount )
1630 ctrlr = { 'controllerType': 'ref',
1631 'hostname': name,
1632 'controllerProtocol': 'tcp',
1633 'remoteIP': '127.0.0.1',
1634 'remotePort': 6633}
1635 self.controllers[name] = ctrlr
1636 # We want to start controller count at 0
1637 self.controllerCount += 1
1638
carlosmscabralf40ecd12013-02-01 18:15:58 -02001639 icon = self.nodeIcon( node, name )
1640 item = self.canvas.create_window( x, y, anchor='c', window=icon,
1641 tags=node )
1642 self.widgetToItem[ icon ] = item
1643 self.itemToWidget[ item ] = icon
1644 self.selectItem( item )
1645 icon.links = {}
Caio Elias47b243c2014-11-23 19:25:34 -02001646 if 'LegacyRouter' == node:
1647 icon.bind('<Button-3>', self.do_legacyRouterPopup )
1648 if 'Host' == node:
1649 icon.bind('<Button-3>', self.do_hostPopup )
1650
1651 def clickController( self, event ):
1652 "Add a new Controller to our canvas."
1653 self.newNode( 'Controller', event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001654
1655 def clickHost( self, event ):
1656 "Add a new host to our canvas."
1657 self.newNode( 'Host', event )
1658
Caio Elias47b243c2014-11-23 19:25:34 -02001659 def clickLegacyRouter( self, event ):
1660 "Add a new switch to our canvas."
1661 self.newNode( 'LegacyRouter', event )
1662
1663 def clickLegacySwitch( self, event ):
1664 "Add a new switch to our canvas."
1665 self.newNode( 'LegacySwitch', event )
1666
carlosmscabralf40ecd12013-02-01 18:15:58 -02001667 def clickSwitch( self, event ):
1668 "Add a new switch to our canvas."
1669 self.newNode( 'Switch', event )
1670
Caio Elias47b243c2014-11-23 19:25:34 -02001671 def dragNetLink( self, event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001672 "Drag a link's endpoint to another node."
1673 if self.link is None:
1674 return
1675 # Since drag starts in widget, we use root coords
1676 x = self.canvasx( event.x_root )
1677 y = self.canvasy( event.y_root )
1678 c = self.canvas
1679 c.coords( self.link, self.linkx, self.linky, x, y )
1680
Caio Elias47b243c2014-11-23 19:25:34 -02001681 def releaseNetLink( self, _event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001682 "Give up on the current link."
1683 if self.link is not None:
1684 self.canvas.delete( self.link )
1685 self.linkWidget = self.linkItem = self.link = None
1686
1687 # Generic node handlers
1688
1689 def createNodeBindings( self ):
1690 "Create a set of bindings for nodes."
1691 bindings = {
1692 '<ButtonPress-1>': self.clickNode,
1693 '<B1-Motion>': self.dragNode,
1694 '<ButtonRelease-1>': self.releaseNode,
1695 '<Enter>': self.enterNode,
Caio Elias47b243c2014-11-23 19:25:34 -02001696 '<Leave>': self.leaveNode
carlosmscabralf40ecd12013-02-01 18:15:58 -02001697 }
1698 l = Label() # lightweight-ish owner for bindings
1699 for event, binding in bindings.items():
1700 l.bind( event, binding )
1701 return l
1702
1703 def selectItem( self, item ):
1704 "Select an item and remember old selection."
1705 self.lastSelection = self.selection
1706 self.selection = item
1707
1708 def enterNode( self, event ):
1709 "Select node on entry."
1710 self.selectNode( event )
1711
1712 def leaveNode( self, _event ):
1713 "Restore old selection on exit."
1714 self.selectItem( self.lastSelection )
1715
1716 def clickNode( self, event ):
1717 "Node click handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001718 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001719 self.startLink( event )
1720 else:
1721 self.selectNode( event )
1722 return 'break'
1723
1724 def dragNode( self, event ):
1725 "Node drag handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001726 if self.active is 'NetLink':
1727 self.dragNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001728 else:
1729 self.dragNodeAround( event )
1730
1731 def releaseNode( self, event ):
1732 "Node release handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001733 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001734 self.finishLink( event )
1735
1736 # Specific node handlers
1737
1738 def selectNode( self, event ):
1739 "Select the node that was clicked on."
1740 item = self.widgetToItem.get( event.widget, None )
1741 self.selectItem( item )
1742
1743 def dragNodeAround( self, event ):
1744 "Drag a node around on the canvas."
1745 c = self.canvas
1746 # Convert global to local coordinates;
1747 # Necessary since x, y are widget-relative
1748 x = self.canvasx( event.x_root )
1749 y = self.canvasy( event.y_root )
1750 w = event.widget
1751 # Adjust node position
1752 item = self.widgetToItem[ w ]
1753 c.coords( item, x, y )
1754 # Adjust link positions
1755 for dest in w.links:
1756 link = w.links[ dest ]
1757 item = self.widgetToItem[ dest ]
1758 x1, y1 = c.coords( item )
1759 c.coords( link, x, y, x1, y1 )
Caio Elias47b243c2014-11-23 19:25:34 -02001760 self.updateScrollRegion()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001761
Caio Elias47b243c2014-11-23 19:25:34 -02001762 def createControlLinkBindings( self ):
1763 "Create a set of bindings for nodes."
carlosmscabralf40ecd12013-02-01 18:15:58 -02001764 # Link bindings
1765 # Selection still needs a bit of work overall
1766 # Callbacks ignore event
1767
1768 def select( _event, link=self.link ):
1769 "Select item on mouse entry."
1770 self.selectItem( link )
1771
1772 def highlight( _event, link=self.link ):
1773 "Highlight item on mouse entry."
Caio Elias47b243c2014-11-23 19:25:34 -02001774 self.selectItem( link )
1775 self.canvas.itemconfig( link, fill='green' )
1776
1777 def unhighlight( _event, link=self.link ):
1778 "Unhighlight item on mouse exit."
1779 self.canvas.itemconfig( link, fill='red' )
1780 #self.selectItem( None )
1781
1782 self.canvas.tag_bind( self.link, '<Enter>', highlight )
1783 self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
1784 self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
1785
1786 def createDataLinkBindings( self ):
1787 "Create a set of bindings for nodes."
1788 # Link bindings
1789 # Selection still needs a bit of work overall
1790 # Callbacks ignore event
1791
1792 def select( _event, link=self.link ):
1793 "Select item on mouse entry."
1794 self.selectItem( link )
1795
1796 def highlight( _event, link=self.link ):
1797 "Highlight item on mouse entry."
1798 self.selectItem( link )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001799 self.canvas.itemconfig( link, fill='green' )
1800
1801 def unhighlight( _event, link=self.link ):
1802 "Unhighlight item on mouse exit."
1803 self.canvas.itemconfig( link, fill='blue' )
Caio Elias47b243c2014-11-23 19:25:34 -02001804 #self.selectItem( None )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001805
1806 self.canvas.tag_bind( self.link, '<Enter>', highlight )
1807 self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
1808 self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
Caio Elias47b243c2014-11-23 19:25:34 -02001809 self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
1810
1811
1812 def startLink( self, event ):
1813 "Start a new link."
1814 if event.widget not in self.widgetToItem:
1815 # Didn't click on a node
1816 return
1817
1818 w = event.widget
1819 item = self.widgetToItem[ w ]
1820 x, y = self.canvas.coords( item )
1821 self.link = self.canvas.create_line( x, y, x, y, width=4,
1822 fill='blue', tag='link' )
1823 self.linkx, self.linky = x, y
1824 self.linkWidget = w
1825 self.linkItem = item
1826
carlosmscabralf40ecd12013-02-01 18:15:58 -02001827
1828 def finishLink( self, event ):
1829 "Finish creating a link"
1830 if self.link is None:
1831 return
1832 source = self.linkWidget
1833 c = self.canvas
1834 # Since we dragged from the widget, use root coords
1835 x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
1836 target = self.findItem( x, y )
1837 dest = self.itemToWidget.get( target, None )
1838 if ( source is None or dest is None or source == dest
1839 or dest in source.links or source in dest.links ):
Caio Elias47b243c2014-11-23 19:25:34 -02001840 self.releaseNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001841 return
1842 # For now, don't allow hosts to be directly linked
Caio Elias47b243c2014-11-23 19:25:34 -02001843 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1844 dtags = self.canvas.gettags( target )
1845 if (('Host' in stags and 'Host' in dtags) or
1846 ('Controller' in dtags and 'LegacyRouter' in stags) or
1847 ('Controller' in stags and 'LegacyRouter' in dtags) or
1848 ('Controller' in dtags and 'LegacySwitch' in stags) or
1849 ('Controller' in stags and 'LegacySwitch' in dtags) or
1850 ('Controller' in dtags and 'Host' in stags) or
1851 ('Controller' in stags and 'Host' in dtags) or
1852 ('Controller' in stags and 'Controller' in dtags)):
1853 self.releaseNetLink( event )
1854 return
1855
1856 # Set link type
1857 linkType='data'
1858 if 'Controller' in stags or 'Controller' in dtags:
1859 linkType='control'
1860 c.itemconfig(self.link, dash=(6, 4, 2, 4), fill='red')
1861 self.createControlLinkBindings()
1862 else:
1863 linkType='data'
1864 self.createDataLinkBindings()
1865 c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
1866
carlosmscabralf40ecd12013-02-01 18:15:58 -02001867 x, y = c.coords( target )
1868 c.coords( self.link, self.linkx, self.linky, x, y )
Caio Elias47b243c2014-11-23 19:25:34 -02001869 self.addLink( source, dest, linktype=linkType )
1870 if linkType == 'control':
1871 controllerName = ''
1872 switchName = ''
1873 if 'Controller' in stags:
1874 controllerName = source[ 'text' ]
1875 switchName = dest[ 'text' ]
1876 else:
1877 controllerName = dest[ 'text' ]
1878 switchName = source[ 'text' ]
1879
1880 self.switchOpts[switchName]['controllers'].append(controllerName)
1881
carlosmscabralf40ecd12013-02-01 18:15:58 -02001882 # We're done
1883 self.link = self.linkWidget = None
1884
1885 # Menu handlers
1886
1887 def about( self ):
1888 "Display about box."
1889 about = self.aboutBox
1890 if about is None:
1891 bg = 'white'
1892 about = Toplevel( bg='white' )
1893 about.title( 'About' )
Caio Elias47b243c2014-11-23 19:25:34 -02001894 info = self.appName + ': a simple network editor for MiniNet'
1895 version = 'MiniEdit '+MINIEDIT_VERSION
1896 author = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
1897 enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
1898 www = 'http://gregorygee.wordpress.com/category/miniedit/'
carlosmscabralf40ecd12013-02-01 18:15:58 -02001899 line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
Caio Elias47b243c2014-11-23 19:25:34 -02001900 line2 = Label( about, text=version, font='Helvetica 9', bg=bg )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001901 line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
Caio Elias47b243c2014-11-23 19:25:34 -02001902 line4 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
1903 line5 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
1904 line5.insert(0, www)
1905 line5.configure(state='readonly')
carlosmscabralf40ecd12013-02-01 18:15:58 -02001906 line1.pack( padx=20, pady=10 )
1907 line2.pack(pady=10 )
1908 line3.pack(pady=10 )
Caio Elias47b243c2014-11-23 19:25:34 -02001909 line4.pack(pady=10 )
1910 line5.pack(pady=10 )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001911 hide = ( lambda about=about: about.withdraw() )
1912 self.aboutBox = about
1913 # Hide on close rather than destroying window
1914 Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
1915 # Show (existing) window
1916 about.deiconify()
1917
1918 def createToolImages( self ):
1919 "Create toolbar (and icon) images."
1920
Caio Elias47b243c2014-11-23 19:25:34 -02001921 def checkIntf( self, intf ):
1922 "Make sure intf exists and is not configured."
1923 if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
1924 showerror(title="Error",
1925 message='External interface ' +intf + ' does not exist! Skipping.')
1926 return False
1927 ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
1928 if ips:
1929 showerror(title="Error",
1930 message= intf + ' has an IP address and is probably in use! Skipping.' )
1931 return False
1932 return True
1933
1934 def hostDetails( self, _ignore=None ):
1935 if ( self.selection is None or
1936 self.net is not None or
1937 self.selection not in self.itemToWidget ):
1938 return
1939 widget = self.itemToWidget[ self.selection ]
1940 name = widget[ 'text' ]
1941 tags = self.canvas.gettags( self.selection )
1942 if 'Host' not in tags:
1943 return
1944
1945 prefDefaults = self.hostOpts[name]
1946 hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults)
1947 self.master.wait_window(hostBox.top)
1948 if hostBox.result:
1949 newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
1950 newHostOpts['sched'] = hostBox.result['sched']
1951 if len(hostBox.result['startCommand']) > 0:
1952 newHostOpts['startCommand'] = hostBox.result['startCommand']
1953 if len(hostBox.result['stopCommand']) > 0:
1954 newHostOpts['stopCommand'] = hostBox.result['stopCommand']
1955 if len(hostBox.result['cpu']) > 0:
1956 newHostOpts['cpu'] = float(hostBox.result['cpu'])
1957 if len(hostBox.result['cores']) > 0:
1958 newHostOpts['cores'] = hostBox.result['cores']
1959 if len(hostBox.result['hostname']) > 0:
1960 newHostOpts['hostname'] = hostBox.result['hostname']
1961 name = hostBox.result['hostname']
1962 widget[ 'text' ] = name
1963 if len(hostBox.result['defaultRoute']) > 0:
1964 newHostOpts['defaultRoute'] = hostBox.result['defaultRoute']
1965 if len(hostBox.result['ip']) > 0:
1966 newHostOpts['ip'] = hostBox.result['ip']
1967 if len(hostBox.result['externalInterfaces']) > 0:
1968 newHostOpts['externalInterfaces'] = hostBox.result['externalInterfaces']
1969 if len(hostBox.result['vlanInterfaces']) > 0:
1970 newHostOpts['vlanInterfaces'] = hostBox.result['vlanInterfaces']
1971 if len(hostBox.result['privateDirectory']) > 0:
1972 newHostOpts['privateDirectory'] = hostBox.result['privateDirectory']
1973 self.hostOpts[name] = newHostOpts
1974 print 'New host details for ' + name + ' = ' + str(newHostOpts)
1975
1976 def switchDetails( self, _ignore=None ):
1977 if ( self.selection is None or
1978 self.net is not None or
1979 self.selection not in self.itemToWidget ):
1980 return
1981 widget = self.itemToWidget[ self.selection ]
1982 name = widget[ 'text' ]
1983 tags = self.canvas.gettags( self.selection )
1984 if 'Switch' not in tags:
1985 return
1986
1987 prefDefaults = self.switchOpts[name]
1988 switchBox = SwitchDialog(self, title='Switch Details', prefDefaults=prefDefaults)
1989 self.master.wait_window(switchBox.top)
1990 if switchBox.result:
1991 newSwitchOpts = {'nodeNum':self.switchOpts[name]['nodeNum']}
1992 newSwitchOpts['switchType'] = switchBox.result['switchType']
1993 newSwitchOpts['controllers'] = self.switchOpts[name]['controllers']
1994 if len(switchBox.result['startCommand']) > 0:
1995 newSwitchOpts['startCommand'] = switchBox.result['startCommand']
1996 if len(switchBox.result['stopCommand']) > 0:
1997 newSwitchOpts['stopCommand'] = switchBox.result['stopCommand']
1998 if len(switchBox.result['dpctl']) > 0:
1999 newSwitchOpts['dpctl'] = switchBox.result['dpctl']
2000 if len(switchBox.result['dpid']) > 0:
2001 newSwitchOpts['dpid'] = switchBox.result['dpid']
2002 if len(switchBox.result['hostname']) > 0:
2003 newSwitchOpts['hostname'] = switchBox.result['hostname']
2004 name = switchBox.result['hostname']
2005 widget[ 'text' ] = name
2006 if len(switchBox.result['externalInterfaces']) > 0:
2007 newSwitchOpts['externalInterfaces'] = switchBox.result['externalInterfaces']
2008 newSwitchOpts['switchIP'] = switchBox.result['switchIP']
2009 newSwitchOpts['sflow'] = switchBox.result['sflow']
2010 newSwitchOpts['netflow'] = switchBox.result['netflow']
2011 self.switchOpts[name] = newSwitchOpts
2012 print 'New switch details for ' + name + ' = ' + str(newSwitchOpts)
2013
2014 def linkUp( self ):
2015 if ( self.selection is None or
2016 self.net is None):
2017 return
2018 link = self.selection
2019 linkDetail = self.links[link]
2020 src = linkDetail['src']
2021 dst = linkDetail['dest']
2022 srcName, dstName = src[ 'text' ], dst[ 'text' ]
2023 self.net.configLinkStatus(srcName, dstName, 'up')
2024 self.canvas.itemconfig(link, dash=())
2025
2026 def linkDown( self ):
2027 if ( self.selection is None or
2028 self.net is None):
2029 return
2030 link = self.selection
2031 linkDetail = self.links[link]
2032 src = linkDetail['src']
2033 dst = linkDetail['dest']
2034 srcName, dstName = src[ 'text' ], dst[ 'text' ]
2035 self.net.configLinkStatus(srcName, dstName, 'down')
2036 self.canvas.itemconfig(link, dash=(4, 4))
2037
2038 def linkDetails( self, _ignore=None ):
2039 if ( self.selection is None or
2040 self.net is not None):
2041 return
2042 link = self.selection
2043
2044 linkDetail = self.links[link]
2045 src = linkDetail['src']
2046 dest = linkDetail['dest']
2047 linkopts = linkDetail['linkOpts']
2048 linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
2049 if linkBox.result is not None:
2050 linkDetail['linkOpts'] = linkBox.result
2051 print 'New link details = ' + str(linkBox.result)
2052
2053 def prefDetails( self ):
2054 prefDefaults = self.appPrefs
2055 prefBox = PrefsDialog(self, title='Preferences', prefDefaults=prefDefaults)
2056 print 'New Prefs = ' + str(prefBox.result)
2057 if prefBox.result:
2058 self.appPrefs = prefBox.result
2059
2060
2061 def controllerDetails( self ):
2062 if ( self.selection is None or
2063 self.net is not None or
2064 self.selection not in self.itemToWidget ):
2065 return
2066 widget = self.itemToWidget[ self.selection ]
2067 name = widget[ 'text' ]
2068 tags = self.canvas.gettags( self.selection )
2069 oldName = name
2070 if 'Controller' not in tags:
2071 return
2072
2073 ctrlrBox = ControllerDialog(self, title='Controller Details', ctrlrDefaults=self.controllers[name])
2074 if ctrlrBox.result:
2075 #print 'Controller is ' + ctrlrBox.result[0]
2076 if len(ctrlrBox.result['hostname']) > 0:
2077 name = ctrlrBox.result['hostname']
2078 widget[ 'text' ] = name
2079 else:
2080 ctrlrBox.result['hostname'] = name
2081 self.controllers[name] = ctrlrBox.result
2082 print 'New controller details for ' + name + ' = ' + str(self.controllers[name])
2083 # Find references to controller and change name
2084 if oldName != name:
2085 for widget in self.widgetToItem:
2086 switchName = widget[ 'text' ]
2087 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2088 if 'Switch' in tags:
2089 switch = self.switchOpts[switchName]
2090 if oldName in switch['controllers']:
2091 switch['controllers'].remove(oldName)
2092 switch['controllers'].append(name)
2093
2094
2095 def listBridge( self, _ignore=None ):
2096 if ( self.selection is None or
2097 self.net is None or
2098 self.selection not in self.itemToWidget ):
2099 return
2100 name = self.itemToWidget[ self.selection ][ 'text' ]
2101 tags = self.canvas.gettags( self.selection )
2102
2103 if name not in self.net.nameToNode:
2104 return
2105 if 'Switch' in tags or 'LegacySwitch' in tags:
2106 call(["xterm -T 'Bridge Details' -sb -sl 2000 -e 'ovs-vsctl list bridge " + name + "; read -p \"Press Enter to close\"' &"], shell=True)
2107
2108 def ovsShow( self, _ignore=None ):
2109 call(["xterm -T 'OVS Summary' -sb -sl 2000 -e 'ovs-vsctl show; read -p \"Press Enter to close\"' &"], shell=True)
2110
2111 def rootTerminal( self, _ignore=None ):
2112 call(["xterm -T 'Root Terminal' -sb -sl 2000 &"], shell=True)
2113
carlosmscabralf40ecd12013-02-01 18:15:58 -02002114 # Model interface
2115 #
2116 # Ultimately we will either want to use a topo or
2117 # mininet object here, probably.
2118
Caio Elias47b243c2014-11-23 19:25:34 -02002119 def addLink( self, source, dest, linktype='data', linkopts={} ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02002120 "Add link to model."
2121 source.links[ dest ] = self.link
2122 dest.links[ source ] = self.link
Caio Elias47b243c2014-11-23 19:25:34 -02002123 self.links[ self.link ] = {'type' :linktype,
2124 'src':source,
2125 'dest':dest,
2126 'linkOpts':linkopts}
carlosmscabralf40ecd12013-02-01 18:15:58 -02002127
2128 def deleteLink( self, link ):
2129 "Delete link from model."
2130 pair = self.links.get( link, None )
2131 if pair is not None:
Caio Elias47b243c2014-11-23 19:25:34 -02002132 source=pair['src']
2133 dest=pair['dest']
carlosmscabralf40ecd12013-02-01 18:15:58 -02002134 del source.links[ dest ]
2135 del dest.links[ source ]
Caio Elias47b243c2014-11-23 19:25:34 -02002136 stags = self.canvas.gettags( self.widgetToItem[ source ] )
2137 dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
2138 ltags = self.canvas.gettags( link )
2139
2140 if 'control' in ltags:
2141 controllerName = ''
2142 switchName = ''
2143 if 'Controller' in stags:
2144 controllerName = source[ 'text' ]
2145 switchName = dest[ 'text' ]
2146 else:
2147 controllerName = dest[ 'text' ]
2148 switchName = source[ 'text' ]
2149
2150 if controllerName in self.switchOpts[switchName]['controllers']:
2151 self.switchOpts[switchName]['controllers'].remove(controllerName)
2152
2153
carlosmscabralf40ecd12013-02-01 18:15:58 -02002154 if link is not None:
2155 del self.links[ link ]
2156
2157 def deleteNode( self, item ):
2158 "Delete node (and its links) from model."
Caio Elias47b243c2014-11-23 19:25:34 -02002159
carlosmscabralf40ecd12013-02-01 18:15:58 -02002160 widget = self.itemToWidget[ item ]
Caio Elias47b243c2014-11-23 19:25:34 -02002161 tags = self.canvas.gettags(item)
2162 if 'Controller' in tags:
2163 # remove from switch controller lists
2164 for serachwidget in self.widgetToItem:
2165 name = serachwidget[ 'text' ]
2166 tags = self.canvas.gettags( self.widgetToItem[ serachwidget ] )
2167 if 'Switch' in tags:
2168 if widget['text'] in self.switchOpts[name]['controllers']:
2169 self.switchOpts[name]['controllers'].remove(widget['text'])
2170
carlosmscabralf40ecd12013-02-01 18:15:58 -02002171 for link in widget.links.values():
2172 # Delete from view and model
2173 self.deleteItem( link )
2174 del self.itemToWidget[ item ]
2175 del self.widgetToItem[ widget ]
2176
Caio Elias47b243c2014-11-23 19:25:34 -02002177 def buildNodes( self, net):
carlosmscabralf40ecd12013-02-01 18:15:58 -02002178 # Make nodes
Caio Elias47b243c2014-11-23 19:25:34 -02002179 print "Getting Hosts and Switches."
carlosmscabralf40ecd12013-02-01 18:15:58 -02002180 for widget in self.widgetToItem:
2181 name = widget[ 'text' ]
2182 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
Caio Elias47b243c2014-11-23 19:25:34 -02002183 #print name+' has '+str(tags)
2184
carlosmscabralf40ecd12013-02-01 18:15:58 -02002185 if 'Switch' in tags:
Caio Elias47b243c2014-11-23 19:25:34 -02002186 opts = self.switchOpts[name]
2187 #print str(opts)
2188
2189 # Create the correct switch class
2190 switchClass = customOvs
2191 switchParms={}
2192 if 'dpctl' in opts:
2193 switchParms['listenPort']=int(opts['dpctl'])
2194 if 'dpid' in opts:
2195 switchParms['dpid']=opts['dpid']
2196 if opts['switchType'] == 'default':
2197 if self.appPrefs['switchType'] == 'ivs':
2198 switchClass = IVSSwitch
2199 elif self.appPrefs['switchType'] == 'user':
2200 switchClass = CustomUserSwitch
2201 elif self.appPrefs['switchType'] == 'userns':
2202 switchParms['inNamespace'] = True
2203 switchClass = CustomUserSwitch
2204 else:
2205 switchClass = customOvs
2206 elif opts['switchType'] == 'user':
2207 switchClass = CustomUserSwitch
2208 elif opts['switchType'] == 'userns':
2209 switchClass = CustomUserSwitch
2210 switchParms['inNamespace'] = True
2211 elif opts['switchType'] == 'ivs':
2212 switchClass = IVSSwitch
2213 else:
2214 switchClass = customOvs
2215
2216 if switchClass == customOvs:
2217 # Set OpenFlow versions
2218 self.openFlowVersions = []
2219 if self.appPrefs['openFlowVersions']['ovsOf10'] == '1':
2220 self.openFlowVersions.append('OpenFlow10')
2221 if self.appPrefs['openFlowVersions']['ovsOf11'] == '1':
2222 self.openFlowVersions.append('OpenFlow11')
2223 if self.appPrefs['openFlowVersions']['ovsOf12'] == '1':
2224 self.openFlowVersions.append('OpenFlow12')
2225 if self.appPrefs['openFlowVersions']['ovsOf13'] == '1':
2226 self.openFlowVersions.append('OpenFlow13')
2227 protoList = ",".join(self.openFlowVersions)
2228 switchParms['protocols'] = protoList
2229 newSwitch = net.addSwitch( name , cls=switchClass, **switchParms)
2230
2231 # Some post startup config
2232 if switchClass == CustomUserSwitch:
2233 if ('switchIP' in opts):
2234 if (len(opts['switchIP']) > 0):
2235 newSwitch.setSwitchIP(opts['switchIP'])
2236 if switchClass == customOvs:
2237 if ('switchIP' in opts):
2238 if (len(opts['switchIP']) > 0):
2239 newSwitch.setSwitchIP(opts['switchIP'])
2240
2241 # Attach external interfaces
2242 if ('externalInterfaces' in opts):
2243 for extInterface in opts['externalInterfaces']:
2244 if self.checkIntf(extInterface):
2245 Intf( extInterface, node=newSwitch )
2246
2247 elif 'LegacySwitch' in tags:
2248 newSwitch = net.addSwitch( name , cls=LegacySwitch)
2249 elif 'LegacyRouter' in tags:
2250 newSwitch = net.addHost( name , cls=LegacyRouter)
carlosmscabralf40ecd12013-02-01 18:15:58 -02002251 elif 'Host' in tags:
Caio Elias47b243c2014-11-23 19:25:34 -02002252 opts = self.hostOpts[name]
2253 #print str(opts)
2254 ip = None
2255 defaultRoute = None
2256 if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
2257 defaultRoute = 'via '+opts['defaultRoute']
2258 if 'ip' in opts and len(opts['ip']) > 0:
2259 ip = opts['ip']
2260 else:
2261 nodeNum = self.hostOpts[name]['nodeNum']
2262 ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
2263 ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)
2264
2265 # Create the correct host class
2266 if 'cores' in opts or 'cpu' in opts:
2267 if ('privateDirectory' in opts):
2268 hostCls = partial( CPULimitedHost,
2269 privateDirs=opts['privateDirectory'] )
2270 else:
2271 hostCls=CPULimitedHost
2272 else:
2273 if ('privateDirectory' in opts):
2274 hostCls = partial( Host,
2275 privateDirs=opts['privateDirectory'] )
2276 else:
2277 hostCls=Host
2278 print hostCls
2279 newHost = net.addHost( name,
2280 cls=hostCls,
2281 ip=ip,
2282 defaultRoute=defaultRoute
2283 )
2284
2285 # Set the CPULimitedHost specific options
2286 if 'cores' in opts:
2287 newHost.setCPUs(cores = opts['cores'])
2288 if 'cpu' in opts:
2289 newHost.setCPUFrac(f=opts['cpu'], sched=opts['sched'])
2290
2291 # Attach external interfaces
2292 if ('externalInterfaces' in opts):
2293 for extInterface in opts['externalInterfaces']:
2294 if self.checkIntf(extInterface):
2295 Intf( extInterface, node=newHost )
2296 if ('vlanInterfaces' in opts):
2297 if len(opts['vlanInterfaces']) > 0:
2298 print 'Checking that OS is VLAN prepared'
2299 self.pathCheck('vconfig', moduleName='vlan package')
2300 moduleDeps( add='8021q' )
2301 elif 'Controller' in tags:
2302 opts = self.controllers[name]
2303
2304 # Get controller info from panel
2305 controllerType = opts['controllerType']
2306 if 'controllerProtocol' in opts:
2307 controllerProtocol = opts['controllerProtocol']
2308 else:
2309 controllerProtocol = 'tcp'
2310 opts['controllerProtocol'] = 'tcp'
2311 controllerIP = opts['remoteIP']
2312 controllerPort = opts['remotePort']
2313
2314 # Make controller
2315 print 'Getting controller selection:'+controllerType
2316 if controllerType == 'remote':
2317 net.addController(name=name,
2318 controller=RemoteController,
2319 ip=controllerIP,
2320 protocol=controllerProtocol,
2321 port=controllerPort)
2322 elif controllerType == 'inband':
2323 net.addController(name=name,
2324 controller=InbandController,
2325 ip=controllerIP,
2326 protocol=controllerProtocol,
2327 port=controllerPort)
2328 elif controllerType == 'ovsc':
2329 net.addController(name=name,
2330 controller=OVSController,
2331 protocol=controllerProtocol,
2332 port=controllerPort)
2333 else:
2334 net.addController(name=name,
2335 controller=Controller,
2336 protocol=controllerProtocol,
2337 port=controllerPort)
2338
carlosmscabralf40ecd12013-02-01 18:15:58 -02002339 else:
2340 raise Exception( "Cannot create mystery node: " + name )
Caio Elias47b243c2014-11-23 19:25:34 -02002341
2342 def pathCheck( self, *args, **kwargs ):
2343 "Make sure each program in *args can be found in $PATH."
2344 moduleName = kwargs.get( 'moduleName', 'it' )
2345 for arg in args:
2346 if not quietRun( 'which ' + arg ):
2347 showerror(title="Error",
2348 message= 'Cannot find required executable %s.\n' % arg +
2349 'Please make sure that %s is installed ' % moduleName +
2350 'and available in your $PATH.' )
2351
2352 def buildLinks( self, net):
carlosmscabralf40ecd12013-02-01 18:15:58 -02002353 # Make links
Caio Elias47b243c2014-11-23 19:25:34 -02002354 print "Getting Links."
2355 for key,link in self.links.iteritems():
2356 tags = self.canvas.gettags(key)
2357 if 'data' in tags:
2358 src=link['src']
2359 dst=link['dest']
2360 linkopts=link['linkOpts']
2361 srcName, dstName = src[ 'text' ], dst[ 'text' ]
2362 srcNode, dstNode = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
2363 if linkopts:
2364 net.addLink(srcNode, dstNode, cls=TCLink, **linkopts)
2365 else:
2366 #print str(srcNode)
2367 #print str(dstNode)
2368 net.addLink(srcNode, dstNode)
2369 self.canvas.itemconfig(key, dash=())
2370
2371
2372 def build( self ):
2373 print "Build network based on our topology."
2374
2375 dpctl = None
2376 if len(self.appPrefs['dpctl']) > 0:
2377 dpctl = int(self.appPrefs['dpctl'])
2378 net = Mininet( topo=None,
2379 listenPort=dpctl,
2380 build=False,
2381 ipBase=self.appPrefs['ipBase'] )
2382
2383 self.buildNodes(net)
2384 self.buildLinks(net)
carlosmscabralf40ecd12013-02-01 18:15:58 -02002385
2386 # Build network (we have to do this separately at the moment )
2387 net.build()
2388
2389 return net
2390
Caio Elias47b243c2014-11-23 19:25:34 -02002391
2392 def postStartSetup( self ):
2393
2394 # Setup host details
2395 for widget in self.widgetToItem:
2396 name = widget[ 'text' ]
2397 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2398 if 'Host' in tags:
2399 newHost = self.net.get(name)
2400 opts = self.hostOpts[name]
2401 # Attach vlan interfaces
2402 if ('vlanInterfaces' in opts):
2403 for vlanInterface in opts['vlanInterfaces']:
2404 print 'adding vlan interface '+vlanInterface[1]
2405 newHost.cmdPrint('ifconfig '+name+'-eth0.'+vlanInterface[1]+' '+vlanInterface[0])
2406 # Run User Defined Start Command
2407 if ('startCommand' in opts):
2408 newHost.cmdPrint(opts['startCommand'])
2409 if 'Switch' in tags:
2410 newNode = self.net.get(name)
2411 opts = self.switchOpts[name]
2412 # Run User Defined Start Command
2413 if ('startCommand' in opts):
2414 newNode.cmdPrint(opts['startCommand'])
2415
2416
2417 # Configure NetFlow
2418 nflowValues = self.appPrefs['netflow']
2419 if len(nflowValues['nflowTarget']) > 0:
2420 nflowEnabled = False
2421 nflowSwitches = ''
2422 for widget in self.widgetToItem:
2423 name = widget[ 'text' ]
2424 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2425
2426 if 'Switch' in tags:
2427 opts = self.switchOpts[name]
2428 if 'netflow' in opts:
2429 if opts['netflow'] == '1':
2430 print name+' has Netflow enabled'
2431 nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
2432 nflowEnabled=True
2433 if nflowEnabled:
2434 nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '+ 'target=\\\"'+nflowValues['nflowTarget']+'\\\" '+ 'active-timeout='+nflowValues['nflowTimeout']
2435 if nflowValues['nflowAddId'] == '1':
2436 nflowCmd = nflowCmd + ' add_id_to_interface=true'
2437 else:
2438 nflowCmd = nflowCmd + ' add_id_to_interface=false'
2439 print 'cmd = '+nflowCmd+nflowSwitches
2440 call(nflowCmd+nflowSwitches, shell=True)
2441
2442 else:
2443 print 'No switches with Netflow'
2444 else:
2445 print 'No NetFlow targets specified.'
2446
2447 # Configure sFlow
2448 sflowValues = self.appPrefs['sflow']
2449 if len(sflowValues['sflowTarget']) > 0:
2450 sflowEnabled = False
2451 sflowSwitches = ''
2452 for widget in self.widgetToItem:
2453 name = widget[ 'text' ]
2454 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2455
2456 if 'Switch' in tags:
2457 opts = self.switchOpts[name]
2458 if 'sflow' in opts:
2459 if opts['sflow'] == '1':
2460 print name+' has sflow enabled'
2461 sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
2462 sflowEnabled=True
2463 if sflowEnabled:
2464 sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
2465 print 'cmd = '+sflowCmd+sflowSwitches
2466 call(sflowCmd+sflowSwitches, shell=True)
2467
2468 else:
2469 print 'No switches with sflow'
2470 else:
2471 print 'No sFlow targets specified.'
2472
2473 ## NOTE: MAKE SURE THIS IS LAST THING CALLED
2474 # Start the CLI if enabled
2475 if self.appPrefs['startCLI'] == '1':
2476 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")
2477 CLI(self.net)
2478
carlosmscabralf40ecd12013-02-01 18:15:58 -02002479 def start( self ):
2480 "Start network."
2481 if self.net is None:
2482 self.net = self.build()
Caio Elias47b243c2014-11-23 19:25:34 -02002483
2484 # Since I am going to inject per switch controllers.
2485 # I can't call net.start(). I have to replicate what it
2486 # does and add the controller options.
2487 #self.net.start()
2488 info( '**** Starting %s controllers\n' % len( self.net.controllers ) )
2489 for controller in self.net.controllers:
2490 info( str(controller) + ' ')
2491 controller.start()
2492 info('\n')
2493 info( '**** Starting %s switches\n' % len( self.net.switches ) )
2494 #for switch in self.net.switches:
2495 # info( switch.name + ' ')
2496 # switch.start( self.net.controllers )
2497 for widget in self.widgetToItem:
2498 name = widget[ 'text' ]
2499 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2500 if 'Switch' in tags:
2501 opts = self.switchOpts[name]
2502 switchControllers = []
2503 for ctrl in opts['controllers']:
2504 switchControllers.append(self.net.get(ctrl))
2505 info( name + ' ')
2506 # Figure out what controllers will manage this switch
2507 self.net.get(name).start( switchControllers )
2508 if 'LegacySwitch' in tags:
2509 self.net.get(name).start( [] )
2510 info( name + ' ')
2511 info('\n')
2512
2513 self.postStartSetup()
carlosmscabralf40ecd12013-02-01 18:15:58 -02002514
2515 def stop( self ):
2516 "Stop network."
2517 if self.net is not None:
Caio Elias47b243c2014-11-23 19:25:34 -02002518 # Stop host details
2519 for widget in self.widgetToItem:
2520 name = widget[ 'text' ]
2521 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
2522 if 'Host' in tags:
2523 newHost = self.net.get(name)
2524 opts = self.hostOpts[name]
2525 # Run User Defined Stop Command
2526 if ('stopCommand' in opts):
2527 newHost.cmdPrint(opts['stopCommand'])
2528 if 'Switch' in tags:
2529 newNode = self.net.get(name)
2530 opts = self.switchOpts[name]
2531 # Run User Defined Stop Command
2532 if ('stopCommand' in opts):
2533 newNode.cmdPrint(opts['stopCommand'])
2534
carlosmscabralf40ecd12013-02-01 18:15:58 -02002535 self.net.stop()
2536 cleanUpScreens()
2537 self.net = None
2538
Caio Elias47b243c2014-11-23 19:25:34 -02002539 def do_linkPopup(self, event):
2540 # display the popup menu
2541 if ( self.net is None ):
2542 try:
2543 self.linkPopup.tk_popup(event.x_root, event.y_root)
2544 finally:
2545 # make sure to release the grab (Tk 8.0a1 only)
2546 self.linkPopup.grab_release()
2547 else:
2548 try:
2549 self.linkPopup.tk_popup(event.x_root, event.y_root)
2550 finally:
2551 # make sure to release the grab (Tk 8.0a1 only)
2552 self.linkPopup.grab_release()
2553
2554 def do_legacyRouterPopup(self, event):
2555 # display the popup menu
2556 if ( self.net is None ):
2557 try:
2558 self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
2559 finally:
2560 # make sure to release the grab (Tk 8.0a1 only)
2561 self.legacyRouterPopup.grab_release()
2562
2563 def do_hostPopup(self, event):
2564 # display the popup menu
2565
2566 try:
2567 self.hostPopup.tk_popup(event.x_root, event.y_root)
2568 isHostPopup = True
2569 finally:
2570 # make sure to release the grab (Tk 8.0a1 only)
2571 self.hostPopup.grab_release()
2572
carlosmscabralf40ecd12013-02-01 18:15:58 -02002573 def xterm( self, _ignore=None ):
2574 "Make an xterm when a button is pressed."
2575 if ( self.selection is None or
2576 self.net is None or
2577 self.selection not in self.itemToWidget ):
2578 return
2579 name = self.itemToWidget[ self.selection ][ 'text' ]
2580 if name not in self.net.nameToNode:
2581 return
Caio Elias47b243c2014-11-23 19:25:34 -02002582 term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
2583 if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
2584 self.net.terms += term
2585 else:
2586 self.net.terms.append(term)
carlosmscabralf40ecd12013-02-01 18:15:58 -02002587
Caio Elias47b243c2014-11-23 19:25:34 -02002588 def iperf( self, _ignore=None ):
2589 "Make an xterm when a button is pressed."
2590 if ( self.selection is None or
2591 self.net is None or
2592 self.selection not in self.itemToWidget ):
2593 return
2594 name = self.itemToWidget[ self.selection ][ 'text' ]
2595 if name not in self.net.nameToNode:
2596 return
2597 self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
2598
2599 """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
2600
2601 def parseArgs( self ):
2602 """Parse command-line args and return options object.
2603 returns: opts parse options dict"""
2604
2605 if '--custom' in sys.argv:
2606 index = sys.argv.index( '--custom' )
2607 if len( sys.argv ) > index + 1:
2608 filename = sys.argv[ index + 1 ]
2609 self.parseCustomFile( filename )
2610 else:
2611 raise Exception( 'Custom file name not found' )
2612
2613 desc = ( "The %prog utility creates Mininet network from the\n"
2614 "command line. It can create parametrized topologies,\n"
2615 "invoke the Mininet CLI, and run tests." )
2616
2617 usage = ( '%prog [options]\n'
2618 '(type %prog -h for details)' )
2619
2620 opts = OptionParser( description=desc, usage=usage )
2621
2622 addDictOption( opts, TOPOS, TOPODEF, 'topo' )
2623 addDictOption( opts, LINKS, LINKDEF, 'link' )
2624
2625 opts.add_option( '--custom', type='string', default=None,
2626 help='read custom topo and node params from .py' +
2627 'file' )
2628
2629 self.options, self.args = opts.parse_args()
2630 # We don't accept extra arguments after the options
2631 if self.args:
2632 opts.print_help()
2633 exit()
2634
2635 def setCustom( self, name, value ):
2636 "Set custom parameters for MininetRunner."
2637 if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
2638 # Update dictionaries
2639 param = name.upper()
2640 globals()[ param ].update( value )
2641 elif name == 'validate':
2642 # Add custom validate function
2643 self.validate = value
2644 else:
2645 # Add or modify global variable or class
2646 globals()[ name ] = value
2647
2648 def parseCustomFile( self, fileName ):
2649 "Parse custom file and add params before parsing cmd-line options."
2650 customs = {}
2651 if os.path.isfile( fileName ):
2652 execfile( fileName, customs, customs )
2653 for name, val in customs.iteritems():
2654 self.setCustom( name, val )
2655 else:
2656 raise Exception( 'could not find custom file: %s' % fileName )
2657
2658 def importTopo( self ):
2659 print 'topo='+self.options.topo
2660 if self.options.topo == 'none':
2661 return
2662 self.newTopology()
2663 topo = buildTopo( TOPOS, self.options.topo )
2664 link = customConstructor( LINKS, self.options.link )
2665 importNet = Mininet(topo=topo, build=False, link=link)
2666 importNet.build()
2667
2668 c = self.canvas
2669 rowIncrement = 100
2670 currentY = 100
2671
2672 # Add Controllers
2673 print 'controllers:'+str(len(importNet.controllers))
2674 for controller in importNet.controllers:
2675 name = controller.name
2676 x = self.controllerCount*100+100
2677 self.addNode('Controller', self.controllerCount,
2678 float(x), float(currentY), name=name)
2679 icon = self.findWidgetByName(name)
2680 #icon.bind('<Button-3>', self.do_controllerPopup )
2681 ctrlr = { 'controllerType': 'ref',
2682 'hostname': name,
2683 'controllerProtocol': controller.protocol,
2684 'remoteIP': controller.ip,
2685 'remotePort': controller.port}
2686 self.controllers[name] = ctrlr
2687
2688
2689
2690 currentY = currentY + rowIncrement
2691
2692 # Add switches
2693 print 'switches:'+str(len(importNet.switches))
2694 columnCount = 0
2695 for switch in importNet.switches:
2696 name = switch.name
2697 self.switchOpts[name] = {}
2698 self.switchOpts[name]['nodeNum']=self.switchCount
2699 self.switchOpts[name]['hostname']=name
2700 self.switchOpts[name]['switchType']='default'
2701 self.switchOpts[name]['controllers']=[]
2702
2703 x = columnCount*100+100
2704 self.addNode('Switch', self.switchCount,
2705 float(x), float(currentY), name=name)
2706 icon = self.findWidgetByName(name)
2707 icon.bind('<Button-3>', self.do_switchPopup )
2708 # Now link to controllers
2709 for controller in importNet.controllers:
2710 self.switchOpts[name]['controllers'].append(controller.name)
2711 dest = self.findWidgetByName(controller.name)
2712 dx, dy = c.coords( self.widgetToItem[ dest ] )
2713 self.link = c.create_line(float(x),
2714 float(currentY),
2715 dx,
2716 dy,
2717 width=4,
2718 fill='red',
2719 dash=(6, 4, 2, 4),
2720 tag='link' )
2721 c.itemconfig(self.link, tags=c.gettags(self.link)+('control',))
2722 self.addLink( icon, dest, linktype='control' )
2723 self.createControlLinkBindings()
2724 self.link = self.linkWidget = None
2725 if columnCount == 9:
2726 columnCount = 0
2727 currentY = currentY + rowIncrement
2728 else:
2729 columnCount =columnCount+1
2730
2731
2732 currentY = currentY + rowIncrement
2733 # Add hosts
2734 print 'hosts:'+str(len(importNet.hosts))
2735 columnCount = 0
2736 for host in importNet.hosts:
2737 name = host.name
2738 self.hostOpts[name] = {'sched':'host'}
2739 self.hostOpts[name]['nodeNum']=self.hostCount
2740 self.hostOpts[name]['hostname']=name
2741 self.hostOpts[name]['ip']=host.IP()
2742
2743 x = columnCount*100+100
2744 self.addNode('Host', self.hostCount,
2745 float(x), float(currentY), name=name)
2746 icon = self.findWidgetByName(name)
2747 icon.bind('<Button-3>', self.do_hostPopup )
2748 if columnCount == 9:
2749 columnCount = 0
2750 currentY = currentY + rowIncrement
2751 else:
2752 columnCount =columnCount+1
2753
2754 print 'links:'+str(len(topo.links()))
2755 #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
2756 for link in topo.links():
2757 print str(link)
2758 srcNode = link[0]
2759 src = self.findWidgetByName(srcNode)
2760 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
2761
2762 destNode = link[1]
2763 dest = self.findWidgetByName(destNode)
2764 dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
2765
2766 params = topo.linkInfo( srcNode, destNode )
2767 print 'Link Parameters='+str(params)
2768
2769 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
2770 fill='blue', tag='link' )
2771 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
2772 self.addLink( src, dest, linkopts=params )
2773 self.createDataLinkBindings()
2774 self.link = self.linkWidget = None
2775
2776 importNet.stop()
carlosmscabralf40ecd12013-02-01 18:15:58 -02002777
2778def miniEditImages():
2779 "Create and return images for MiniEdit."
2780
2781 # Image data. Git will be unhappy. However, the alternative
2782 # is to keep track of separate binary files, which is also
2783 # unappealing.
2784
2785 return {
2786 'Select': BitmapImage(
2787 file='/usr/include/X11/bitmaps/left_ptr' ),
2788
Caio Elias47b243c2014-11-23 19:25:34 -02002789 'LegacyRouter': PhotoImage( data=r"""
2790 R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+3QFq1DmL3wJMmAMzZZW11dnZ
2791 2SFrtyNdmTSO6gIZMUKa8gJVqEOHzR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq
2792 6ymF4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9vwNgvwJZsX+69gsXJQFH
2793 jTtjizF0tvHx8VOm9z2V736Dhz2N3QM2acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtg
2794 tktjfQFu3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh312Gt+VGm/AQIDTmB
2795 yAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i56gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia
2796 7DpeggFt2QNPm97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er/yVVhwJJktPh
2797 70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVVhQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18
2798 zKfP9wwcLAMHCwFFiS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh7cve8pG/
2799 7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR1RMjNTF3vU2X4TZupwRSolNne4nB+T+L
2800 2YGz4zJ/zYe99YGHjRdDcT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6WlpW2t
2801 7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90uvPz8wIVKBp42SV5zbfT7wtXpStV
2802 fwFWrBVvyTt3swFz5kGBv2+1/QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
2803 u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T5yH5BAEAAAAALAAAAAAyABgA
2804 Bwj/AAEIHEiQYJY7Qwg9UsTplRIbENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4c
2805 HeoIabJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPATqEBoRB9gVJsxRlhPwHI
2806 0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkS
2807 rtwADuxCG/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmDjhTMmseoKQIFDx7R
2808 oxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq
2809 9dGvv09RHFhcIUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p9HEUFhxgMSAv
2810 jbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CF
2811 oVggmEgCyRf01WcFCYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57AgyZckpKKP
2812 GFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemMIQggeaJywSQ/wgHOAmJskQEfWqBlFBEH
2813 1P/QaGY3QOpDZXA2+A6m7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlBpZdi
2814 isd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtChRmVPNWgpr+Be+Nc9icARww9TkIEu
2815 DAsQ0O7DzGIQzD2QdDEJHTsIAROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
2816 xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE42Q9jtFIp8z0Dy1jQMA1AGzi
2817 z9VoW7310V0znYDTGMQgwUDXLDBO2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOM
2818 LQkcjvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZMDHKCTwI8EcQFHBBAAFc
2819 gGPLHwLwcMIo12Qxu0ABAQA7
2820 """),
2821
2822 'Host': PhotoImage( data=r"""
2823 R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
2824 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
2825 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
2826 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
2827 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
2828 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
2829 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
2830 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
2831 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
2832 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
2833 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
2834 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
2835 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
2836 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
2837 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
2838 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
2839 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
2840 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
2841 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
2842 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
2843 ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
2844 BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
2845 HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
2846 p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
2847 C8cSBBAQADs=
2848 """ ),
2849
2850 'NetLink': PhotoImage( data=r"""
carlosmscabralf40ecd12013-02-01 18:15:58 -02002851 R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
2852 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
2853 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
2854 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
2855 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
2856 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
2857 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
2858 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
2859 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
2860 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
2861 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
2862 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
2863 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
2864 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
2865 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
2866 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
2867 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
2868 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
2869 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
2870 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
2871 ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
2872 Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
2873 lBmxI8mSNknm1Dnx5sCAADs=
2874 """ )
2875 }
2876
Caio Elias47b243c2014-11-23 19:25:34 -02002877def addDictOption( opts, choicesDict, default, name, helpStr=None ):
2878 """Convenience function to add choices dicts to OptionParser.
2879 opts: OptionParser instance
2880 choicesDict: dictionary of valid choices, must include default
2881 default: default choice key
2882 name: long option name
2883 help: string"""
2884 if default not in choicesDict:
2885 raise Exception( 'Invalid default %s for choices dict: %s' %
2886 ( default, name ) )
2887 if not helpStr:
2888 helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
2889 '[,param=value...]' )
2890 opts.add_option( '--' + name,
2891 type='string',
2892 default = default,
2893 help = helpStr )
2894
carlosmscabralf40ecd12013-02-01 18:15:58 -02002895if __name__ == '__main__':
2896 setLogLevel( 'info' )
Caio Elias47b243c2014-11-23 19:25:34 -02002897 app = MiniEdit()
2898 """ import topology if specified """
2899 app.parseArgs()
2900 app.importTopo()
2901
2902 global isHostPopup
2903 global isRouterPopup
2904 global isLinkPopup
2905
carlosmscabralf40ecd12013-02-01 18:15:58 -02002906 app.mainloop()