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