blob: c1399ee2263742ec6f2acd16e6d0625e67e37929 [file] [log] [blame]
carlosmscabralf40ecd12013-02-01 18:15:58 -02001#!/usr/bin/python
2
3"""
Caio Eliase904f532014-12-01 16:19:22 -02004MiniCCNxEdit: a simple network editor for MiniCCNx
carlosmscabralf40ecd12013-02-01 18:15:58 -02005
Caio Elias2fa082d2014-12-01 16:38:04 -02006Based on miniedit by:
Caio Elias47b243c2014-11-23 19:25:34 -02007Bob Lantz, April 2010
8Gregory Gee, July 2013
carlosmscabralf40ecd12013-02-01 18:15:58 -02009
Caio Elias2fa082d2014-12-01 16:38:04 -020010Carlos Cabral, Jan 2013
11Caio Elias, Nov 2014
12
carlosmscabralf40ecd12013-02-01 18:15:58 -020013"""
carlosmscabralf40ecd12013-02-01 18:15:58 -020014
Caio Elias47b243c2014-11-23 19:25:34 -020015MINIEDIT_VERSION = '2.2.0.1'
16
17from optparse import OptionParser
18from Tkinter import *
19from ttk import Notebook
20from tkMessageBox import showinfo, showerror, showwarning
21from subprocess import call
22import tkFont
23import csv
24import tkFileDialog
25import tkSimpleDialog
26import re
27import json
28from distutils.version import StrictVersion
29import os
30import sys
31from functools import partial
32
33if 'PYTHONPATH' in os.environ:
34 sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
carlosmscabralf40ecd12013-02-01 18:15:58 -020035
36# someday: from ttk import *
37
Caio Elias47b243c2014-11-23 19:25:34 -020038from mininet.log import info, error, debug, output, setLogLevel
39from mininet.net import Mininet, VERSION
40from mininet.util import ipStr, netParse, ipAdd, quietRun
41from mininet.util import buildTopo
42from mininet.util import custom, customConstructor
carlosmscabralf40ecd12013-02-01 18:15:58 -020043from mininet.term import makeTerm, cleanUpScreens
Caio Elias47b243c2014-11-23 19:25:34 -020044from mininet.node import Controller, RemoteController, NOX, OVSController
45from mininet.node import CPULimitedHost, Host, Node
46from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch
47from mininet.link import TCLink, Intf, Link
48from mininet.cli import CLI
49from mininet.moduledeps import moduleDeps, pathCheck
50from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
51from mininet.topolib import TreeTopo
52
Caio Eliase904f532014-12-01 16:19:22 -020053print 'MiniCCNxEdit running...' #+VERSION
Caio Elias47b243c2014-11-23 19:25:34 -020054MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
55if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
56 from mininet.node import IVSSwitch
57
58TOPODEF = 'none'
59TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
60 'linear': LinearTopo,
61 'reversed': SingleSwitchReversedTopo,
62 'single': SingleSwitchTopo,
63 'none': None,
64 'tree': TreeTopo }
65LINKDEF = 'default'
66LINKS = { 'default': Link,
67 'tc': TCLink }
68HOSTDEF = 'proc'
69HOSTS = { 'proc': Host,
70 'rt': custom( CPULimitedHost, sched='rt' ),
71 'cfs': custom( CPULimitedHost, sched='cfs' ) }
72
73class LegacyRouter( Node ):
74
75 def __init__( self, name, inNamespace=True, **params ):
76 Node.__init__( self, name, inNamespace, **params )
77
78 def config( self, **_params ):
79 if self.intfs:
80 self.setParam( _params, 'setIP', ip='0.0.0.0' )
81 r = Node.config( self, **_params )
82 self.cmd('sysctl -w net.ipv4.ip_forward=1')
83 return r
84
85class CustomDialog(object):
86
87 # TODO: Fix button placement and Title and window focus lock
88 def __init__(self, master, title):
89 self.top=Toplevel(master)
90
91 self.bodyFrame = Frame(self.top)
92 self.bodyFrame.grid(row=0, column=0, sticky='nswe')
93 self.body(self.bodyFrame)
94
95 #return self.b # initial focus
96 buttonFrame = Frame(self.top, relief='ridge', bd=3, bg='lightgrey')
97 buttonFrame.grid(row=1 , column=0, sticky='nswe')
98
99 okButton = Button(buttonFrame, width=8, text='OK', relief='groove',
100 bd=4, command=self.okAction)
101 okButton.grid(row=0, column=0, sticky=E)
102
103 canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
104 bd=4, command=self.cancelAction)
105 canlceButton.grid(row=0, column=1, sticky=W)
106
107 def body(self, master):
108 self.rootFrame = master
109
110 def apply(self):
111 self.top.destroy()
112
113 def cancelAction(self):
114 self.top.destroy()
115
116 def okAction(self):
117 self.apply()
118 self.top.destroy()
119
120class HostDialog(CustomDialog):
121
122 def __init__(self, master, title, prefDefaults):
123
124 self.prefValues = prefDefaults
125 self.result = None
126
127 CustomDialog.__init__(self, master, title)
128
129 def body(self, master):
130 self.rootFrame = master
131 n = Notebook(self.rootFrame)
132 self.propFrame = Frame(n)
133 self.vlanFrame = Frame(n)
134 self.interfaceFrame = Frame(n)
135 self.mountFrame = Frame(n)
136 n.add(self.propFrame, text='Properties')
137 n.add(self.vlanFrame, text='VLAN Interfaces')
138 n.add(self.interfaceFrame, text='External Interfaces')
139 n.add(self.mountFrame, text='Private Directories')
140 n.pack()
141
142 ### TAB 1
143 # Field for Hostname
144 Label(self.propFrame, text="Hostname:").grid(row=0, sticky=E)
145 self.hostnameEntry = Entry(self.propFrame)
146 self.hostnameEntry.grid(row=0, column=1)
147 if 'hostname' in self.prefValues:
148 self.hostnameEntry.insert(0, self.prefValues['hostname'])
149
150 # Field for Switch IP
151 Label(self.propFrame, text="IP Address:").grid(row=1, sticky=E)
152 self.ipEntry = Entry(self.propFrame)
153 self.ipEntry.grid(row=1, column=1)
154 if 'ip' in self.prefValues:
155 self.ipEntry.insert(0, self.prefValues['ip'])
156
157 # Field for default route
158 Label(self.propFrame, text="Default Route:").grid(row=2, sticky=E)
159 self.routeEntry = Entry(self.propFrame)
160 self.routeEntry.grid(row=2, column=1)
161 if 'defaultRoute' in self.prefValues:
162 self.routeEntry.insert(0, self.prefValues['defaultRoute'])
163
164 # Field for CPU
165 Label(self.propFrame, text="Amount CPU:").grid(row=3, sticky=E)
166 self.cpuEntry = Entry(self.propFrame)
167 self.cpuEntry.grid(row=3, column=1)
168 if 'cpu' in self.prefValues:
169 self.cpuEntry.insert(0, str(self.prefValues['cpu']))
170 # Selection of Scheduler
171 if 'sched' in self.prefValues:
172 sched = self.prefValues['sched']
173 else:
174 sched = 'host'
175 self.schedVar = StringVar(self.propFrame)
176 self.schedOption = OptionMenu(self.propFrame, self.schedVar, "host", "cfs", "rt")
177 self.schedOption.grid(row=3, column=2, sticky=W)
178 self.schedVar.set(sched)
179
180 # Selection of Cores
181 Label(self.propFrame, text="Cores:").grid(row=4, sticky=E)
182 self.coreEntry = Entry(self.propFrame)
183 self.coreEntry.grid(row=4, column=1)
184 if 'cores' in self.prefValues:
185 self.coreEntry.insert(1, self.prefValues['cores'])
186
187 # Start command
188 Label(self.propFrame, text="Start Command:").grid(row=5, sticky=E)
189 self.startEntry = Entry(self.propFrame)
190 self.startEntry.grid(row=5, column=1, sticky='nswe', columnspan=3)
191 if 'startCommand' in self.prefValues:
192 self.startEntry.insert(0, str(self.prefValues['startCommand']))
193 # Stop command
194 Label(self.propFrame, text="Stop Command:").grid(row=6, sticky=E)
195 self.stopEntry = Entry(self.propFrame)
196 self.stopEntry.grid(row=6, column=1, sticky='nswe', columnspan=3)
197 if 'stopCommand' in self.prefValues:
198 self.stopEntry.insert(0, str(self.prefValues['stopCommand']))
199
200 ### TAB 2
201 # External Interfaces
202 self.externalInterfaces = 0
203 Label(self.interfaceFrame, text="External Interface:").grid(row=0, column=0, sticky=E)
204 self.b = Button( self.interfaceFrame, text='Add', command=self.addInterface)
205 self.b.grid(row=0, column=1)
206
207 self.interfaceFrame = VerticalScrolledTable(self.interfaceFrame, rows=0, columns=1, title='External Interfaces')
208 self.interfaceFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
209 self.tableFrame = self.interfaceFrame.interior
210 self.tableFrame.addRow(value=['Interface Name'], readonly=True)
211
212 # Add defined interfaces
213 externalInterfaces = []
214 if 'externalInterfaces' in self.prefValues:
215 externalInterfaces = self.prefValues['externalInterfaces']
216
217 for externalInterface in externalInterfaces:
218 self.tableFrame.addRow(value=[externalInterface])
219
220 ### TAB 3
221 # VLAN Interfaces
222 self.vlanInterfaces = 0
223 Label(self.vlanFrame, text="VLAN Interface:").grid(row=0, column=0, sticky=E)
224 self.vlanButton = Button( self.vlanFrame, text='Add', command=self.addVlanInterface)
225 self.vlanButton.grid(row=0, column=1)
226
227 self.vlanFrame = VerticalScrolledTable(self.vlanFrame, rows=0, columns=2, title='VLAN Interfaces')
228 self.vlanFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
229 self.vlanTableFrame = self.vlanFrame.interior
230 self.vlanTableFrame.addRow(value=['IP Address','VLAN ID'], readonly=True)
231
232 vlanInterfaces = []
233 if 'vlanInterfaces' in self.prefValues:
234 vlanInterfaces = self.prefValues['vlanInterfaces']
235 for vlanInterface in vlanInterfaces:
236 self.vlanTableFrame.addRow(value=vlanInterface)
237
238 ### TAB 4
239 # Private Directories
240 self.privateDirectories = 0
241 Label(self.mountFrame, text="Private Directory:").grid(row=0, column=0, sticky=E)
242 self.mountButton = Button( self.mountFrame, text='Add', command=self.addDirectory)
243 self.mountButton.grid(row=0, column=1)
244
245 self.mountFrame = VerticalScrolledTable(self.mountFrame, rows=0, columns=2, title='Directories')
246 self.mountFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
247 self.mountTableFrame = self.mountFrame.interior
248 self.mountTableFrame.addRow(value=['Mount','Persistent Directory'], readonly=True)
249
250 directoryList = []
251 if 'privateDirectory' in self.prefValues:
252 directoryList = self.prefValues['privateDirectory']
253 for privateDir in directoryList:
254 if isinstance( privateDir, tuple ):
255 self.mountTableFrame.addRow(value=privateDir)
256 else:
257 self.mountTableFrame.addRow(value=[privateDir,''])
carlosmscabralf40ecd12013-02-01 18:15:58 -0200258
259
Caio Elias47b243c2014-11-23 19:25:34 -0200260 def addDirectory( self ):
261 self.mountTableFrame.addRow()
carlosmscabralf40ecd12013-02-01 18:15:58 -0200262
Caio Elias47b243c2014-11-23 19:25:34 -0200263 def addVlanInterface( self ):
264 self.vlanTableFrame.addRow()
265
266 def addInterface( self ):
267 self.tableFrame.addRow()
268
269 def apply(self):
270 externalInterfaces = []
271 for row in range(self.tableFrame.rows):
272 if (len(self.tableFrame.get(row, 0)) > 0 and
273 row > 0):
274 externalInterfaces.append(self.tableFrame.get(row, 0))
275 vlanInterfaces = []
276 for row in range(self.vlanTableFrame.rows):
277 if (len(self.vlanTableFrame.get(row, 0)) > 0 and
278 len(self.vlanTableFrame.get(row, 1)) > 0 and
279 row > 0):
280 vlanInterfaces.append([self.vlanTableFrame.get(row, 0), self.vlanTableFrame.get(row, 1)])
281 privateDirectories = []
282 for row in range(self.mountTableFrame.rows):
283 if (len(self.mountTableFrame.get(row, 0)) > 0 and row > 0):
284 if(len(self.mountTableFrame.get(row, 1)) > 0):
285 privateDirectories.append((self.mountTableFrame.get(row, 0), self.mountTableFrame.get(row, 1)))
286 else:
287 privateDirectories.append(self.mountTableFrame.get(row, 0))
288
289 results = {'cpu': self.cpuEntry.get(),
290 'cores':self.coreEntry.get(),
291 'sched':self.schedVar.get(),
292 'hostname':self.hostnameEntry.get(),
293 'ip':self.ipEntry.get(),
294 'defaultRoute':self.routeEntry.get(),
295 'startCommand':self.startEntry.get(),
296 'stopCommand':self.stopEntry.get(),
297 'privateDirectory':privateDirectories,
298 'externalInterfaces':externalInterfaces,
299 'vlanInterfaces':vlanInterfaces}
300 self.result = results
301
Caio Elias47b243c2014-11-23 19:25:34 -0200302class VerticalScrolledTable(LabelFrame):
303 """A pure Tkinter scrollable frame that actually works!
304
305 * Use the 'interior' attribute to place widgets inside the scrollable frame
306 * Construct and pack/place/grid normally
307 * This frame only allows vertical scrolling
carlosmscabralf40ecd12013-02-01 18:15:58 -0200308
Caio Elias47b243c2014-11-23 19:25:34 -0200309 """
310 def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
311 LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
carlosmscabralf40ecd12013-02-01 18:15:58 -0200312
Caio Elias47b243c2014-11-23 19:25:34 -0200313 # create a canvas object and a vertical scrollbar for scrolling it
314 vscrollbar = Scrollbar(self, orient=VERTICAL)
315 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
316 canvas = Canvas(self, bd=0, highlightthickness=0,
317 yscrollcommand=vscrollbar.set)
318 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
319 vscrollbar.config(command=canvas.yview)
320
321 # reset the view
322 canvas.xview_moveto(0)
323 canvas.yview_moveto(0)
324
325 # create a frame inside the canvas which will be scrolled with it
326 self.interior = interior = TableFrame(canvas, rows=rows, columns=columns)
327 interior_id = canvas.create_window(0, 0, window=interior,
328 anchor=NW)
329
330 # track changes to the canvas and frame width and sync them,
331 # also updating the scrollbar
332 def _configure_interior(event):
333 # update the scrollbars to match the size of the inner frame
334 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
335 canvas.config(scrollregion="0 0 %s %s" % size)
336 if interior.winfo_reqwidth() != canvas.winfo_width():
337 # update the canvas's width to fit the inner frame
338 canvas.config(width=interior.winfo_reqwidth())
339 interior.bind('<Configure>', _configure_interior)
340
341 def _configure_canvas(event):
342 if interior.winfo_reqwidth() != canvas.winfo_width():
343 # update the inner frame's width to fill the canvas
344 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
345 canvas.bind('<Configure>', _configure_canvas)
346
347 return
348
349class TableFrame(Frame):
350 def __init__(self, parent, rows=2, columns=2):
351
352 Frame.__init__(self, parent, background="black")
353 self._widgets = []
354 self.rows = rows
355 self.columns = columns
356 for row in range(rows):
357 current_row = []
358 for column in range(columns):
359 label = Entry(self, borderwidth=0)
360 label.grid(row=row, column=column, sticky="wens", padx=1, pady=1)
361 current_row.append(label)
362 self._widgets.append(current_row)
363
364 def set(self, row, column, value):
365 widget = self._widgets[row][column]
366 widget.insert(0, value)
367
368 def get(self, row, column):
369 widget = self._widgets[row][column]
370 return widget.get()
371
372 def addRow( self, value=None, readonly=False ):
373 #print "Adding row " + str(self.rows +1)
374 current_row = []
375 for column in range(self.columns):
376 label = Entry(self, borderwidth=0)
377 label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
378 if value is not None:
379 label.insert(0, value[column])
380 if (readonly == True):
381 label.configure(state='readonly')
382 current_row.append(label)
383 self._widgets.append(current_row)
384 self.update_idletasks()
385 self.rows += 1
386
387class LinkDialog(tkSimpleDialog.Dialog):
388
389 def __init__(self, parent, title, linkDefaults):
390
391 self.linkValues = linkDefaults
392
393 tkSimpleDialog.Dialog.__init__(self, parent, title)
394
395 def body(self, master):
396
397 self.var = StringVar(master)
398 Label(master, text="Bandwidth:").grid(row=0, sticky=E)
399 self.e1 = Entry(master)
400 self.e1.grid(row=0, column=1)
401 Label(master, text="Mbit").grid(row=0, column=2, sticky=W)
402 if 'bw' in self.linkValues:
403 self.e1.insert(0,str(self.linkValues['bw']))
404
405 Label(master, text="Delay:").grid(row=1, sticky=E)
406 self.e2 = Entry(master)
407 self.e2.grid(row=1, column=1)
408 if 'delay' in self.linkValues:
409 self.e2.insert(0, self.linkValues['delay'])
410
411 Label(master, text="Loss:").grid(row=2, sticky=E)
412 self.e3 = Entry(master)
413 self.e3.grid(row=2, column=1)
414 Label(master, text="%").grid(row=2, column=2, sticky=W)
415 if 'loss' in self.linkValues:
416 self.e3.insert(0, str(self.linkValues['loss']))
417
418 Label(master, text="Max Queue size:").grid(row=3, sticky=E)
419 self.e4 = Entry(master)
420 self.e4.grid(row=3, column=1)
421 if 'max_queue_size' in self.linkValues:
422 self.e4.insert(0, str(self.linkValues['max_queue_size']))
423
424 Label(master, text="Jitter:").grid(row=4, sticky=E)
425 self.e5 = Entry(master)
426 self.e5.grid(row=4, column=1)
427 if 'jitter' in self.linkValues:
428 self.e5.insert(0, self.linkValues['jitter'])
429
430 Label(master, text="Speedup:").grid(row=5, sticky=E)
431 self.e6 = Entry(master)
432 self.e6.grid(row=5, column=1)
433 if 'speedup' in self.linkValues:
434 self.e6.insert(0, str(self.linkValues['speedup']))
435
436 return self.e1 # initial focus
437
438 def apply(self):
439 self.result = {}
440 if (len(self.e1.get()) > 0):
441 self.result['bw'] = int(self.e1.get())
442 if (len(self.e2.get()) > 0):
443 self.result['delay'] = self.e2.get()
444 if (len(self.e3.get()) > 0):
445 self.result['loss'] = int(self.e3.get())
446 if (len(self.e4.get()) > 0):
447 self.result['max_queue_size'] = int(self.e4.get())
448 if (len(self.e5.get()) > 0):
449 self.result['jitter'] = self.e5.get()
450 if (len(self.e6.get()) > 0):
451 self.result['speedup'] = int(self.e6.get())
452
Caio Elias47b243c2014-11-23 19:25:34 -0200453class ToolTip(object):
454
455 def __init__(self, widget):
456 self.widget = widget
457 self.tipwindow = None
458 self.id = None
459 self.x = self.y = 0
460
461 def showtip(self, text):
462 "Display text in tooltip window"
463 self.text = text
464 if self.tipwindow or not self.text:
465 return
466 x, y, cx, cy = self.widget.bbox("insert")
467 x = x + self.widget.winfo_rootx() + 27
468 y = y + cy + self.widget.winfo_rooty() +27
469 self.tipwindow = tw = Toplevel(self.widget)
470 tw.wm_overrideredirect(1)
471 tw.wm_geometry("+%d+%d" % (x, y))
472 try:
473 # For Mac OS
474 tw.tk.call("::tk::unsupported::MacWindowStyle",
475 "style", tw._w,
476 "help", "noActivates")
477 except TclError:
478 pass
479 label = Label(tw, text=self.text, justify=LEFT,
480 background="#ffffe0", relief=SOLID, borderwidth=1,
481 font=("tahoma", "8", "normal"))
482 label.pack(ipadx=1)
483
484 def hidetip(self):
485 tw = self.tipwindow
486 self.tipwindow = None
487 if tw:
488 tw.destroy()
carlosmscabralf40ecd12013-02-01 18:15:58 -0200489
490class MiniEdit( Frame ):
491
Caio Eliase904f532014-12-01 16:19:22 -0200492 "A simple network editor for MiniCCNx."
carlosmscabralf40ecd12013-02-01 18:15:58 -0200493
Caio Eliase904f532014-12-01 16:19:22 -0200494 def __init__( self, parent=None, cheight=600, cwidth=1000, template_file='miniccnx.conf' ):
Caio Elias47b243c2014-11-23 19:25:34 -0200495
Caio Eliase904f532014-12-01 16:19:22 -0200496 #self.defaultIpBase='10.0.0.0/8'
Caio Elias47b243c2014-11-23 19:25:34 -0200497
498 self.nflowDefaults = {'nflowTarget':'',
499 'nflowTimeout':'600',
500 'nflowAddId':'0'}
501 self.sflowDefaults = {'sflowTarget':'',
502 'sflowSampling':'400',
503 'sflowHeader':'128',
504 'sflowPolling':'30'}
505
506 self.appPrefs={
Caio Eliase904f532014-12-01 16:19:22 -0200507 #"ipBase": self.defaultIpBase,
Caio Elias47b243c2014-11-23 19:25:34 -0200508 "startCLI": "0",
509 "terminalType": 'xterm',
510 "switchType": 'ovs',
511 "dpctl": '',
512 'sflow':self.sflowDefaults,
513 'netflow':self.nflowDefaults,
514 'openFlowVersions':{'ovsOf10':'1',
515 'ovsOf11':'0',
516 'ovsOf12':'0',
517 'ovsOf13':'0'}
518
519 }
Caio Eliase904f532014-12-01 16:19:22 -0200520
521 self.template_file = template_file
carlosmscabralf40ecd12013-02-01 18:15:58 -0200522
523 Frame.__init__( self, parent )
524 self.action = None
Caio Eliase904f532014-12-01 16:19:22 -0200525 self.appName = 'MiniccnxEdit'
Caio Elias47b243c2014-11-23 19:25:34 -0200526 self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
carlosmscabralf40ecd12013-02-01 18:15:58 -0200527
528 # Style
529 self.font = ( 'Geneva', 9 )
530 self.smallFont = ( 'Geneva', 7 )
531 self.bg = 'white'
532
533 # Title
534 self.top = self.winfo_toplevel()
535 self.top.title( self.appName )
536
537 # Menu bar
538 self.createMenubar()
539
540 # Editing canvas
541 self.cheight, self.cwidth = cheight, cwidth
542 self.cframe, self.canvas = self.createCanvas()
543
544 # Toolbar
Caio Elias47b243c2014-11-23 19:25:34 -0200545 self.controllers = {}
546
547 # Toolbar
carlosmscabralf40ecd12013-02-01 18:15:58 -0200548 self.images = miniEditImages()
549 self.buttons = {}
550 self.active = None
Caio Elias47b243c2014-11-23 19:25:34 -0200551 self.tools = ( 'Select', 'Host', 'LegacyRouter', 'NetLink' )
552 self.customColors = { 'LegacyRouter': 'darkGreen', 'Host': 'blue' }
carlosmscabralf40ecd12013-02-01 18:15:58 -0200553 self.toolbar = self.createToolbar()
554
555 # Layout
556 self.toolbar.grid( column=0, row=0, sticky='nsew')
557 self.cframe.grid( column=1, row=0 )
558 self.columnconfigure( 1, weight=1 )
559 self.rowconfigure( 0, weight=1 )
560 self.pack( expand=True, fill='both' )
561
562 # About box
563 self.aboutBox = None
564
565 # Initialize node data
566 self.nodeBindings = self.createNodeBindings()
Caio Elias47b243c2014-11-23 19:25:34 -0200567 self.nodePrefixes = { 'LegacyRouter': 'r', 'Host': 'h'}
carlosmscabralf40ecd12013-02-01 18:15:58 -0200568 self.widgetToItem = {}
569 self.itemToWidget = {}
570
571 # Initialize link tool
572 self.link = self.linkWidget = None
573
574 # Selection support
575 self.selection = None
576
577 # Keyboard bindings
578 self.bind( '<Control-q>', lambda event: self.quit() )
579 self.bind( '<KeyPress-Delete>', self.deleteSelection )
580 self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
581 self.focus()
582
Caio Elias47b243c2014-11-23 19:25:34 -0200583 #Mouse bindings
Caio Elias47b243c2014-11-23 19:25:34 -0200584 self.bind( '<Button-1>', lambda event: self.clearPopups )
585
586 self.hostPopup = Menu(self.top, tearoff=0)
587 self.hostPopup.add_command(label='Host Options', font=self.font)
588 self.hostPopup.add_separator()
589 self.hostPopup.add_command(label='Properties', font=self.font, command=self.hostDetails )
590
591 self.legacyRouterPopup = Menu(self.top, tearoff=0)
592 self.legacyRouterPopup.add_command(label='Router Options', font=self.font)
593
594 self.linkPopup = Menu(self.top, tearoff=0)
595 self.linkPopup.add_command(label='Link Options', font=self.font)
Caio Eliase904f532014-12-01 16:19:22 -0200596 #self.linkPopup.add_separator()
597 #self.linkPopup.add_command(label='Properties', font=self.font, command=self.linkDetails )
Caio Elias47b243c2014-11-23 19:25:34 -0200598
carlosmscabralf40ecd12013-02-01 18:15:58 -0200599 # Event handling initalization
600 self.linkx = self.linky = self.linkItem = None
601 self.lastSelection = None
602
603 # Model initialization
604 self.links = {}
Caio Elias47b243c2014-11-23 19:25:34 -0200605 self.hostOpts = {}
606 self.switchOpts = {}
607 self.hostCount = 0
608 self.switchCount = 0
609 self.controllerCount = 0
carlosmscabralf40ecd12013-02-01 18:15:58 -0200610 self.net = None
611
612 # Close window gracefully
613 Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
614
615 def quit( self ):
616 "Stop our network, if any, then quit."
617 self.stop()
618 Frame.quit( self )
619
Caio Elias47b243c2014-11-23 19:25:34 -0200620 def createMenubar( self ): # MODIFICADO - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200621 "Create our menu bar."
622
623 font = self.font
624
625 mbar = Menu( self.top, font=font )
626 self.top.configure( menu=mbar )
627
Caio Elias47b243c2014-11-23 19:25:34 -0200628
Caio Eliase904f532014-12-01 16:19:22 -0200629 fileMenu = Menu( mbar, tearoff=False )
630 mbar.add_cascade( label="File", font=font, menu=fileMenu )
631 fileMenu.add_command( label="New", font=font, command=self.newTopology )
632 fileMenu.add_command( label="Open", font=font, command=self.loadTopology )
633 fileMenu.add_command( label="Save", font=font, command=self.saveTopology )
Caio Elias47b243c2014-11-23 19:25:34 -0200634 #fileMenu.add_command( label="Export Level 2 Script", font=font, command=self.exportScript )
Caio Eliase904f532014-12-01 16:19:22 -0200635 fileMenu.add_separator()
636 fileMenu.add_command( label='Quit', command=self.quit, font=font )
Caio Elias47b243c2014-11-23 19:25:34 -0200637
638 #editMenu.add_command( label="Preferences", font=font, command=self.prefDetails)
639
640 #runMenu = Menu( mbar, tearoff=False )
641 #mbar.add_cascade( label="Run", font=font, menu=runMenu )
642 #runMenu.add_command( label="Run", font=font, command=self.doRun )
643 #runMenu.add_command( label="Stop", font=font, command=self.doStop )
644 #fileMenu.add_separator()
645 #runMenu.add_command( label='Show OVS Summary', font=font, command=self.ovsShow )
646 #runMenu.add_command( label='Root Terminal', font=font, command=self.rootTerminal )
Caio Elias47b243c2014-11-23 19:25:34 -0200647
carlosmscabralf40ecd12013-02-01 18:15:58 -0200648 editMenu = Menu( mbar, tearoff=False )
649 mbar.add_cascade( label="Edit", font=font, menu=editMenu )
650 editMenu.add_command( label="Cut", font=font,
651 command=lambda: self.deleteSelection( None ) )
652
Caio Eliase904f532014-12-01 16:19:22 -0200653 # Application menu
654 appMenu = Menu( mbar, tearoff=False )
655 mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
656 appMenu.add_command( label='About Mini-CCNx', command=self.about,
657 font=font)
658 #appMenu.add_separator()
659 #appMenu.add_command( label='Quit', command=self.quit, font=font )
660
Caio Elias47b243c2014-11-23 19:25:34 -0200661 # Canvas - TUDO IGUAL - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200662
663 def createCanvas( self ):
664 "Create and return our scrolling canvas frame."
665 f = Frame( self )
666
667 canvas = Canvas( f, width=self.cwidth, height=self.cheight,
668 bg=self.bg )
669
670 # Scroll bars
671 xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
672 ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
673 canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
674
675 # Resize box
676 resize = Label( f, bg='white' )
677
678 # Layout
679 canvas.grid( row=0, column=1, sticky='nsew')
680 ybar.grid( row=0, column=2, sticky='ns')
681 xbar.grid( row=1, column=1, sticky='ew' )
682 resize.grid( row=1, column=2, sticky='nsew' )
683
684 # Resize behavior
685 f.rowconfigure( 0, weight=1 )
686 f.columnconfigure( 1, weight=1 )
687 f.grid( row=0, column=0, sticky='nsew' )
688 f.bind( '<Configure>', lambda event: self.updateScrollRegion() )
689
690 # Mouse bindings
691 canvas.bind( '<ButtonPress-1>', self.clickCanvas )
692 canvas.bind( '<B1-Motion>', self.dragCanvas )
693 canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
694
695 return f, canvas
696
697 def updateScrollRegion( self ):
698 "Update canvas scroll region to hold everything."
699 bbox = self.canvas.bbox( 'all' )
700 if bbox is not None:
701 self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
702 bbox[ 3 ] ) )
703
704 def canvasx( self, x_root ):
705 "Convert root x coordinate to canvas coordinate."
706 c = self.canvas
707 return c.canvasx( x_root ) - c.winfo_rootx()
708
709 def canvasy( self, y_root ):
710 "Convert root y coordinate to canvas coordinate."
711 c = self.canvas
712 return c.canvasy( y_root ) - c.winfo_rooty()
713
714 # Toolbar
715
Caio Elias47b243c2014-11-23 19:25:34 -0200716 def activate( self, toolName ): #IGUAL - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200717 "Activate a tool and press its button."
718 # Adjust button appearance
719 if self.active:
720 self.buttons[ self.active ].configure( relief='raised' )
721 self.buttons[ toolName ].configure( relief='sunken' )
722 # Activate dynamic bindings
723 self.active = toolName
724
Caio Elias47b243c2014-11-23 19:25:34 -0200725
726 def createToolTip(self, widget, text): #NOVA - CRIA HINTS E TIPS
727 toolTip = ToolTip(widget)
728 def enter(event):
729 toolTip.showtip(text)
730 def leave(event):
731 toolTip.hidetip()
732 widget.bind('<Enter>', enter)
733 widget.bind('<Leave>', leave)
734
735 def createToolbar( self ): #MODIFICADO - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200736 "Create and return our toolbar frame."
737
738 toolbar = Frame( self )
739
740 # Tools
741 for tool in self.tools:
742 cmd = ( lambda t=tool: self.activate( t ) )
743 b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
744 if tool in self.images:
Caio Elias47b243c2014-11-23 19:25:34 -0200745 b.config( height=35, image=self.images[ tool ] )
746 self.createToolTip(b, str(tool))
carlosmscabralf40ecd12013-02-01 18:15:58 -0200747 # b.config( compound='top' )
748 b.pack( fill='x' )
749 self.buttons[ tool ] = b
750 self.activate( self.tools[ 0 ] )
751
752 # Spacer
753 Label( toolbar, text='' ).pack()
754
755 # Commands
756 #for cmd, color in [ ( 'Stop', 'darkRed' ), ( 'Run', 'darkGreen' ) ]:
757 # doCmd = getattr( self, 'do' + cmd )
758 # b = Button( toolbar, text=cmd, font=self.smallFont,
759 # fg=color, command=doCmd )
760 # b.pack( fill='x', side='bottom' )
761
Caio Elias47b243c2014-11-23 19:25:34 -0200762 # abaixo copiado Mini-CCNx para criar botao Generate
763
764 for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200765 doCmd = getattr( self, 'do' + cmd )
766 b = Button( toolbar, text=cmd, font=self.smallFont,
767 fg=color, command=doCmd )
768 b.pack( fill='x', side='bottom' )
769
carlosmscabralf40ecd12013-02-01 18:15:58 -0200770 return toolbar
771
Caio Elias47b243c2014-11-23 19:25:34 -0200772 def doGenerate( self ): #COPIA Mini-CCNx - GERA TEMPLATE
carlosmscabralf40ecd12013-02-01 18:15:58 -0200773 "Generate template."
774 self.activate( 'Select' )
775 for tool in self.tools:
776 self.buttons[ tool ].config( state='disabled' )
777
778 self.buildTemplate()
779
Caio Elias47b243c2014-11-23 19:25:34 -0200780 for tool in self.tools:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200781 self.buttons[ tool ].config( state='normal' )
782
Caio Elias47b243c2014-11-23 19:25:34 -0200783 #def doRun( self ):
784 # "Run command."
785 # self.activate( 'Select' )
786 # for tool in self.tools:
787 # self.buttons[ tool ].config( state='disabled' )
788 # self.start()
789
carlosmscabralf40ecd12013-02-01 18:15:58 -0200790 def doStop( self ):
791 "Stop command."
792 self.stop()
793 for tool in self.tools:
794 self.buttons[ tool ].config( state='normal' )
795
Caio Elias47b243c2014-11-23 19:25:34 -0200796 def buildTemplate( self ): #COPIA Mini-CCNx para criar Template
797 "Generate template"
carlosmscabralf40ecd12013-02-01 18:15:58 -0200798
Caio Elias47b243c2014-11-23 19:25:34 -0200799 template = open(self.template_file, 'w')
800
801 # hosts
802 template.write('[hosts]\n')
803 for widget in self.widgetToItem:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200804 name = widget[ 'text' ]
805 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
Caio Elias47b243c2014-11-23 19:25:34 -0200806 if 'Host' in tags:
807 template.write(name + ':\n')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200808
Caio Elias47b243c2014-11-23 19:25:34 -0200809 # switches/routers
810 template.write('[routers]\n')
Caio Eliase904f532014-12-01 16:19:22 -0200811 for router in self.switchOpts.values():
812 #print router
813 hostname=router['hostname']
814 nodetype=router['switchType']
815 nodenum=router['nodeNum']
816
817 template.write(hostname + ':\n')
818
819 #name = widget[ 'text' ]
820 #tags = self.canvas.gettags( self.widgetToItem[ widget ] )
821 #if 'Switch' in tags:
822 #template.write(name + ':\n')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200823
824 # Make links
Caio Elias47b243c2014-11-23 19:25:34 -0200825 template.write('[links]\n')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200826 for link in self.links.values():
Caio Eliase904f532014-12-01 16:19:22 -0200827 dst=link['dest']
828 src=link['src']
829 linkopts=link['linkOpts']
830 linktype=link['type']
831
832 #print linkopts
833 #print linktype
834
carlosmscabralf40ecd12013-02-01 18:15:58 -0200835 srcName, dstName = src[ 'text' ], dst[ 'text' ]
836 template.write(srcName + ':' + dstName + '\n')
837
Caio Eliase904f532014-12-01 16:19:22 -0200838 template.close()
Caio Elias47b243c2014-11-23 19:25:34 -0200839
840 def addNode( self, node, nodeNum, x, y, name=None):
841 "Add a new node to our canvas."
842 if 'Switch' == node:
843 self.switchCount += 1
844 if 'Host' == node:
845 self.hostCount += 1
846 if 'Controller' == node:
847 self.controllerCount += 1
848 if name is None:
849 name = self.nodePrefixes[ node ] + nodeNum
850 self.addNamedNode(node, name, x, y)
851
852 def addNamedNode( self, node, name, x, y):
853 "Add a new node to our canvas."
854 c = self.canvas
855 icon = self.nodeIcon( node, name )
856 item = self.canvas.create_window( x, y, anchor='c', window=icon,
857 tags=node )
858 self.widgetToItem[ icon ] = item
859 self.itemToWidget[ item ] = icon
860 icon.links = {}
861
862 def convertJsonUnicode(self, input):
863 "Some part of Mininet don't like Unicode"
864 if isinstance(input, dict):
865 return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
866 elif isinstance(input, list):
867 return [self.convertJsonUnicode(element) for element in input]
868 elif isinstance(input, unicode):
869 return input.encode('utf-8')
870 else:
871 return input
872
873 def loadTopology( self ):
874 "Load command."
875 c = self.canvas
876
877 myFormats = [
Caio Eliase904f532014-12-01 16:19:22 -0200878 ('Miniccnx Topology','*.mnccnx'),
Caio Elias47b243c2014-11-23 19:25:34 -0200879 ('All Files','*'),
880 ]
881 f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
882 if f == None:
883 return
884 self.newTopology()
885 loadedTopology = self.convertJsonUnicode(json.load(f))
886
887 # Load application preferences
888 if 'application' in loadedTopology:
889 self.appPrefs = dict(self.appPrefs.items() + loadedTopology['application'].items())
890 if "ovsOf10" not in self.appPrefs["openFlowVersions"]:
891 self.appPrefs["openFlowVersions"]["ovsOf10"] = '0'
892 if "ovsOf11" not in self.appPrefs["openFlowVersions"]:
893 self.appPrefs["openFlowVersions"]["ovsOf11"] = '0'
894 if "ovsOf12" not in self.appPrefs["openFlowVersions"]:
895 self.appPrefs["openFlowVersions"]["ovsOf12"] = '0'
896 if "ovsOf13" not in self.appPrefs["openFlowVersions"]:
897 self.appPrefs["openFlowVersions"]["ovsOf13"] = '0'
898 if "sflow" not in self.appPrefs:
899 self.appPrefs["sflow"] = self.sflowDefaults
900 if "netflow" not in self.appPrefs:
901 self.appPrefs["netflow"] = self.nflowDefaults
902
Caio Elias47b243c2014-11-23 19:25:34 -0200903 # Load hosts
904 hosts = loadedTopology['hosts']
905 for host in hosts:
906 nodeNum = host['number']
907 hostname = 'h'+nodeNum
908 if 'hostname' in host['opts']:
909 hostname = host['opts']['hostname']
910 else:
911 host['opts']['hostname'] = hostname
912 if 'nodeNum' not in host['opts']:
913 host['opts']['nodeNum'] = int(nodeNum)
914 x = host['x']
915 y = host['y']
916 self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
917
918 # Fix JSON converting tuple to list when saving
919 if 'privateDirectory' in host['opts']:
920 newDirList = []
921 for privateDir in host['opts']['privateDirectory']:
922 if isinstance( privateDir, list ):
923 newDirList.append((privateDir[0],privateDir[1]))
924 else:
925 newDirList.append(privateDir)
926 host['opts']['privateDirectory'] = newDirList
927 self.hostOpts[hostname] = host['opts']
928 icon = self.findWidgetByName(hostname)
929 icon.bind('<Button-3>', self.do_hostPopup )
930
931 # Load switches
932 switches = loadedTopology['switches']
933 for switch in switches:
934 nodeNum = switch['number']
935 hostname = 's'+nodeNum
Caio Elias47b243c2014-11-23 19:25:34 -0200936 if 'switchType' not in switch['opts']:
937 switch['opts']['switchType'] = 'default'
938 if 'hostname' in switch['opts']:
939 hostname = switch['opts']['hostname']
940 else:
941 switch['opts']['hostname'] = hostname
942 if 'nodeNum' not in switch['opts']:
943 switch['opts']['nodeNum'] = int(nodeNum)
944 x = switch['x']
945 y = switch['y']
946 if switch['opts']['switchType'] == "legacyRouter":
947 self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
948 icon = self.findWidgetByName(hostname)
949 icon.bind('<Button-3>', self.do_legacyRouterPopup )
Caio Eliase904f532014-12-01 16:19:22 -0200950 self.switchOpts[hostname] = switch['opts']
Caio Elias47b243c2014-11-23 19:25:34 -0200951
952 # Load links
953 links = loadedTopology['links']
954 for link in links:
955 srcNode = link['src']
956 src = self.findWidgetByName(srcNode)
957 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
958
959 destNode = link['dest']
960 dest = self.findWidgetByName(destNode)
961 dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
962
963 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
964 fill='blue', tag='link' )
965 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
966 self.addLink( src, dest, linkopts=link['opts'] )
967 self.createDataLinkBindings()
968 self.link = self.linkWidget = None
969
970 f.close
971
972 def findWidgetByName( self, name ):
973 for widget in self.widgetToItem:
974 if name == widget[ 'text' ]:
975 return widget
976
977 def newTopology( self ):
978 "New command."
979 for widget in self.widgetToItem.keys():
980 self.deleteItem( self.widgetToItem[ widget ] )
981 self.hostCount = 0
982 self.switchCount = 0
983 self.links = {}
984 self.hostOpts = {}
985 self.switchOpts = {}
Caio Eliase904f532014-12-01 16:19:22 -0200986 #self.appPrefs["ipBase"]= self.defaultIpBase
Caio Elias47b243c2014-11-23 19:25:34 -0200987
988 def saveTopology( self ):
989 "Save command."
990 myFormats = [
Caio Eliase904f532014-12-01 16:19:22 -0200991 ('Miniccnx Topology','*.mnccnx'),
Caio Elias47b243c2014-11-23 19:25:34 -0200992 ('All Files','*'),
993 ]
994
995 savingDictionary = {}
996 fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
997 if len(fileName ) > 0:
998 # Save Application preferences
999 savingDictionary['version'] = '2'
1000
1001 # Save Switches and Hosts
1002 hostsToSave = []
1003 switchesToSave = []
1004 controllersToSave = []
1005 for widget in self.widgetToItem:
1006 name = widget[ 'text' ]
1007 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1008 x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
1009 if 'LegacyRouter' in tags:
1010 nodeNum = self.switchOpts[name]['nodeNum']
1011 nodeToSave = {'number':str(nodeNum),
1012 'x':str(x1),
1013 'y':str(y1),
1014 'opts':self.switchOpts[name] }
1015 switchesToSave.append(nodeToSave)
1016 elif 'Host' in tags:
1017 nodeNum = self.hostOpts[name]['nodeNum']
1018 nodeToSave = {'number':str(nodeNum),
1019 'x':str(x1),
1020 'y':str(y1),
1021 'opts':self.hostOpts[name] }
1022 hostsToSave.append(nodeToSave)
1023 else:
1024 raise Exception( "Cannot create mystery node: " + name )
1025 savingDictionary['hosts'] = hostsToSave
1026 savingDictionary['switches'] = switchesToSave
1027 savingDictionary['controllers'] = controllersToSave
1028
1029 # Save Links
1030 linksToSave = []
1031 for link in self.links.values():
1032 src = link['src']
1033 dst = link['dest']
1034 linkopts = link['linkOpts']
1035
1036 srcName, dstName = src[ 'text' ], dst[ 'text' ]
1037 linkToSave = {'src':srcName,
1038 'dest':dstName,
1039 'opts':linkopts}
1040 if link['type'] == 'data':
1041 linksToSave.append(linkToSave)
1042 savingDictionary['links'] = linksToSave
1043
1044 # Save Application preferences
1045 savingDictionary['application'] = self.appPrefs
1046
1047 try:
1048 f = open(fileName, 'wb')
1049 f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
1050 except Exception as er:
1051 print er
1052 finally:
1053 f.close()
1054
carlosmscabralf40ecd12013-02-01 18:15:58 -02001055 # Generic canvas handler
1056 #
1057 # We could have used bindtags, as in nodeIcon, but
1058 # the dynamic approach used here
1059 # may actually require less code. In any case, it's an
1060 # interesting introspection-based alternative to bindtags.
1061
1062 def canvasHandle( self, eventName, event ):
1063 "Generic canvas event handler"
1064 if self.active is None:
1065 return
1066 toolName = self.active
1067 handler = getattr( self, eventName + toolName, None )
1068 if handler is not None:
1069 handler( event )
1070
1071 def clickCanvas( self, event ):
1072 "Canvas click handler."
1073 self.canvasHandle( 'click', event )
1074
1075 def dragCanvas( self, event ):
1076 "Canvas drag handler."
1077 self.canvasHandle( 'drag', event )
1078
1079 def releaseCanvas( self, event ):
1080 "Canvas mouse up handler."
1081 self.canvasHandle( 'release', event )
1082
1083 # Currently the only items we can select directly are
1084 # links. Nodes are handled by bindings in the node icon.
1085
1086 def findItem( self, x, y ):
1087 "Find items at a location in our canvas."
1088 items = self.canvas.find_overlapping( x, y, x, y )
1089 if len( items ) == 0:
1090 return None
1091 else:
1092 return items[ 0 ]
1093
1094 # Canvas bindings for Select, Host, Switch and Link tools
1095
1096 def clickSelect( self, event ):
1097 "Select an item."
1098 self.selectItem( self.findItem( event.x, event.y ) )
1099
1100 def deleteItem( self, item ):
1101 "Delete an item."
1102 # Don't delete while network is running
1103 if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
1104 return
1105 # Delete from model
1106 if item in self.links:
1107 self.deleteLink( item )
1108 if item in self.itemToWidget:
1109 self.deleteNode( item )
1110 # Delete from view
1111 self.canvas.delete( item )
1112
1113 def deleteSelection( self, _event ):
1114 "Delete the selected item."
1115 if self.selection is not None:
1116 self.deleteItem( self.selection )
1117 self.selectItem( None )
1118
Caio Elias47b243c2014-11-23 19:25:34 -02001119 def clearPopups(self):
1120 print 'Entrou funcao clear_popups'
1121
1122 if isHostPopup == True:
1123 print 'Hostpopup = true'
1124 self.hostPopup.unpost
1125 isHostPopup = False
1126 #if isRouterPopup == True
1127 #if isLinkPopup == True
1128
carlosmscabralf40ecd12013-02-01 18:15:58 -02001129 def nodeIcon( self, node, name ):
1130 "Create a new node icon."
1131 icon = Button( self.canvas, image=self.images[ node ],
1132 text=name, compound='top' )
1133 # Unfortunately bindtags wants a tuple
1134 bindtags = [ str( self.nodeBindings ) ]
1135 bindtags += list( icon.bindtags() )
1136 icon.bindtags( tuple( bindtags ) )
1137 return icon
1138
1139 def newNode( self, node, event ):
1140 "Add a new node to our canvas."
1141 c = self.canvas
1142 x, y = c.canvasx( event.x ), c.canvasy( event.y )
Caio Elias47b243c2014-11-23 19:25:34 -02001143 name = self.nodePrefixes[ node ]
1144 if 'Switch' == node:
1145 self.switchCount += 1
1146 name = self.nodePrefixes[ node ] + str( self.switchCount )
1147 self.switchOpts[name] = {}
1148 self.switchOpts[name]['nodeNum']=self.switchCount
1149 self.switchOpts[name]['hostname']=name
1150 self.switchOpts[name]['switchType']='default'
1151 self.switchOpts[name]['controllers']=[]
1152 if 'LegacyRouter' == node:
1153 self.switchCount += 1
1154 name = self.nodePrefixes[ node ] + str( self.switchCount )
1155 self.switchOpts[name] = {}
1156 self.switchOpts[name]['nodeNum']=self.switchCount
1157 self.switchOpts[name]['hostname']=name
1158 self.switchOpts[name]['switchType']='legacyRouter'
1159 if 'LegacySwitch' == node:
1160 self.switchCount += 1
1161 name = self.nodePrefixes[ node ] + str( self.switchCount )
1162 self.switchOpts[name] = {}
1163 self.switchOpts[name]['nodeNum']=self.switchCount
1164 self.switchOpts[name]['hostname']=name
1165 self.switchOpts[name]['switchType']='legacySwitch'
1166 self.switchOpts[name]['controllers']=[]
1167 if 'Host' == node:
1168 self.hostCount += 1
1169 name = self.nodePrefixes[ node ] + str( self.hostCount )
1170 self.hostOpts[name] = {'sched':'host'}
1171 self.hostOpts[name]['nodeNum']=self.hostCount
1172 self.hostOpts[name]['hostname']=name
Caio Elias47b243c2014-11-23 19:25:34 -02001173
carlosmscabralf40ecd12013-02-01 18:15:58 -02001174 icon = self.nodeIcon( node, name )
1175 item = self.canvas.create_window( x, y, anchor='c', window=icon,
1176 tags=node )
1177 self.widgetToItem[ icon ] = item
1178 self.itemToWidget[ item ] = icon
1179 self.selectItem( item )
1180 icon.links = {}
Caio Elias47b243c2014-11-23 19:25:34 -02001181 if 'LegacyRouter' == node:
1182 icon.bind('<Button-3>', self.do_legacyRouterPopup )
1183 if 'Host' == node:
1184 icon.bind('<Button-3>', self.do_hostPopup )
1185
carlosmscabralf40ecd12013-02-01 18:15:58 -02001186 def clickHost( self, event ):
1187 "Add a new host to our canvas."
1188 self.newNode( 'Host', event )
1189
Caio Elias47b243c2014-11-23 19:25:34 -02001190 def clickLegacyRouter( self, event ):
1191 "Add a new switch to our canvas."
1192 self.newNode( 'LegacyRouter', event )
1193
Caio Elias47b243c2014-11-23 19:25:34 -02001194 def dragNetLink( self, event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001195 "Drag a link's endpoint to another node."
1196 if self.link is None:
1197 return
1198 # Since drag starts in widget, we use root coords
1199 x = self.canvasx( event.x_root )
1200 y = self.canvasy( event.y_root )
1201 c = self.canvas
1202 c.coords( self.link, self.linkx, self.linky, x, y )
1203
Caio Elias47b243c2014-11-23 19:25:34 -02001204 def releaseNetLink( self, _event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001205 "Give up on the current link."
1206 if self.link is not None:
1207 self.canvas.delete( self.link )
1208 self.linkWidget = self.linkItem = self.link = None
1209
1210 # Generic node handlers
1211
1212 def createNodeBindings( self ):
1213 "Create a set of bindings for nodes."
1214 bindings = {
1215 '<ButtonPress-1>': self.clickNode,
1216 '<B1-Motion>': self.dragNode,
1217 '<ButtonRelease-1>': self.releaseNode,
1218 '<Enter>': self.enterNode,
Caio Elias47b243c2014-11-23 19:25:34 -02001219 '<Leave>': self.leaveNode
carlosmscabralf40ecd12013-02-01 18:15:58 -02001220 }
1221 l = Label() # lightweight-ish owner for bindings
1222 for event, binding in bindings.items():
1223 l.bind( event, binding )
1224 return l
1225
1226 def selectItem( self, item ):
1227 "Select an item and remember old selection."
1228 self.lastSelection = self.selection
1229 self.selection = item
1230
1231 def enterNode( self, event ):
1232 "Select node on entry."
1233 self.selectNode( event )
1234
1235 def leaveNode( self, _event ):
1236 "Restore old selection on exit."
1237 self.selectItem( self.lastSelection )
1238
1239 def clickNode( self, event ):
1240 "Node click handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001241 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001242 self.startLink( event )
1243 else:
1244 self.selectNode( event )
1245 return 'break'
1246
1247 def dragNode( self, event ):
1248 "Node drag handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001249 if self.active is 'NetLink':
1250 self.dragNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001251 else:
1252 self.dragNodeAround( event )
1253
1254 def releaseNode( self, event ):
1255 "Node release handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001256 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001257 self.finishLink( event )
1258
1259 # Specific node handlers
1260
1261 def selectNode( self, event ):
1262 "Select the node that was clicked on."
1263 item = self.widgetToItem.get( event.widget, None )
1264 self.selectItem( item )
1265
1266 def dragNodeAround( self, event ):
1267 "Drag a node around on the canvas."
1268 c = self.canvas
1269 # Convert global to local coordinates;
1270 # Necessary since x, y are widget-relative
1271 x = self.canvasx( event.x_root )
1272 y = self.canvasy( event.y_root )
1273 w = event.widget
1274 # Adjust node position
1275 item = self.widgetToItem[ w ]
1276 c.coords( item, x, y )
1277 # Adjust link positions
1278 for dest in w.links:
1279 link = w.links[ dest ]
1280 item = self.widgetToItem[ dest ]
1281 x1, y1 = c.coords( item )
1282 c.coords( link, x, y, x1, y1 )
Caio Elias47b243c2014-11-23 19:25:34 -02001283 self.updateScrollRegion()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001284
Caio Elias47b243c2014-11-23 19:25:34 -02001285 def createControlLinkBindings( self ):
1286 "Create a set of bindings for nodes."
carlosmscabralf40ecd12013-02-01 18:15:58 -02001287 # Link bindings
1288 # Selection still needs a bit of work overall
1289 # Callbacks ignore event
1290
1291 def select( _event, link=self.link ):
1292 "Select item on mouse entry."
1293 self.selectItem( link )
1294
1295 def highlight( _event, link=self.link ):
1296 "Highlight item on mouse entry."
Caio Elias47b243c2014-11-23 19:25:34 -02001297 self.selectItem( link )
1298 self.canvas.itemconfig( link, fill='green' )
1299
1300 def unhighlight( _event, link=self.link ):
1301 "Unhighlight item on mouse exit."
1302 self.canvas.itemconfig( link, fill='red' )
1303 #self.selectItem( None )
1304
1305 self.canvas.tag_bind( self.link, '<Enter>', highlight )
1306 self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
1307 self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
1308
1309 def createDataLinkBindings( self ):
1310 "Create a set of bindings for nodes."
1311 # Link bindings
1312 # Selection still needs a bit of work overall
1313 # Callbacks ignore event
1314
1315 def select( _event, link=self.link ):
1316 "Select item on mouse entry."
1317 self.selectItem( link )
1318
1319 def highlight( _event, link=self.link ):
1320 "Highlight item on mouse entry."
1321 self.selectItem( link )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001322 self.canvas.itemconfig( link, fill='green' )
1323
1324 def unhighlight( _event, link=self.link ):
1325 "Unhighlight item on mouse exit."
1326 self.canvas.itemconfig( link, fill='blue' )
Caio Elias47b243c2014-11-23 19:25:34 -02001327 #self.selectItem( None )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001328
1329 self.canvas.tag_bind( self.link, '<Enter>', highlight )
1330 self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
1331 self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
Caio Elias47b243c2014-11-23 19:25:34 -02001332 self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
1333
Caio Elias47b243c2014-11-23 19:25:34 -02001334 def startLink( self, event ):
1335 "Start a new link."
1336 if event.widget not in self.widgetToItem:
1337 # Didn't click on a node
1338 return
1339
1340 w = event.widget
1341 item = self.widgetToItem[ w ]
1342 x, y = self.canvas.coords( item )
1343 self.link = self.canvas.create_line( x, y, x, y, width=4,
1344 fill='blue', tag='link' )
1345 self.linkx, self.linky = x, y
1346 self.linkWidget = w
1347 self.linkItem = item
1348
carlosmscabralf40ecd12013-02-01 18:15:58 -02001349 def finishLink( self, event ):
1350 "Finish creating a link"
1351 if self.link is None:
1352 return
1353 source = self.linkWidget
1354 c = self.canvas
1355 # Since we dragged from the widget, use root coords
1356 x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
1357 target = self.findItem( x, y )
1358 dest = self.itemToWidget.get( target, None )
1359 if ( source is None or dest is None or source == dest
1360 or dest in source.links or source in dest.links ):
Caio Elias47b243c2014-11-23 19:25:34 -02001361 self.releaseNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001362 return
1363 # For now, don't allow hosts to be directly linked
Caio Elias47b243c2014-11-23 19:25:34 -02001364 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1365 dtags = self.canvas.gettags( target )
Caio Eliase904f532014-12-01 16:19:22 -02001366 if (('Host' in stags and 'Host' in dtags)):
Caio Elias47b243c2014-11-23 19:25:34 -02001367 self.releaseNetLink( event )
1368 return
1369
1370 # Set link type
1371 linkType='data'
Caio Eliase904f532014-12-01 16:19:22 -02001372
1373 self.createDataLinkBindings()
Caio Elias47b243c2014-11-23 19:25:34 -02001374 c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
1375
carlosmscabralf40ecd12013-02-01 18:15:58 -02001376 x, y = c.coords( target )
1377 c.coords( self.link, self.linkx, self.linky, x, y )
Caio Elias47b243c2014-11-23 19:25:34 -02001378 self.addLink( source, dest, linktype=linkType )
Caio Eliase904f532014-12-01 16:19:22 -02001379
carlosmscabralf40ecd12013-02-01 18:15:58 -02001380 # We're done
1381 self.link = self.linkWidget = None
1382
1383 # Menu handlers
1384
1385 def about( self ):
1386 "Display about box."
1387 about = self.aboutBox
1388 if about is None:
1389 bg = 'white'
1390 about = Toplevel( bg='white' )
1391 about.title( 'About' )
Caio Elias2fa082d2014-12-01 16:38:04 -02001392 info = self.appName + ': a simple network editor for MiniCCNx - based on Miniedit'
1393 warning = 'Development version - not entirely functional!'
1394 #version = 'MiniEdit '+MINIEDIT_VERSION
1395 author = 'Carlos Cabral, Jan 2013'
1396 author2 = 'Caio Elias, Nov 2014'
1397 author3 = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
Caio Elias47b243c2014-11-23 19:25:34 -02001398 enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
1399 www = 'http://gregorygee.wordpress.com/category/miniedit/'
carlosmscabralf40ecd12013-02-01 18:15:58 -02001400 line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
Caio Elias2fa082d2014-12-01 16:38:04 -02001401 line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001402 line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
Caio Elias2fa082d2014-12-01 16:38:04 -02001403 line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
1404 line5 = Label( about, text=author3, font='Helvetica 9', bg=bg )
1405 line6 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
1406 line7 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
1407
1408
1409 line7.insert(0, www)
1410 line7.configure(state='readonly')
carlosmscabralf40ecd12013-02-01 18:15:58 -02001411 line1.pack( padx=20, pady=10 )
1412 line2.pack(pady=10 )
1413 line3.pack(pady=10 )
Caio Elias47b243c2014-11-23 19:25:34 -02001414 line4.pack(pady=10 )
1415 line5.pack(pady=10 )
Caio Elias2fa082d2014-12-01 16:38:04 -02001416 line6.pack(pady=10 )
1417 line7.pack(pady=10 )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001418 hide = ( lambda about=about: about.withdraw() )
1419 self.aboutBox = about
1420 # Hide on close rather than destroying window
1421 Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
1422 # Show (existing) window
1423 about.deiconify()
1424
1425 def createToolImages( self ):
1426 "Create toolbar (and icon) images."
1427
Caio Elias47b243c2014-11-23 19:25:34 -02001428 def checkIntf( self, intf ):
1429 "Make sure intf exists and is not configured."
1430 if ( ' %s:' % intf ) not in quietRun( 'ip link show' ):
1431 showerror(title="Error",
1432 message='External interface ' +intf + ' does not exist! Skipping.')
1433 return False
1434 ips = re.findall( r'\d+\.\d+\.\d+\.\d+', quietRun( 'ifconfig ' + intf ) )
1435 if ips:
1436 showerror(title="Error",
1437 message= intf + ' has an IP address and is probably in use! Skipping.' )
1438 return False
1439 return True
1440
1441 def hostDetails( self, _ignore=None ):
1442 if ( self.selection is None or
1443 self.net is not None or
1444 self.selection not in self.itemToWidget ):
1445 return
1446 widget = self.itemToWidget[ self.selection ]
1447 name = widget[ 'text' ]
1448 tags = self.canvas.gettags( self.selection )
1449 if 'Host' not in tags:
1450 return
1451
1452 prefDefaults = self.hostOpts[name]
1453 hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults)
1454 self.master.wait_window(hostBox.top)
1455 if hostBox.result:
1456 newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
1457 newHostOpts['sched'] = hostBox.result['sched']
1458 if len(hostBox.result['startCommand']) > 0:
1459 newHostOpts['startCommand'] = hostBox.result['startCommand']
1460 if len(hostBox.result['stopCommand']) > 0:
1461 newHostOpts['stopCommand'] = hostBox.result['stopCommand']
1462 if len(hostBox.result['cpu']) > 0:
1463 newHostOpts['cpu'] = float(hostBox.result['cpu'])
1464 if len(hostBox.result['cores']) > 0:
1465 newHostOpts['cores'] = hostBox.result['cores']
1466 if len(hostBox.result['hostname']) > 0:
1467 newHostOpts['hostname'] = hostBox.result['hostname']
1468 name = hostBox.result['hostname']
1469 widget[ 'text' ] = name
1470 if len(hostBox.result['defaultRoute']) > 0:
1471 newHostOpts['defaultRoute'] = hostBox.result['defaultRoute']
1472 if len(hostBox.result['ip']) > 0:
1473 newHostOpts['ip'] = hostBox.result['ip']
1474 if len(hostBox.result['externalInterfaces']) > 0:
1475 newHostOpts['externalInterfaces'] = hostBox.result['externalInterfaces']
1476 if len(hostBox.result['vlanInterfaces']) > 0:
1477 newHostOpts['vlanInterfaces'] = hostBox.result['vlanInterfaces']
1478 if len(hostBox.result['privateDirectory']) > 0:
1479 newHostOpts['privateDirectory'] = hostBox.result['privateDirectory']
1480 self.hostOpts[name] = newHostOpts
1481 print 'New host details for ' + name + ' = ' + str(newHostOpts)
1482
Caio Elias47b243c2014-11-23 19:25:34 -02001483 def linkUp( self ):
1484 if ( self.selection is None or
1485 self.net is None):
1486 return
1487 link = self.selection
1488 linkDetail = self.links[link]
1489 src = linkDetail['src']
1490 dst = linkDetail['dest']
1491 srcName, dstName = src[ 'text' ], dst[ 'text' ]
1492 self.net.configLinkStatus(srcName, dstName, 'up')
1493 self.canvas.itemconfig(link, dash=())
1494
1495 def linkDown( self ):
1496 if ( self.selection is None or
1497 self.net is None):
1498 return
1499 link = self.selection
1500 linkDetail = self.links[link]
1501 src = linkDetail['src']
1502 dst = linkDetail['dest']
1503 srcName, dstName = src[ 'text' ], dst[ 'text' ]
1504 self.net.configLinkStatus(srcName, dstName, 'down')
1505 self.canvas.itemconfig(link, dash=(4, 4))
1506
1507 def linkDetails( self, _ignore=None ):
1508 if ( self.selection is None or
1509 self.net is not None):
1510 return
1511 link = self.selection
1512
1513 linkDetail = self.links[link]
1514 src = linkDetail['src']
1515 dest = linkDetail['dest']
1516 linkopts = linkDetail['linkOpts']
1517 linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
1518 if linkBox.result is not None:
1519 linkDetail['linkOpts'] = linkBox.result
1520 print 'New link details = ' + str(linkBox.result)
1521
1522 def prefDetails( self ):
1523 prefDefaults = self.appPrefs
1524 prefBox = PrefsDialog(self, title='Preferences', prefDefaults=prefDefaults)
1525 print 'New Prefs = ' + str(prefBox.result)
1526 if prefBox.result:
1527 self.appPrefs = prefBox.result
1528
Caio Elias47b243c2014-11-23 19:25:34 -02001529 def listBridge( self, _ignore=None ):
1530 if ( self.selection is None or
1531 self.net is None or
1532 self.selection not in self.itemToWidget ):
1533 return
1534 name = self.itemToWidget[ self.selection ][ 'text' ]
1535 tags = self.canvas.gettags( self.selection )
1536
1537 if name not in self.net.nameToNode:
1538 return
1539 if 'Switch' in tags or 'LegacySwitch' in tags:
1540 call(["xterm -T 'Bridge Details' -sb -sl 2000 -e 'ovs-vsctl list bridge " + name + "; read -p \"Press Enter to close\"' &"], shell=True)
1541
1542 def ovsShow( self, _ignore=None ):
1543 call(["xterm -T 'OVS Summary' -sb -sl 2000 -e 'ovs-vsctl show; read -p \"Press Enter to close\"' &"], shell=True)
1544
1545 def rootTerminal( self, _ignore=None ):
1546 call(["xterm -T 'Root Terminal' -sb -sl 2000 &"], shell=True)
1547
carlosmscabralf40ecd12013-02-01 18:15:58 -02001548 # Model interface
1549 #
1550 # Ultimately we will either want to use a topo or
1551 # mininet object here, probably.
1552
Caio Elias47b243c2014-11-23 19:25:34 -02001553 def addLink( self, source, dest, linktype='data', linkopts={} ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001554 "Add link to model."
1555 source.links[ dest ] = self.link
1556 dest.links[ source ] = self.link
Caio Elias47b243c2014-11-23 19:25:34 -02001557 self.links[ self.link ] = {'type' :linktype,
1558 'src':source,
1559 'dest':dest,
1560 'linkOpts':linkopts}
carlosmscabralf40ecd12013-02-01 18:15:58 -02001561
1562 def deleteLink( self, link ):
1563 "Delete link from model."
1564 pair = self.links.get( link, None )
1565 if pair is not None:
Caio Elias47b243c2014-11-23 19:25:34 -02001566 source=pair['src']
1567 dest=pair['dest']
carlosmscabralf40ecd12013-02-01 18:15:58 -02001568 del source.links[ dest ]
1569 del dest.links[ source ]
Caio Elias47b243c2014-11-23 19:25:34 -02001570 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1571 dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
1572 ltags = self.canvas.gettags( link )
1573
1574 if 'control' in ltags:
1575 controllerName = ''
1576 switchName = ''
1577 if 'Controller' in stags:
1578 controllerName = source[ 'text' ]
1579 switchName = dest[ 'text' ]
1580 else:
1581 controllerName = dest[ 'text' ]
1582 switchName = source[ 'text' ]
1583
1584 if controllerName in self.switchOpts[switchName]['controllers']:
1585 self.switchOpts[switchName]['controllers'].remove(controllerName)
1586
1587
carlosmscabralf40ecd12013-02-01 18:15:58 -02001588 if link is not None:
1589 del self.links[ link ]
1590
1591 def deleteNode( self, item ):
1592 "Delete node (and its links) from model."
Caio Elias47b243c2014-11-23 19:25:34 -02001593
carlosmscabralf40ecd12013-02-01 18:15:58 -02001594 widget = self.itemToWidget[ item ]
Caio Elias47b243c2014-11-23 19:25:34 -02001595 tags = self.canvas.gettags(item)
Caio Elias47b243c2014-11-23 19:25:34 -02001596
carlosmscabralf40ecd12013-02-01 18:15:58 -02001597 for link in widget.links.values():
1598 # Delete from view and model
1599 self.deleteItem( link )
1600 del self.itemToWidget[ item ]
1601 del self.widgetToItem[ widget ]
1602
Caio Elias47b243c2014-11-23 19:25:34 -02001603 def buildNodes( self, net):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001604 # Make nodes
Caio Elias47b243c2014-11-23 19:25:34 -02001605 print "Getting Hosts and Switches."
carlosmscabralf40ecd12013-02-01 18:15:58 -02001606 for widget in self.widgetToItem:
1607 name = widget[ 'text' ]
1608 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
Caio Elias47b243c2014-11-23 19:25:34 -02001609 #print name+' has '+str(tags)
1610
Caio Eliase904f532014-12-01 16:19:22 -02001611 if 'LegacyRouter' in tags:
Caio Elias47b243c2014-11-23 19:25:34 -02001612 newSwitch = net.addHost( name , cls=LegacyRouter)
carlosmscabralf40ecd12013-02-01 18:15:58 -02001613 elif 'Host' in tags:
Caio Elias47b243c2014-11-23 19:25:34 -02001614 opts = self.hostOpts[name]
1615 #print str(opts)
1616 ip = None
1617 defaultRoute = None
1618 if 'defaultRoute' in opts and len(opts['defaultRoute']) > 0:
1619 defaultRoute = 'via '+opts['defaultRoute']
1620 if 'ip' in opts and len(opts['ip']) > 0:
1621 ip = opts['ip']
1622 else:
1623 nodeNum = self.hostOpts[name]['nodeNum']
Caio Eliase904f532014-12-01 16:19:22 -02001624 #ipBaseNum, prefixLen = netParse( self.appPrefs['ipBase'] )
1625 #ip = ipAdd(i=nodeNum, prefixLen=prefixLen, ipBaseNum=ipBaseNum)
Caio Elias47b243c2014-11-23 19:25:34 -02001626
1627 # Create the correct host class
1628 if 'cores' in opts or 'cpu' in opts:
1629 if ('privateDirectory' in opts):
1630 hostCls = partial( CPULimitedHost,
1631 privateDirs=opts['privateDirectory'] )
1632 else:
1633 hostCls=CPULimitedHost
1634 else:
1635 if ('privateDirectory' in opts):
1636 hostCls = partial( Host,
1637 privateDirs=opts['privateDirectory'] )
1638 else:
1639 hostCls=Host
1640 print hostCls
1641 newHost = net.addHost( name,
1642 cls=hostCls,
1643 ip=ip,
1644 defaultRoute=defaultRoute
1645 )
1646
1647 # Set the CPULimitedHost specific options
1648 if 'cores' in opts:
1649 newHost.setCPUs(cores = opts['cores'])
1650 if 'cpu' in opts:
1651 newHost.setCPUFrac(f=opts['cpu'], sched=opts['sched'])
1652
1653 # Attach external interfaces
1654 if ('externalInterfaces' in opts):
1655 for extInterface in opts['externalInterfaces']:
1656 if self.checkIntf(extInterface):
1657 Intf( extInterface, node=newHost )
1658 if ('vlanInterfaces' in opts):
1659 if len(opts['vlanInterfaces']) > 0:
1660 print 'Checking that OS is VLAN prepared'
1661 self.pathCheck('vconfig', moduleName='vlan package')
1662 moduleDeps( add='8021q' )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001663 else:
1664 raise Exception( "Cannot create mystery node: " + name )
Caio Elias47b243c2014-11-23 19:25:34 -02001665
1666 def pathCheck( self, *args, **kwargs ):
1667 "Make sure each program in *args can be found in $PATH."
1668 moduleName = kwargs.get( 'moduleName', 'it' )
1669 for arg in args:
1670 if not quietRun( 'which ' + arg ):
1671 showerror(title="Error",
1672 message= 'Cannot find required executable %s.\n' % arg +
1673 'Please make sure that %s is installed ' % moduleName +
1674 'and available in your $PATH.' )
1675
1676 def buildLinks( self, net):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001677 # Make links
Caio Elias47b243c2014-11-23 19:25:34 -02001678 print "Getting Links."
1679 for key,link in self.links.iteritems():
1680 tags = self.canvas.gettags(key)
1681 if 'data' in tags:
1682 src=link['src']
1683 dst=link['dest']
1684 linkopts=link['linkOpts']
1685 srcName, dstName = src[ 'text' ], dst[ 'text' ]
1686 srcNode, dstNode = net.nameToNode[ srcName ], net.nameToNode[ dstName ]
1687 if linkopts:
1688 net.addLink(srcNode, dstNode, cls=TCLink, **linkopts)
1689 else:
1690 #print str(srcNode)
1691 #print str(dstNode)
1692 net.addLink(srcNode, dstNode)
1693 self.canvas.itemconfig(key, dash=())
1694
1695
1696 def build( self ):
1697 print "Build network based on our topology."
1698
1699 dpctl = None
1700 if len(self.appPrefs['dpctl']) > 0:
1701 dpctl = int(self.appPrefs['dpctl'])
1702 net = Mininet( topo=None,
1703 listenPort=dpctl,
Caio Eliase904f532014-12-01 16:19:22 -02001704 build=False)
1705 #ipBase=self.appPrefs['ipBase'] )
Caio Elias47b243c2014-11-23 19:25:34 -02001706
1707 self.buildNodes(net)
1708 self.buildLinks(net)
carlosmscabralf40ecd12013-02-01 18:15:58 -02001709
1710 # Build network (we have to do this separately at the moment )
1711 net.build()
1712
1713 return net
1714
Caio Elias47b243c2014-11-23 19:25:34 -02001715
1716 def postStartSetup( self ):
1717
1718 # Setup host details
1719 for widget in self.widgetToItem:
1720 name = widget[ 'text' ]
1721 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1722 if 'Host' in tags:
1723 newHost = self.net.get(name)
1724 opts = self.hostOpts[name]
1725 # Attach vlan interfaces
1726 if ('vlanInterfaces' in opts):
1727 for vlanInterface in opts['vlanInterfaces']:
1728 print 'adding vlan interface '+vlanInterface[1]
1729 newHost.cmdPrint('ifconfig '+name+'-eth0.'+vlanInterface[1]+' '+vlanInterface[0])
1730 # Run User Defined Start Command
1731 if ('startCommand' in opts):
1732 newHost.cmdPrint(opts['startCommand'])
Caio Elias47b243c2014-11-23 19:25:34 -02001733
1734 # Configure NetFlow
1735 nflowValues = self.appPrefs['netflow']
1736 if len(nflowValues['nflowTarget']) > 0:
1737 nflowEnabled = False
1738 nflowSwitches = ''
1739 for widget in self.widgetToItem:
1740 name = widget[ 'text' ]
1741 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1742
1743 if 'Switch' in tags:
1744 opts = self.switchOpts[name]
1745 if 'netflow' in opts:
1746 if opts['netflow'] == '1':
1747 print name+' has Netflow enabled'
1748 nflowSwitches = nflowSwitches+' -- set Bridge '+name+' netflow=@MiniEditNF'
1749 nflowEnabled=True
1750 if nflowEnabled:
1751 nflowCmd = 'ovs-vsctl -- --id=@MiniEditNF create NetFlow '+ 'target=\\\"'+nflowValues['nflowTarget']+'\\\" '+ 'active-timeout='+nflowValues['nflowTimeout']
1752 if nflowValues['nflowAddId'] == '1':
1753 nflowCmd = nflowCmd + ' add_id_to_interface=true'
1754 else:
1755 nflowCmd = nflowCmd + ' add_id_to_interface=false'
1756 print 'cmd = '+nflowCmd+nflowSwitches
1757 call(nflowCmd+nflowSwitches, shell=True)
1758
1759 else:
1760 print 'No switches with Netflow'
1761 else:
1762 print 'No NetFlow targets specified.'
1763
1764 # Configure sFlow
1765 sflowValues = self.appPrefs['sflow']
1766 if len(sflowValues['sflowTarget']) > 0:
1767 sflowEnabled = False
1768 sflowSwitches = ''
1769 for widget in self.widgetToItem:
1770 name = widget[ 'text' ]
1771 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1772
1773 if 'Switch' in tags:
1774 opts = self.switchOpts[name]
1775 if 'sflow' in opts:
1776 if opts['sflow'] == '1':
1777 print name+' has sflow enabled'
1778 sflowSwitches = sflowSwitches+' -- set Bridge '+name+' sflow=@MiniEditSF'
1779 sflowEnabled=True
1780 if sflowEnabled:
1781 sflowCmd = 'ovs-vsctl -- --id=@MiniEditSF create sFlow '+ 'target=\\\"'+sflowValues['sflowTarget']+'\\\" '+ 'header='+sflowValues['sflowHeader']+' '+ 'sampling='+sflowValues['sflowSampling']+' '+ 'polling='+sflowValues['sflowPolling']
1782 print 'cmd = '+sflowCmd+sflowSwitches
1783 call(sflowCmd+sflowSwitches, shell=True)
1784
1785 else:
1786 print 'No switches with sflow'
1787 else:
1788 print 'No sFlow targets specified.'
1789
1790 ## NOTE: MAKE SURE THIS IS LAST THING CALLED
1791 # Start the CLI if enabled
1792 if self.appPrefs['startCLI'] == '1':
1793 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")
1794 CLI(self.net)
1795
carlosmscabralf40ecd12013-02-01 18:15:58 -02001796 def start( self ):
1797 "Start network."
1798 if self.net is None:
1799 self.net = self.build()
Caio Elias47b243c2014-11-23 19:25:34 -02001800
Caio Elias47b243c2014-11-23 19:25:34 -02001801 for widget in self.widgetToItem:
1802 name = widget[ 'text' ]
1803 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
Caio Elias47b243c2014-11-23 19:25:34 -02001804
1805 self.postStartSetup()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001806
1807 def stop( self ):
1808 "Stop network."
1809 if self.net is not None:
Caio Elias47b243c2014-11-23 19:25:34 -02001810 # Stop host details
1811 for widget in self.widgetToItem:
1812 name = widget[ 'text' ]
1813 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
1814 if 'Host' in tags:
1815 newHost = self.net.get(name)
1816 opts = self.hostOpts[name]
1817 # Run User Defined Stop Command
1818 if ('stopCommand' in opts):
1819 newHost.cmdPrint(opts['stopCommand'])
Caio Elias47b243c2014-11-23 19:25:34 -02001820
carlosmscabralf40ecd12013-02-01 18:15:58 -02001821 self.net.stop()
1822 cleanUpScreens()
1823 self.net = None
1824
Caio Elias47b243c2014-11-23 19:25:34 -02001825 def do_linkPopup(self, event):
1826 # display the popup menu
1827 if ( self.net is None ):
1828 try:
1829 self.linkPopup.tk_popup(event.x_root, event.y_root)
1830 finally:
1831 # make sure to release the grab (Tk 8.0a1 only)
1832 self.linkPopup.grab_release()
Caio Elias47b243c2014-11-23 19:25:34 -02001833
1834 def do_legacyRouterPopup(self, event):
1835 # display the popup menu
1836 if ( self.net is None ):
1837 try:
1838 self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
1839 finally:
1840 # make sure to release the grab (Tk 8.0a1 only)
1841 self.legacyRouterPopup.grab_release()
1842
1843 def do_hostPopup(self, event):
1844 # display the popup menu
Caio Eliase904f532014-12-01 16:19:22 -02001845 if ( self.net is None ):
1846 try:
1847 self.hostPopup.tk_popup(event.x_root, event.y_root)
1848 isHostPopup = True
1849 finally:
1850 # make sure to release the grab (Tk 8.0a1 only)
1851 self.hostPopup.grab_release()
Caio Elias47b243c2014-11-23 19:25:34 -02001852
carlosmscabralf40ecd12013-02-01 18:15:58 -02001853 def xterm( self, _ignore=None ):
1854 "Make an xterm when a button is pressed."
1855 if ( self.selection is None or
1856 self.net is None or
1857 self.selection not in self.itemToWidget ):
1858 return
1859 name = self.itemToWidget[ self.selection ][ 'text' ]
1860 if name not in self.net.nameToNode:
1861 return
Caio Elias47b243c2014-11-23 19:25:34 -02001862 term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
1863 if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
1864 self.net.terms += term
1865 else:
1866 self.net.terms.append(term)
carlosmscabralf40ecd12013-02-01 18:15:58 -02001867
Caio Elias47b243c2014-11-23 19:25:34 -02001868 def iperf( self, _ignore=None ):
1869 "Make an xterm when a button is pressed."
1870 if ( self.selection is None or
1871 self.net is None or
1872 self.selection not in self.itemToWidget ):
1873 return
1874 name = self.itemToWidget[ self.selection ][ 'text' ]
1875 if name not in self.net.nameToNode:
1876 return
1877 self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
1878
1879 """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
1880
1881 def parseArgs( self ):
1882 """Parse command-line args and return options object.
1883 returns: opts parse options dict"""
1884
1885 if '--custom' in sys.argv:
1886 index = sys.argv.index( '--custom' )
1887 if len( sys.argv ) > index + 1:
1888 filename = sys.argv[ index + 1 ]
1889 self.parseCustomFile( filename )
1890 else:
1891 raise Exception( 'Custom file name not found' )
1892
Caio Eliase904f532014-12-01 16:19:22 -02001893 desc = ( "The %prog utility creates Miniccnx network from the\n"
Caio Elias47b243c2014-11-23 19:25:34 -02001894 "command line. It can create parametrized topologies,\n"
Caio Eliase904f532014-12-01 16:19:22 -02001895 "invoke the Miniccnx CLI, and run tests." )
Caio Elias47b243c2014-11-23 19:25:34 -02001896
Caio Eliase904f532014-12-01 16:19:22 -02001897 usage = ( '%prog [options] [template_file]\n'
1898 '\nIf no template_file is given, generated template will be written to the file miniccnx.conf in the current directory.\n'
1899 'Type %prog -h for details)' )
Caio Elias47b243c2014-11-23 19:25:34 -02001900
1901 opts = OptionParser( description=desc, usage=usage )
1902
1903 addDictOption( opts, TOPOS, TOPODEF, 'topo' )
1904 addDictOption( opts, LINKS, LINKDEF, 'link' )
1905
1906 opts.add_option( '--custom', type='string', default=None,
1907 help='read custom topo and node params from .py' +
1908 'file' )
1909
1910 self.options, self.args = opts.parse_args()
1911 # We don't accept extra arguments after the options
1912 if self.args:
Caio Eliase904f532014-12-01 16:19:22 -02001913 if len(self.args) > 1:
1914 opts.print_help()
1915 exit()
1916 else:
1917 self.template_file=self.args[0]
Caio Elias47b243c2014-11-23 19:25:34 -02001918
1919 def setCustom( self, name, value ):
1920 "Set custom parameters for MininetRunner."
1921 if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
1922 # Update dictionaries
1923 param = name.upper()
1924 globals()[ param ].update( value )
1925 elif name == 'validate':
1926 # Add custom validate function
1927 self.validate = value
1928 else:
1929 # Add or modify global variable or class
1930 globals()[ name ] = value
1931
1932 def parseCustomFile( self, fileName ):
1933 "Parse custom file and add params before parsing cmd-line options."
1934 customs = {}
1935 if os.path.isfile( fileName ):
1936 execfile( fileName, customs, customs )
1937 for name, val in customs.iteritems():
1938 self.setCustom( name, val )
1939 else:
1940 raise Exception( 'could not find custom file: %s' % fileName )
1941
1942 def importTopo( self ):
1943 print 'topo='+self.options.topo
1944 if self.options.topo == 'none':
1945 return
1946 self.newTopology()
1947 topo = buildTopo( TOPOS, self.options.topo )
1948 link = customConstructor( LINKS, self.options.link )
1949 importNet = Mininet(topo=topo, build=False, link=link)
1950 importNet.build()
1951
1952 c = self.canvas
1953 rowIncrement = 100
1954 currentY = 100
1955
Caio Elias47b243c2014-11-23 19:25:34 -02001956 # Add switches
1957 print 'switches:'+str(len(importNet.switches))
1958 columnCount = 0
1959 for switch in importNet.switches:
1960 name = switch.name
1961 self.switchOpts[name] = {}
1962 self.switchOpts[name]['nodeNum']=self.switchCount
1963 self.switchOpts[name]['hostname']=name
1964 self.switchOpts[name]['switchType']='default'
1965 self.switchOpts[name]['controllers']=[]
1966
1967 x = columnCount*100+100
1968 self.addNode('Switch', self.switchCount,
1969 float(x), float(currentY), name=name)
1970 icon = self.findWidgetByName(name)
1971 icon.bind('<Button-3>', self.do_switchPopup )
Caio Eliase904f532014-12-01 16:19:22 -02001972
Caio Elias47b243c2014-11-23 19:25:34 -02001973 if columnCount == 9:
1974 columnCount = 0
1975 currentY = currentY + rowIncrement
1976 else:
1977 columnCount =columnCount+1
1978
Caio Elias47b243c2014-11-23 19:25:34 -02001979 currentY = currentY + rowIncrement
1980 # Add hosts
1981 print 'hosts:'+str(len(importNet.hosts))
1982 columnCount = 0
1983 for host in importNet.hosts:
1984 name = host.name
1985 self.hostOpts[name] = {'sched':'host'}
1986 self.hostOpts[name]['nodeNum']=self.hostCount
1987 self.hostOpts[name]['hostname']=name
1988 self.hostOpts[name]['ip']=host.IP()
1989
1990 x = columnCount*100+100
1991 self.addNode('Host', self.hostCount,
1992 float(x), float(currentY), name=name)
1993 icon = self.findWidgetByName(name)
1994 icon.bind('<Button-3>', self.do_hostPopup )
1995 if columnCount == 9:
1996 columnCount = 0
1997 currentY = currentY + rowIncrement
1998 else:
1999 columnCount =columnCount+1
2000
2001 print 'links:'+str(len(topo.links()))
2002 #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
2003 for link in topo.links():
2004 print str(link)
2005 srcNode = link[0]
2006 src = self.findWidgetByName(srcNode)
2007 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
2008
2009 destNode = link[1]
2010 dest = self.findWidgetByName(destNode)
2011 dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
2012
2013 params = topo.linkInfo( srcNode, destNode )
2014 print 'Link Parameters='+str(params)
2015
2016 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
2017 fill='blue', tag='link' )
2018 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
2019 self.addLink( src, dest, linkopts=params )
2020 self.createDataLinkBindings()
2021 self.link = self.linkWidget = None
2022
2023 importNet.stop()
carlosmscabralf40ecd12013-02-01 18:15:58 -02002024
2025def miniEditImages():
2026 "Create and return images for MiniEdit."
2027
2028 # Image data. Git will be unhappy. However, the alternative
2029 # is to keep track of separate binary files, which is also
2030 # unappealing.
2031
2032 return {
2033 'Select': BitmapImage(
2034 file='/usr/include/X11/bitmaps/left_ptr' ),
2035
Caio Elias47b243c2014-11-23 19:25:34 -02002036 'LegacyRouter': PhotoImage( data=r"""
2037 R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+3QFq1DmL3wJMmAMzZZW11dnZ
2038 2SFrtyNdmTSO6gIZMUKa8gJVqEOHzR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq
2039 6ymF4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9vwNgvwJZsX+69gsXJQFH
2040 jTtjizF0tvHx8VOm9z2V736Dhz2N3QM2acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtg
2041 tktjfQFu3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh312Gt+VGm/AQIDTmB
2042 yAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i56gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia
2043 7DpeggFt2QNPm97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er/yVVhwJJktPh
2044 70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVVhQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18
2045 zKfP9wwcLAMHCwFFiS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh7cve8pG/
2046 7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR1RMjNTF3vU2X4TZupwRSolNne4nB+T+L
2047 2YGz4zJ/zYe99YGHjRdDcT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6WlpW2t
2048 7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90uvPz8wIVKBp42SV5zbfT7wtXpStV
2049 fwFWrBVvyTt3swFz5kGBv2+1/QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
2050 u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T5yH5BAEAAAAALAAAAAAyABgA
2051 Bwj/AAEIHEiQYJY7Qwg9UsTplRIbENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4c
2052 HeoIabJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPATqEBoRB9gVJsxRlhPwHI
2053 0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkS
2054 rtwADuxCG/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmDjhTMmseoKQIFDx7R
2055 oxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq
2056 9dGvv09RHFhcIUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p9HEUFhxgMSAv
2057 jbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CF
2058 oVggmEgCyRf01WcFCYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57AgyZckpKKP
2059 GFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemMIQggeaJywSQ/wgHOAmJskQEfWqBlFBEH
2060 1P/QaGY3QOpDZXA2+A6m7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlBpZdi
2061 isd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtChRmVPNWgpr+Be+Nc9icARww9TkIEu
2062 DAsQ0O7DzGIQzD2QdDEJHTsIAROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
2063 xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE42Q9jtFIp8z0Dy1jQMA1AGzi
2064 z9VoW7310V0znYDTGMQgwUDXLDBO2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOM
2065 LQkcjvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZMDHKCTwI8EcQFHBBAAFc
2066 gGPLHwLwcMIo12Qxu0ABAQA7
2067 """),
2068
2069 'Host': PhotoImage( data=r"""
2070 R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
2071 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
2072 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
2073 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
2074 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
2075 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
2076 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
2077 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
2078 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
2079 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
2080 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
2081 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
2082 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
2083 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
2084 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
2085 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
2086 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
2087 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
2088 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
2089 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
2090 ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
2091 BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
2092 HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
2093 p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
2094 C8cSBBAQADs=
2095 """ ),
2096
2097 'NetLink': PhotoImage( data=r"""
carlosmscabralf40ecd12013-02-01 18:15:58 -02002098 R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
2099 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
2100 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
2101 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
2102 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
2103 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
2104 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
2105 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
2106 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
2107 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
2108 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
2109 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
2110 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
2111 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
2112 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
2113 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
2114 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
2115 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
2116 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
2117 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
2118 ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
2119 Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
2120 lBmxI8mSNknm1Dnx5sCAADs=
2121 """ )
2122 }
2123
Caio Elias47b243c2014-11-23 19:25:34 -02002124def addDictOption( opts, choicesDict, default, name, helpStr=None ):
2125 """Convenience function to add choices dicts to OptionParser.
2126 opts: OptionParser instance
2127 choicesDict: dictionary of valid choices, must include default
2128 default: default choice key
2129 name: long option name
2130 help: string"""
2131 if default not in choicesDict:
2132 raise Exception( 'Invalid default %s for choices dict: %s' %
2133 ( default, name ) )
2134 if not helpStr:
2135 helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
2136 '[,param=value...]' )
2137 opts.add_option( '--' + name,
2138 type='string',
2139 default = default,
2140 help = helpStr )
2141
carlosmscabralf40ecd12013-02-01 18:15:58 -02002142if __name__ == '__main__':
2143 setLogLevel( 'info' )
Caio Elias47b243c2014-11-23 19:25:34 -02002144 app = MiniEdit()
2145 """ import topology if specified """
2146 app.parseArgs()
2147 app.importTopo()
2148
2149 global isHostPopup
2150 global isRouterPopup
2151 global isLinkPopup
2152
carlosmscabralf40ecd12013-02-01 18:15:58 -02002153 app.mainloop()