blob: 8e945a4ef8f28882c8290cf745d4a24f7d0f0afa [file] [log] [blame]
ashu01b62f72015-03-12 15:16:11 -05001#!/usr/bin/python
2
3"""
ashu7b6ba182015-04-17 15:02:37 -05004MiniNDNEdit: a simple network editor for MiniNDN
ashu01b62f72015-03-12 15:16:11 -05005
6Based on miniedit by:
7Bob Lantz, April 2010
8Gregory Gee, July 2013
9
10Carlos Cabral, Jan 2013
11Caio Elias, Nov 2014
12
13"""
14
15MINIEDIT_VERSION = '2.2.0.1'
16
17from optparse import OptionParser
18from Tkinter import *
19from ttk import Notebook
20from tkMessageBox import showinfo, showerror, showwarning
ashu7b6ba182015-04-17 15:02:37 -050021from subprocess import call, Popen
ashu01b62f72015-03-12 15:16:11 -050022import tkFont
23import csv
24import tkFileDialog
25import tkSimpleDialog
26import re
27import json
28from distutils.version import StrictVersion
29import os
30import sys
ashu7b6ba182015-04-17 15:02:37 -050031import threading
ashu01b62f72015-03-12 15:16:11 -050032from functools import partial
33
34import pdb
35
36if 'PYTHONPATH' in os.environ:
37 sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
38
39# someday: from ttk import *
40
41from mininet.log import info, error, debug, output, setLogLevel
42from mininet.net import Mininet, VERSION
43from mininet.util import ipStr, netParse, ipAdd, quietRun
44from mininet.util import buildTopo
ashu2ad32e22015-05-29 13:37:40 -050045from mininet.util import custom
ashu01b62f72015-03-12 15:16:11 -050046from mininet.term import makeTerm, cleanUpScreens
47from mininet.node import Controller, RemoteController, NOX, OVSController
48from mininet.node import CPULimitedHost, Host, Node
49from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch
50from mininet.link import TCLink, Intf, Link
51from mininet.cli import CLI
52from mininet.moduledeps import moduleDeps, pathCheck
53from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
54from mininet.topolib import TreeTopo
55
ashu7b6ba182015-04-17 15:02:37 -050056print 'MiniNDNEdit running...' #+VERSION
ashu01b62f72015-03-12 15:16:11 -050057MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
58if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
59 from mininet.node import IVSSwitch
60
ashu7b6ba182015-04-17 15:02:37 -050061
62from ndn.gui import NfdFrame, NlsrFrame
63
ashu01b62f72015-03-12 15:16:11 -050064TOPODEF = 'none'
65TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
66 'linear': LinearTopo,
67 'reversed': SingleSwitchReversedTopo,
68 'single': SingleSwitchTopo,
69 'none': None,
70 'tree': TreeTopo }
71LINKDEF = 'default'
72LINKS = { 'default': Link,
73 'tc': TCLink }
74HOSTDEF = 'proc'
75HOSTS = { 'proc': Host,
76 'rt': custom( CPULimitedHost, sched='rt' ),
77 'cfs': custom( CPULimitedHost, sched='cfs' ) }
78
ashu7b6ba182015-04-17 15:02:37 -050079def runMiniNdn(window, template):
80 # Hide window
81 window.withdraw()
82
83 proc = Popen("sudo minindn %s" % template, shell=True)
84 proc.wait()
85
86 # Restore window
87 window.deiconify()
88
ashu01b62f72015-03-12 15:16:11 -050089class LegacyRouter( Node ):
90
91 def __init__( self, name, inNamespace=True, **params ):
92 Node.__init__( self, name, inNamespace, **params )
93
94 def config( self, **_params ):
95 if self.intfs:
96 self.setParam( _params, 'setIP', ip='0.0.0.0' )
97 r = Node.config( self, **_params )
98 self.cmd('sysctl -w net.ipv4.ip_forward=1')
99 return r
100
101class CustomDialog(object):
102
103 # TODO: Fix button placement and Title and window focus lock
104 def __init__(self, master, title):
105 self.top=Toplevel(master)
106
107 self.bodyFrame = Frame(self.top)
108 self.bodyFrame.grid(row=0, column=0, sticky='nswe')
109 self.body(self.bodyFrame)
110
111 #return self.b # initial focus
112 buttonFrame = Frame(self.top, relief='ridge', bd=3, bg='lightgrey')
113 buttonFrame.grid(row=1 , column=0, sticky='nswe')
114
115 okButton = Button(buttonFrame, width=8, text='OK', relief='groove',
116 bd=4, command=self.okAction)
117 okButton.grid(row=1, column=0, sticky=E)
118
119 canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
120 bd=4, command=self.cancelAction)
121 canlceButton.grid(row=1, column=1, sticky=W)
122
123 def body(self, master):
124 self.rootFrame = master
125
126 def apply(self):
127 self.top.destroy()
128
129 def cancelAction(self):
130 self.top.destroy()
131
132 def okAction(self):
133 self.apply()
134 self.top.destroy()
135
136class HostDialog(CustomDialog):
137
138 def __init__(self, master, title, prefDefaults, isRouter):
139
140 self.prefValues = prefDefaults
141 self.result = None
142 self.isRouter = isRouter
143 self.title = title
144
145 CustomDialog.__init__(self, master, title)
146
147 def body(self, master):
148 self.rootFrame = master
149 n = Notebook(self.rootFrame)
150 self.propFrame = Frame(n)
151 self.fibFrame = Frame(n)
ashu7b6ba182015-04-17 15:02:37 -0500152
153 # NDN
Ashlesh Gawandeab087da2015-07-09 15:10:02 -0500154 self.nfdFrame = NfdFrame(n, self.prefValues)
155 self.nlsrFrame = NlsrFrame(n,self.prefValues)
ashu7b6ba182015-04-17 15:02:37 -0500156
ashu01b62f72015-03-12 15:16:11 -0500157 n.add(self.propFrame, text='Properties')
158 n.add(self.fibFrame, text='FIB Entries')
ashu7b6ba182015-04-17 15:02:37 -0500159
160 n.add(self.nfdFrame, text=self.nfdFrame.frameLabel)
161 n.add(self.nlsrFrame, text=self.nlsrFrame.frameLabel)
162
ashu01b62f72015-03-12 15:16:11 -0500163 n.pack()
164
165 ### TAB 1
166 # Field for Hostname
167 Label(self.propFrame, text="Hostname:").grid(row=0, sticky=E)
168 self.hostnameEntry = Entry(self.propFrame)
169 self.hostnameEntry.grid(row=0, column=1)
170 if 'hostname' in self.prefValues:
171 self.hostnameEntry.insert(0, self.prefValues['hostname'])
172
173 # Field for CPU
174 Label(self.propFrame, text="Amount CPU:").grid(row=2, sticky=E)
175 self.cpuEntry = Entry(self.propFrame)
176 self.cpuEntry.grid(row=2, column=1)
177 Label(self.propFrame, text="%").grid(row=2, column=2, sticky=W)
178 if 'cpu' in self.prefValues:
179 self.cpuEntry.insert(0, str(self.prefValues['cpu']))
180
181 # Field for Memory
182 Label(self.propFrame, text="Amount MEM:").grid(row=3, sticky=E)
183 self.memEntry = Entry(self.propFrame)
184 self.memEntry.grid(row=3, column=1)
185 Label(self.propFrame, text="%").grid(row=3, column=2, sticky=W)
186 if 'mem' in self.prefValues:
187 self.memEntry.insert(0, str(self.prefValues['mem']))
188
189 # Field for Cache
190 Label(self.propFrame, text="Amount CACHE:").grid(row=4, sticky=E)
191 self.cacheEntry = Entry(self.propFrame)
192 self.cacheEntry.grid(row=4, column=1)
193 Label(self.propFrame, text="KBytes").grid(row=4, column=2, sticky=W)
194 if 'cache' in self.prefValues:
195 self.cacheEntry.insert(0, str(self.prefValues['cache']))
196
197 # Start command
198 #print self.isRouter
199 if self.isRouter == 'False':
Ashlesh Gawande557cb842015-07-01 15:39:44 -0500200 Label(self.propFrame, text="Start Command(s):").grid(row=5, sticky=E)
201 self.scrollbar = Scrollbar(self.propFrame, orient="horizontal")
202 self.startEntry = Entry(self.propFrame, xscrollcommand=self.scrollbar.set,)
203 self.startEntry.grid(row=5, column=1)
204 self.scrollbar.grid(row=6, column=1, sticky=N+S+E+W)
205 self.scrollbar.config(command=self.startEntry.xview)
206 Label(self.propFrame, text="[Use bash syntax]").grid(row=5, column=2, sticky=W)
ashu01b62f72015-03-12 15:16:11 -0500207 if 'startCommand' in self.prefValues:
208 self.startEntry.insert(0, str(self.prefValues['startCommand']))
209 else:
210 self.startEntry= Entry(self.propFrame)
211
212 ### TAB 2
213 # FIB Entries
214 self.fibEntries = 0
215 Label(self.fibFrame, text="FIB Entry:").grid(row=0, column=0, sticky=E)
216 self.fibButton = Button( self.fibFrame, text='Add', command=self.addEntry)
217 self.fibButton.grid(row=0, column=1)
218
219 self.fibFrame = VerticalScrolledTable(self.fibFrame, rows=0, columns=2, title='FIB Entries')
220 self.fibFrame.grid(row=1, column=0, sticky='nswe', columnspan=2)
221 self.fibTableFrame = self.fibFrame.interior
222 self.fibTableFrame.addRow(value=['Prefix','Next Hop'], readonly=True)
223
224 fibList = []
225 if 'fibEntries' in self.prefValues:
226 fibList = self.prefValues['fibEntries']
227 for fibEntr in fibList:
228 if isinstance( fibEntr, tuple ):
229 self.fibTableFrame.addRow(value=fibEntr)
230 else:
231 self.fibTableFrame.addRow(value=[fibEntr,''])
232
233 def addEntry( self ):
234 self.fibTableFrame.addRow()
235
236 def apply(self):
237 fibEntries = []
238 for row in range(self.fibTableFrame.rows):
239 if (len(self.fibTableFrame.get(row, 0)) > 0 and row > 0):
240 if(len(self.fibTableFrame.get(row, 1)) > 0):
241 fibEntries.append((self.fibTableFrame.get(row, 0), self.fibTableFrame.get(row, 1)))
242 else:
243 fibEntries.append(self.fibTableFrame.get(row, 0))
244
245 results = {'cpu': self.cpuEntry.get(),
246 'cache': self.cacheEntry.get(),
247 'mem': self.memEntry.get(),
248 'hostname':self.hostnameEntry.get(),
249 'startCommand':self.startEntry.get(),
ashu7b6ba182015-04-17 15:02:37 -0500250 'fibEntries':fibEntries,
Ashlesh Gawande3a4afb12015-07-09 09:23:30 -0500251 'nfd': self.nfdFrame.getValues(),
ashu7b6ba182015-04-17 15:02:37 -0500252 'nlsr': self.nlsrFrame.getValues()
253 }
254
ashu01b62f72015-03-12 15:16:11 -0500255 self.result = results
256
257class VerticalScrolledTable(LabelFrame):
258 """A pure Tkinter scrollable frame that actually works!
259
260 * Use the 'interior' attribute to place widgets inside the scrollable frame
261 * Construct and pack/place/grid normally
262 * This frame only allows vertical scrolling
263
264 """
265 def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
266 LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
267
268 # create a canvas object and a vertical scrollbar for scrolling it
269 vscrollbar = Scrollbar(self, orient=VERTICAL)
270 vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
271 canvas = Canvas(self, bd=0, highlightthickness=0,
272 yscrollcommand=vscrollbar.set)
273 canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
274 vscrollbar.config(command=canvas.yview)
275
276 # reset the view
277 canvas.xview_moveto(0)
278 canvas.yview_moveto(0)
279
280 # create a frame inside the canvas which will be scrolled with it
281 self.interior = interior = TableFrame(canvas, rows=rows, columns=columns)
282 interior_id = canvas.create_window(0, 0, window=interior,
283 anchor=NW)
284
285 # track changes to the canvas and frame width and sync them,
286 # also updating the scrollbar
287 def _configure_interior(event):
288 # update the scrollbars to match the size of the inner frame
289 size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
290 canvas.config(scrollregion="0 0 %s %s" % size)
291 if interior.winfo_reqwidth() != canvas.winfo_width():
292 # update the canvas's width to fit the inner frame
293 canvas.config(width=interior.winfo_reqwidth())
294 interior.bind('<Configure>', _configure_interior)
295
296 def _configure_canvas(event):
297 if interior.winfo_reqwidth() != canvas.winfo_width():
298 # update the inner frame's width to fill the canvas
299 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
300 canvas.bind('<Configure>', _configure_canvas)
301
302 return
303
304class TableFrame(Frame):
305 def __init__(self, parent, rows=2, columns=2):
306
307 Frame.__init__(self, parent, background="black")
308 self._widgets = []
309 self.rows = rows
310 self.columns = columns
311 for row in range(rows):
312 current_row = []
313 for column in range(columns):
314 label = Entry(self, borderwidth=0)
315 label.grid(row=row, column=column, sticky="wens", padx=1, pady=1)
316 current_row.append(label)
317 self._widgets.append(current_row)
318
319 def set(self, row, column, value):
320 widget = self._widgets[row][column]
321 widget.insert(0, value)
322
323 def get(self, row, column):
324 widget = self._widgets[row][column]
325 return widget.get()
326
327 def addRow( self, value=None, readonly=False ):
328 #print "Adding row " + str(self.rows +1)
329 current_row = []
330 for column in range(self.columns):
331 label = Entry(self, borderwidth=0)
332 label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
333 if value is not None:
334 label.insert(0, value[column])
335 if (readonly == True):
336 label.configure(state='readonly')
337 current_row.append(label)
338 self._widgets.append(current_row)
339 self.update_idletasks()
340 self.rows += 1
341
342class LinkDialog(tkSimpleDialog.Dialog):
343
344 def __init__(self, parent, title, linkDefaults):
345
346 self.linkValues = linkDefaults
347
348 tkSimpleDialog.Dialog.__init__(self, parent, title)
349
350 def body(self, master):
351
352 self.var = StringVar(master)
353 Label(master, text="Bandwidth:").grid(row=0, sticky=E)
354 self.e1 = Entry(master)
355 self.e1.grid(row=0, column=1)
356 Label(master, text="[1-1000] Mbps").grid(row=0, column=2, sticky=W)
357 if 'bw' in self.linkValues:
358 self.e1.insert(0,str(self.linkValues['bw']))
359
360 Label(master, text="Delay:").grid(row=1, sticky=E)
361 self.e2 = Entry(master)
362 self.e2.grid(row=1, column=1)
363 Label(master, text="[0-1000] ms").grid(row=1, column=2, sticky=W)
364 if 'delay' in self.linkValues:
365 self.e2.insert(0, self.linkValues['delay'])
366
367 Label(master, text="Loss:").grid(row=2, sticky=E)
368 self.e3 = Entry(master)
369 self.e3.grid(row=2, column=1)
370 Label(master, text="%").grid(row=2, column=2, sticky=W)
371 if 'loss' in self.linkValues:
372 self.e3.insert(0, str(self.linkValues['loss']))
373
374 return self.e1 # initial focus
375
376 def apply(self):
377 self.result = {}
378 if (len(self.e1.get()) > 0):
379 self.result['bw'] = int(self.e1.get())
380 if (len(self.e2.get()) > 0):
381 self.result['delay'] = self.e2.get()
382 if (len(self.e3.get()) > 0):
383 self.result['loss'] = int(self.e3.get())
384
385class ToolTip(object):
386
387 def __init__(self, widget):
388 self.widget = widget
389 self.tipwindow = None
390 self.id = None
391 self.x = self.y = 0
392
393 def showtip(self, text):
394 "Display text in tooltip window"
395 self.text = text
396 if self.tipwindow or not self.text:
397 return
398 x, y, cx, cy = self.widget.bbox("insert")
399 x = x + self.widget.winfo_rootx() + 27
400 y = y + cy + self.widget.winfo_rooty() +27
401 self.tipwindow = tw = Toplevel(self.widget)
402 tw.wm_overrideredirect(1)
403 tw.wm_geometry("+%d+%d" % (x, y))
404 try:
405 # For Mac OS
406 tw.tk.call("::tk::unsupported::MacWindowStyle",
407 "style", tw._w,
408 "help", "noActivates")
409 except TclError:
410 pass
411 label = Label(tw, text=self.text, justify=LEFT,
412 background="#ffffe0", relief=SOLID, borderwidth=1,
413 font=("tahoma", "8", "normal"))
414 label.pack(ipadx=1)
415
416 def hidetip(self):
417 tw = self.tipwindow
418 self.tipwindow = None
419 if tw:
420 tw.destroy()
421
422class MiniEdit( Frame ):
423
ashu7b6ba182015-04-17 15:02:37 -0500424 "A simple network editor for MiniNDN."
ashu01b62f72015-03-12 15:16:11 -0500425
ashu7b6ba182015-04-17 15:02:37 -0500426 def __init__( self, parent=None, cheight=600, cwidth=1000, template_file='minindn.conf' ):
ashu01b62f72015-03-12 15:16:11 -0500427
428 self.template_file = template_file
429
430 Frame.__init__( self, parent )
431 self.action = None
ashu7b6ba182015-04-17 15:02:37 -0500432 self.appName = 'MiniNDNEdit'
ashu01b62f72015-03-12 15:16:11 -0500433 self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
434
435 # Style
436 self.font = ( 'Geneva', 9 )
437 self.smallFont = ( 'Geneva', 7 )
438 self.bg = 'white'
439
440 # Title
441 self.top = self.winfo_toplevel()
442 self.top.title( self.appName )
443
444 # Menu bar
445 self.createMenubar()
446
447 # Editing canvas
448 self.cheight, self.cwidth = cheight, cwidth
449 self.cframe, self.canvas = self.createCanvas()
450
451 # Toolbar
452 self.controllers = {}
453
454 # Toolbar
455 self.images = miniEditImages()
456 self.buttons = {}
457 self.active = None
ashu7b6ba182015-04-17 15:02:37 -0500458 self.tools = ( 'Select', 'Host', 'NetLink' )
459 #self.customColors = { 'LegacyRouter': 'darkGreen', 'Host': 'blue' }
ashu01b62f72015-03-12 15:16:11 -0500460 self.toolbar = self.createToolbar()
461
462 # Layout
463 self.toolbar.grid( column=0, row=0, sticky='nsew')
464 self.cframe.grid( column=1, row=0 )
465 self.columnconfigure( 1, weight=1 )
466 self.rowconfigure( 0, weight=1 )
467 self.pack( expand=True, fill='both' )
468
469 # About box
470 self.aboutBox = None
471
472 # Initialize node data
473 self.nodeBindings = self.createNodeBindings()
474 self.nodePrefixes = { 'LegacyRouter': 'r', 'Host': 'h'}
475 self.widgetToItem = {}
476 self.itemToWidget = {}
477
478 # Initialize link tool
479 self.link = self.linkWidget = None
480
481 # Selection support
482 self.selection = None
483
484 # Keyboard bindings
485 self.bind( '<Control-q>', lambda event: self.quit() )
486 self.bind( '<KeyPress-Delete>', self.deleteSelection )
487 self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
488 self.focus()
489
490 #Mouse bindings
491 self.bind( '<Button-1>', lambda event: self.clearPopups )
492
493 self.hostPopup = Menu(self.top, tearoff=0)
494 self.hostPopup.add_command(label='Host Options', font=self.font, command=self.hostDetails)
495 #self.hostPopup.add_separator()
496 #self.hostPopup.add_command(label='Properties', font=self.font, command=self.hostDetails )
497
498 self.legacyRouterPopup = Menu(self.top, tearoff=0)
499 self.legacyRouterPopup.add_command(label='Router Options', font=self.font, command=self.hostDetails)
500
501 self.linkPopup = Menu(self.top, tearoff=0)
502 self.linkPopup.add_command(label='Link Options', font=self.font, command=self.linkDetails)
503 #self.linkPopup.add_separator()
504 #self.linkPopup.add_command(label='Properties', font=self.font, command=self.linkDetails )
505
506 # Event handling initalization
507 self.linkx = self.linky = self.linkItem = None
508 self.lastSelection = None
509
510 # Model initialization
511 self.links = {}
512 self.hostOpts = {}
513 self.switchOpts = {}
514 self.routerOpts = {}
515 self.hostCount = 0
516 self.routerCount = 0
517 self.net = None
518
519 # Close window gracefully
520 Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
521
522 def quit( self ):
523 "Stop our network, if any, then quit."
524 #sself.stop()
525 Frame.quit( self )
526
527 def createMenubar( self ): # MODIFICADO - OK
528 "Create our menu bar."
529
530 font = self.font
531
532 mbar = Menu( self.top, font=font )
533 self.top.configure( menu=mbar )
534
535 fileMenu = Menu( mbar, tearoff=False )
536 mbar.add_cascade( label="File", font=font, menu=fileMenu )
537 fileMenu.add_command( label="New", font=font, command=self.newTopology )
538 fileMenu.add_command( label="Open", font=font, command=self.loadTopology )
539 fileMenu.add_command( label="Save", font=font, command=self.saveTopology )
540 fileMenu.add_command( label="Generate", font=font, command=self.doGenerate )
ashu7b6ba182015-04-17 15:02:37 -0500541 fileMenu.add_command( label="Run", font=font, command=self.doRun )
ashu01b62f72015-03-12 15:16:11 -0500542 fileMenu.add_separator()
543 fileMenu.add_command( label='Quit', command=self.quit, font=font )
544
545 editMenu = Menu( mbar, tearoff=False )
546 mbar.add_cascade( label="Edit", font=font, menu=editMenu )
547 editMenu.add_command( label="Cut", font=font,
548 command=lambda: self.deleteSelection( None ) )
549
550 # Application menu
551 appMenu = Menu( mbar, tearoff=False )
552 mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
ashu7b6ba182015-04-17 15:02:37 -0500553 appMenu.add_command( label='About Mini-NDN', command=self.about,
ashu01b62f72015-03-12 15:16:11 -0500554 font=font)
555 #appMenu.add_separator()
556 #appMenu.add_command( label='Quit', command=self.quit, font=font )
557
558 # Canvas - TUDO IGUAL - OK
559
560 def createCanvas( self ):
561 "Create and return our scrolling canvas frame."
562 f = Frame( self )
563
564 canvas = Canvas( f, width=self.cwidth, height=self.cheight,
565 bg=self.bg )
566
567 # Scroll bars
568 xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
569 ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
570 canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
571
572 # Resize box
573 resize = Label( f, bg='white' )
574
575 # Layout
576 canvas.grid( row=0, column=1, sticky='nsew')
577 ybar.grid( row=0, column=2, sticky='ns')
578 xbar.grid( row=1, column=1, sticky='ew' )
579 resize.grid( row=1, column=2, sticky='nsew' )
580
581 # Resize behavior
582 f.rowconfigure( 0, weight=1 )
583 f.columnconfigure( 1, weight=1 )
584 f.grid( row=0, column=0, sticky='nsew' )
585 f.bind( '<Configure>', lambda event: self.updateScrollRegion() )
586
587 # Mouse bindings
588 canvas.bind( '<ButtonPress-1>', self.clickCanvas )
589 canvas.bind( '<B1-Motion>', self.dragCanvas )
590 canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
591
592 return f, canvas
593
594 def updateScrollRegion( self ):
595 "Update canvas scroll region to hold everything."
596 bbox = self.canvas.bbox( 'all' )
597 if bbox is not None:
598 self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
599 bbox[ 3 ] ) )
600
601 def canvasx( self, x_root ):
602 "Convert root x coordinate to canvas coordinate."
603 c = self.canvas
604 return c.canvasx( x_root ) - c.winfo_rootx()
605
606 def canvasy( self, y_root ):
607 "Convert root y coordinate to canvas coordinate."
608 c = self.canvas
609 return c.canvasy( y_root ) - c.winfo_rooty()
610
611 # Toolbar
612
613 def activate( self, toolName ): #IGUAL - OK
614 "Activate a tool and press its button."
615 # Adjust button appearance
616 if self.active:
617 self.buttons[ self.active ].configure( relief='raised' )
618 self.buttons[ toolName ].configure( relief='sunken' )
619 # Activate dynamic bindings
620 self.active = toolName
621
622
623 def createToolTip(self, widget, text): #NOVA - CRIA HINTS E TIPS
624 toolTip = ToolTip(widget)
625 def enter(event):
626 toolTip.showtip(text)
627 def leave(event):
628 toolTip.hidetip()
629 widget.bind('<Enter>', enter)
630 widget.bind('<Leave>', leave)
631
632 def createToolbar( self ): #MODIFICADO - OK
633 "Create and return our toolbar frame."
634
635 toolbar = Frame( self )
636
637 # Tools
638 for tool in self.tools:
639 cmd = ( lambda t=tool: self.activate( t ) )
640 b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
641 if tool in self.images:
642 b.config( height=35, image=self.images[ tool ] )
643 self.createToolTip(b, str(tool))
644 # b.config( compound='top' )
645 b.pack( fill='x' )
646 self.buttons[ tool ] = b
647 self.activate( self.tools[ 0 ] )
648
649 # Spacer
650 Label( toolbar, text='' ).pack()
651
ashu7b6ba182015-04-17 15:02:37 -0500652 # abaixo copiado Mini-NDN para criar botao Generate
ashu01b62f72015-03-12 15:16:11 -0500653
654 for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
655 doCmd = getattr( self, 'do' + cmd )
656 b = Button( toolbar, text=cmd, font=self.smallFont,
657 fg=color, command=doCmd )
658 b.pack( fill='x', side='bottom' )
659
660 return toolbar
661
ashu7b6ba182015-04-17 15:02:37 -0500662 def doGenerate( self ): #COPIA Mini-NDN - GERA TEMPLATE
ashu01b62f72015-03-12 15:16:11 -0500663 "Generate template."
664 self.activate( 'Select' )
665 for tool in self.tools:
666 self.buttons[ tool ].config( state='disabled' )
667
668 self.buildTemplate()
669
670 for tool in self.tools:
671 self.buttons[ tool ].config( state='normal' )
672
673 toplevel = Toplevel()
674 label1 = Label(toplevel, text="Template file generated successfully", height=0, width=30)
675 label1.pack()
676 b=Button(toplevel, text="Ok", width=5, command=toplevel.destroy)
677 b.pack(side='bottom', padx=0,pady=0)
678
ashu7b6ba182015-04-17 15:02:37 -0500679 def doRun( self ):
680 "Use current configuration to generate a template and run the topology"
681
682 # Generate temporary template file
683 old_template_file = self.template_file
684 self.template_file = "/tmp/tmp.conf"
685 self.doGenerate()
686
687 thread = threading.Thread(target=runMiniNdn, args=(self.master, self.template_file))
688 thread.start()
689
690 self.template_file = old_template_file
691
ashu01b62f72015-03-12 15:16:11 -0500692 def parseFibEntries ( self, fibEntries ):
693 "Parse FIB Entries for write"
694 result=''
695
696 for fibEntry in fibEntries:
697 entry = ','.join(map(str, fibEntry))
698 result += entry + ' '
699
700 return result
701
ashu7b6ba182015-04-17 15:02:37 -0500702 def buildTemplate( self ): #COPIA Mini-NDN para criar Template
ashu01b62f72015-03-12 15:16:11 -0500703 "Generate template"
704
705 template = open(self.template_file, 'w')
706
707 # hosts
ashu7b6ba182015-04-17 15:02:37 -0500708 template.write('[nodes]\n')
ashu01b62f72015-03-12 15:16:11 -0500709 for widget in self.widgetToItem:
710 name = widget[ 'text' ]
711 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
ashu7b6ba182015-04-17 15:02:37 -0500712 print self.hostOpts[name]
ashu01b62f72015-03-12 15:16:11 -0500713 if 'Host' in tags:
714 hOpts=self.hostOpts[name]
715 template.write(name + ': ')
716 if 'startCommand' in hOpts:
Ashlesh Gawande557cb842015-07-01 15:39:44 -0500717 cmds = hOpts['startCommand'].replace("\"", "\\\"")
718 template.write('apps="%s" ' % cmds)
ashu01b62f72015-03-12 15:16:11 -0500719 else:
720 template.write('_ ')
721 if 'cache' in hOpts:
722 template.write('cache=' + hOpts['cache'] + ' ')
723 if 'cpu' in hOpts:
724 cpu=float(hOpts['cpu'])/100
725 template.write('cpu=' + repr(cpu) + ' ')
726 if 'mem' in hOpts:
727 mem=float(hOpts['mem'])/100
728 template.write('mem=' + repr(mem) + ' ')
729 if 'fibEntries' in hOpts:
730 customFib = self.parseFibEntries(hOpts['fibEntries'])
731 template.write(customFib)
ashu7b6ba182015-04-17 15:02:37 -0500732 if 'nlsr' in hOpts:
733 values = hOpts['nlsr']
734
735 template.write('hyperbolic-state=' + values['hyperbolic-state'] + ' ')
736 template.write('radius=' + values['radius'] + ' ')
737 template.write('angle=' + values['angle'] + ' ')
738 template.write('network=' + values['network'] + ' ')
739 template.write('router=' + values['router'] + ' ')
740 template.write('site=' + values['site'] + ' ')
Ashlesh Gawandec3ed2b92015-07-01 12:58:08 -0500741 template.write('nlsr-log-level=' + values['log-level'] + ' ')
ashu7b6ba182015-04-17 15:02:37 -0500742 template.write('max-faces-per-prefix=' + values['max-faces-per-prefix'] + ' ')
Ashlesh Gawande3a4afb12015-07-09 09:23:30 -0500743 if 'nfd' in hOpts:
744 values = hOpts['nfd']
745
746 template.write('nfd-log-level=' + values['log-level'] + ' ')
ashu7b6ba182015-04-17 15:02:37 -0500747
ashu01b62f72015-03-12 15:16:11 -0500748 template.write('\n')
749
750 # switches/routers
ashu7b6ba182015-04-17 15:02:37 -0500751 #template.write('[routers]\n')
ashu01b62f72015-03-12 15:16:11 -0500752
753 for router in self.routerOpts.values():
754
755 hasOpt='False'
756 routerName=router['hostname']
757 #nodetype=router['nodetype']
758 #nodenum=router['nodenum']
759
760 rOpts=self.routerOpts[routerName]
761
762 template.write(routerName + ': ')
763
764 if 'cpu' in rOpts:
765 cpu=float(rOpts['cpu'])/100
766 template.write('cpu=' + repr(cpu) + ' ')
767 hasOpt='True'
768 if 'mem' in rOpts:
769 mem=float(rOpts['mem'])/100
770 template.write('mem=' + repr(mem) + ' ')
771 hasOpt='True'
772 if 'cache' in rOpts:
773 template.write('cache=' + rOpts['cache'] + ' ')
774 hasOpt='True'
775 if 'fibEntries' in rOpts:
776 customFib = self.parseFibEntries(rOpts['fibEntries'])
777 template.write(customFib)
778 hasOpt='True'
779 if hasOpt == 'False':
780 template.write('_')
781
782 template.write('\n')
783
784 # Make links
785 template.write('[links]\n')
786 for link in self.links.values():
787 dst=link['dest']
788 src=link['src']
789 linkopts=link['linkOpts']
790 linktype=link['type']
791
792 srcName, dstName = src[ 'text' ], dst[ 'text' ]
793 template.write(srcName + ':' + dstName + ' ')
794 if 'bw' in linkopts:
795 template.write('bw=' + str(linkopts['bw']) + ' ' )
796 if 'loss' in linkopts:
797 template.write('loss=' + repr(linkopts['loss']) + ' ' )
798 if 'delay' in linkopts:
ashu7b6ba182015-04-17 15:02:37 -0500799 template.write('delay=' + str(linkopts['delay'])+ 'ms' )
ashu01b62f72015-03-12 15:16:11 -0500800
801 template.write('\n')
802
803 template.close()
804
805 def addNode( self, node, nodeNum, x, y, name=None):
806 "Add a new node to our canvas."
807
808 if 'LegacyRouter' == node:
809 self.routerCount += 1
810 if 'Host' == node:
811 self.hostCount += 1
812 if name is None:
813 name = self.nodePrefixes[ node ] + nodeNum
814 self.addNamedNode(node, name, x, y)
815
816 def addNamedNode( self, node, name, x, y):
817 "Add a new node to our canvas."
818 c = self.canvas
819 icon = self.nodeIcon( node, name )
820 item = self.canvas.create_window( x, y, anchor='c', window=icon,
821 tags=node )
822 self.widgetToItem[ icon ] = item
823 self.itemToWidget[ item ] = icon
824 icon.links = {}
825
826 def convertJsonUnicode(self, input):
827 "Some part of Mininet don't like Unicode"
828 if isinstance(input, dict):
829 return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
830 elif isinstance(input, list):
831 return [self.convertJsonUnicode(element) for element in input]
832 elif isinstance(input, unicode):
833 return input.encode('utf-8')
834 else:
835 return input
836
837 def loadTopology( self ):
838 "Load command."
839 c = self.canvas
840
841 myFormats = [
ashu7b6ba182015-04-17 15:02:37 -0500842 ('MiniNDN Topology','*.mnndn'),
ashu01b62f72015-03-12 15:16:11 -0500843 ('All Files','*'),
844 ]
845 f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
846 if f == None:
847 return
848 self.newTopology()
849 loadedTopology = self.convertJsonUnicode(json.load(f))
850
851 # Load hosts
Ashlesh Gawandeab087da2015-07-09 15:10:02 -0500852 hosts = loadedTopology['hosts']
ashu01b62f72015-03-12 15:16:11 -0500853 for host in hosts:
854 nodeNum = host['number']
855 hostname = 'h'+nodeNum
856 if 'hostname' in host['opts']:
857 hostname = host['opts']['hostname']
858 else:
859 host['opts']['hostname'] = hostname
860 if 'nodeNum' not in host['opts']:
861 host['opts']['nodeNum'] = int(nodeNum)
Ashlesh Gawandeab087da2015-07-09 15:10:02 -0500862
ashu01b62f72015-03-12 15:16:11 -0500863 x = host['x']
864 y = host['y']
ashu7b6ba182015-04-17 15:02:37 -0500865
ashu01b62f72015-03-12 15:16:11 -0500866 self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
867
868 # Fix JSON converting tuple to list when saving
869 if 'privateDirectory' in host['opts']:
870 newDirList = []
871 for privateDir in host['opts']['privateDirectory']:
872 if isinstance( privateDir, list ):
873 newDirList.append((privateDir[0],privateDir[1]))
874 else:
875 newDirList.append(privateDir)
876 host['opts']['privateDirectory'] = newDirList
877 self.hostOpts[hostname] = host['opts']
878 icon = self.findWidgetByName(hostname)
879 icon.bind('<Button-3>', self.do_hostPopup )
880
881 # Load routers
882 routers = loadedTopology['routers']
883 for router in routers:
884 nodeNum = router['number']
885 hostname = 'r'+nodeNum
886 #print router
887 if 'nodeType' not in router['opts']:
888 router['opts']['nodeType'] = 'legacyRouter'
889 if 'hostname' in router['opts']:
890 hostname = router['opts']['hostname']
891 else:
892 router['opts']['hostname'] = hostname
893 if 'nodeNum' not in router['opts']:
894 router['opts']['nodeNum'] = int(nodeNum)
895 x = router['x']
896 y = router['y']
897 if router['opts']['nodeType'] == "legacyRouter":
898 self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
899 icon = self.findWidgetByName(hostname)
900 icon.bind('<Button-3>', self.do_legacyRouterPopup )
901 self.routerOpts[hostname] = router['opts']
902
903 # Load links
904 links = loadedTopology['links']
905 for link in links:
906 srcNode = link['src']
907 src = self.findWidgetByName(srcNode)
908 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
909
910 destNode = link['dest']
911 dest = self.findWidgetByName(destNode)
912 dx, dy = self.canvas.coords( self.widgetToItem[ dest ] )
913
914 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
915 fill='blue', tag='link' )
916 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
917 self.addLink( src, dest, linkopts=link['opts'] )
918 self.createDataLinkBindings()
919 self.link = self.linkWidget = None
920
921 f.close
922
923 def findWidgetByName( self, name ):
924 for widget in self.widgetToItem:
925 if name == widget[ 'text' ]:
926 return widget
927
928 def newTopology( self ):
929 "New command."
930 for widget in self.widgetToItem.keys():
931 self.deleteItem( self.widgetToItem[ widget ] )
932 self.hostCount = 0
933 self.routerCount = 0
934 self.links = {}
935 self.hostOpts = {}
936 self.routerOpts = {}
937
938 def saveTopology( self ):
939 "Save command."
940 myFormats = [
ashu7b6ba182015-04-17 15:02:37 -0500941 ('MiniNDN Topology','*.mnndn'),
ashu01b62f72015-03-12 15:16:11 -0500942 ('All Files','*'),
943 ]
944
945 savingDictionary = {}
946 fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
947 if len(fileName ) > 0:
948 # Save Application preferences
949 savingDictionary['version'] = '2'
950
951 # Save routers and Hosts
952 hostsToSave = []
953 routersToSave = []
954
955 for widget in self.widgetToItem:
956 name = widget[ 'text' ]
957 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
958 x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
959 if 'LegacyRouter' in tags:
960 nodeNum = self.routerOpts[name]['nodeNum']
961 nodeToSave = {'number':str(nodeNum),
962 'x':str(x1),
963 'y':str(y1),
964 'opts':self.routerOpts[name] }
965 routersToSave.append(nodeToSave)
966 elif 'Host' in tags:
967 nodeNum = self.hostOpts[name]['nodeNum']
968 nodeToSave = {'number':str(nodeNum),
969 'x':str(x1),
970 'y':str(y1),
971 'opts':self.hostOpts[name] }
972 hostsToSave.append(nodeToSave)
973 else:
974 raise Exception( "Cannot create mystery node: " + name )
975 savingDictionary['hosts'] = hostsToSave
976 savingDictionary['routers'] = routersToSave
977
978 # Save Links
979 linksToSave = []
980 for link in self.links.values():
981 src = link['src']
982 dst = link['dest']
983 linkopts = link['linkOpts']
984
985 srcName, dstName = src[ 'text' ], dst[ 'text' ]
986 linkToSave = {'src':srcName,
987 'dest':dstName,
988 'opts':linkopts}
989 if link['type'] == 'data':
990 linksToSave.append(linkToSave)
991 savingDictionary['links'] = linksToSave
992
993 # Save Application preferences
994 #savingDictionary['application'] = self.appPrefs
995
996 try:
997 f = open(fileName, 'wb')
998 f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
999 except Exception as er:
1000 print er
1001 finally:
1002 f.close()
1003
1004 # Generic canvas handler
1005 #
1006 # We could have used bindtags, as in nodeIcon, but
1007 # the dynamic approach used here
1008 # may actually require less code. In any case, it's an
1009 # interesting introspection-based alternative to bindtags.
1010
1011 def canvasHandle( self, eventName, event ):
1012 "Generic canvas event handler"
1013 if self.active is None:
1014 return
1015 toolName = self.active
1016 handler = getattr( self, eventName + toolName, None )
1017 if handler is not None:
1018 handler( event )
1019
1020 def clickCanvas( self, event ):
1021 "Canvas click handler."
1022 self.canvasHandle( 'click', event )
1023
1024 def dragCanvas( self, event ):
1025 "Canvas drag handler."
1026 self.canvasHandle( 'drag', event )
1027
1028 def releaseCanvas( self, event ):
1029 "Canvas mouse up handler."
1030 self.canvasHandle( 'release', event )
1031
1032 # Currently the only items we can select directly are
1033 # links. Nodes are handled by bindings in the node icon.
1034
1035 def findItem( self, x, y ):
1036 "Find items at a location in our canvas."
1037 items = self.canvas.find_overlapping( x, y, x, y )
1038 if len( items ) == 0:
1039 return None
1040 else:
1041 return items[ 0 ]
1042
1043 # Canvas bindings for Select, Host, Router and Link tools
1044
1045 def clickSelect( self, event ):
1046 "Select an item."
1047 self.selectItem( self.findItem( event.x, event.y ) )
1048
1049 def deleteItem( self, item ):
1050 "Delete an item."
1051 # Don't delete while network is running
1052 if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
1053 return
1054 # Delete from model
1055 if item in self.links:
1056 self.deleteLink( item )
1057 if item in self.itemToWidget:
1058 self.deleteNode( item )
1059 # Delete from view
1060 self.canvas.delete( item )
1061
1062 def deleteSelection( self, _event ):
1063 "Delete the selected item."
1064 if self.selection is not None:
1065 self.deleteItem( self.selection )
1066 self.selectItem( None )
1067
1068 def clearPopups(self):
1069 print 'Entrou funcao clear_popups'
ashu7b6ba182015-04-17 15:02:37 -05001070 if isHostPopup == True:
1071 print 'Hostpopup = true'
1072 self.hostPopup.unpost
1073 isHostPopup = False
ashu01b62f72015-03-12 15:16:11 -05001074 #if isRouterPopup == True
1075 #if isLinkPopup == True
1076
1077 def nodeIcon( self, node, name ):
1078 "Create a new node icon."
1079 icon = Button( self.canvas, image=self.images[ node ],
1080 text=name, compound='top' )
1081 # Unfortunately bindtags wants a tuple
1082 bindtags = [ str( self.nodeBindings ) ]
1083 bindtags += list( icon.bindtags() )
1084 icon.bindtags( tuple( bindtags ) )
1085 return icon
1086
1087 def newNode( self, node, event ):
1088 "Add a new node to our canvas."
1089 c = self.canvas
1090 x, y = c.canvasx( event.x ), c.canvasy( event.y )
1091 name = self.nodePrefixes[ node ]
1092
1093 if 'LegacyRouter' == node:
1094 self.routerCount += 1
1095 name = self.nodePrefixes[ node ] + str( self.routerCount )
1096 self.routerOpts[name] = {}
1097 self.routerOpts[name]['nodeNum']=self.routerCount
1098 self.routerOpts[name]['hostname']=name
1099 self.routerOpts[name]['nodeType']='legacyRouter'
1100
1101 if 'Host' == node:
1102 self.hostCount += 1
1103 name = self.nodePrefixes[ node ] + str( self.hostCount )
1104 self.hostOpts[name] = {'sched':'host'}
1105 self.hostOpts[name]['nodeNum']=self.hostCount
1106 self.hostOpts[name]['hostname']=name
1107
1108 icon = self.nodeIcon( node, name )
1109 item = self.canvas.create_window( x, y, anchor='c', window=icon,
1110 tags=node )
1111 self.widgetToItem[ icon ] = item
1112 self.itemToWidget[ item ] = icon
1113 self.selectItem( item )
1114 icon.links = {}
1115 if 'LegacyRouter' == node:
1116 icon.bind('<Button-3>', self.do_legacyRouterPopup )
1117 if 'Host' == node:
1118 icon.bind('<Button-3>', self.do_hostPopup )
1119
1120 def clickHost( self, event ):
1121 "Add a new host to our canvas."
1122 self.newNode( 'Host', event )
1123
1124 def clickLegacyRouter( self, event ):
1125 "Add a new router to our canvas."
1126 self.newNode( 'LegacyRouter', event )
1127
1128 def dragNetLink( self, event ):
1129 "Drag a link's endpoint to another node."
1130 if self.link is None:
1131 return
1132 # Since drag starts in widget, we use root coords
1133 x = self.canvasx( event.x_root )
1134 y = self.canvasy( event.y_root )
1135 c = self.canvas
1136 c.coords( self.link, self.linkx, self.linky, x, y )
1137
1138 def releaseNetLink( self, _event ):
1139 "Give up on the current link."
1140 if self.link is not None:
1141 self.canvas.delete( self.link )
1142 self.linkWidget = self.linkItem = self.link = None
1143
1144 # Generic node handlers
1145
1146 def createNodeBindings( self ):
1147 "Create a set of bindings for nodes."
1148 bindings = {
1149 '<ButtonPress-1>': self.clickNode,
1150 '<B1-Motion>': self.dragNode,
1151 '<ButtonRelease-1>': self.releaseNode,
1152 '<Enter>': self.enterNode,
1153 '<Leave>': self.leaveNode
1154 }
1155 l = Label() # lightweight-ish owner for bindings
1156 for event, binding in bindings.items():
1157 l.bind( event, binding )
1158 return l
1159
1160 def selectItem( self, item ):
1161 "Select an item and remember old selection."
1162 self.lastSelection = self.selection
1163 self.selection = item
1164
1165 def enterNode( self, event ):
1166 "Select node on entry."
1167 self.selectNode( event )
1168
1169 def leaveNode( self, _event ):
1170 "Restore old selection on exit."
1171 self.selectItem( self.lastSelection )
1172
1173 def clickNode( self, event ):
1174 "Node click handler."
1175 if self.active is 'NetLink':
1176 self.startLink( event )
1177 else:
1178 self.selectNode( event )
1179 return 'break'
1180
1181 def dragNode( self, event ):
1182 "Node drag handler."
1183 if self.active is 'NetLink':
1184 self.dragNetLink( event )
1185 else:
1186 self.dragNodeAround( event )
1187
1188 def releaseNode( self, event ):
1189 "Node release handler."
1190 if self.active is 'NetLink':
1191 self.finishLink( event )
1192
1193 # Specific node handlers
1194
1195 def selectNode( self, event ):
1196 "Select the node that was clicked on."
1197 item = self.widgetToItem.get( event.widget, None )
1198 self.selectItem( item )
1199
1200 def dragNodeAround( self, event ):
1201 "Drag a node around on the canvas."
1202 c = self.canvas
1203 # Convert global to local coordinates;
1204 # Necessary since x, y are widget-relative
1205 x = self.canvasx( event.x_root )
1206 y = self.canvasy( event.y_root )
1207 w = event.widget
1208 # Adjust node position
1209 item = self.widgetToItem[ w ]
1210 c.coords( item, x, y )
1211 # Adjust link positions
1212 for dest in w.links:
1213 link = w.links[ dest ]
1214 item = self.widgetToItem[ dest ]
1215 x1, y1 = c.coords( item )
1216 c.coords( link, x, y, x1, y1 )
1217 self.updateScrollRegion()
1218
1219 def createDataLinkBindings( self ):
1220 "Create a set of bindings for nodes."
1221 # Link bindings
1222 # Selection still needs a bit of work overall
1223 # Callbacks ignore event
1224
1225 def select( _event, link=self.link ):
1226 "Select item on mouse entry."
1227 self.selectItem( link )
1228
1229 def highlight( _event, link=self.link ):
1230 "Highlight item on mouse entry."
1231 self.selectItem( link )
1232 self.canvas.itemconfig( link, fill='green' )
1233
1234 def unhighlight( _event, link=self.link ):
1235 "Unhighlight item on mouse exit."
1236 self.canvas.itemconfig( link, fill='blue' )
1237 #self.selectItem( None )
1238
1239 self.canvas.tag_bind( self.link, '<Enter>', highlight )
1240 self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
1241 self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
1242 self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
1243
1244 def startLink( self, event ):
1245 "Start a new link."
1246 if event.widget not in self.widgetToItem:
1247 # Didn't click on a node
1248 return
1249
1250 w = event.widget
1251 item = self.widgetToItem[ w ]
1252 x, y = self.canvas.coords( item )
1253 self.link = self.canvas.create_line( x, y, x, y, width=4,
1254 fill='blue', tag='link' )
1255 self.linkx, self.linky = x, y
1256 self.linkWidget = w
1257 self.linkItem = item
1258
1259 def finishLink( self, event ):
1260 "Finish creating a link"
1261 if self.link is None:
1262 return
1263 source = self.linkWidget
1264 c = self.canvas
1265 # Since we dragged from the widget, use root coords
1266 x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
1267 target = self.findItem( x, y )
1268 dest = self.itemToWidget.get( target, None )
1269 if ( source is None or dest is None or source == dest
1270 or dest in source.links or source in dest.links ):
1271 self.releaseNetLink( event )
1272 return
1273 # For now, don't allow hosts to be directly linked
1274 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1275 dtags = self.canvas.gettags( target )
ashu7b6ba182015-04-17 15:02:37 -05001276 #if (('Host' in stags and 'Host' in dtags)):
1277 #self.releaseNetLink( event )
1278 #return
ashu01b62f72015-03-12 15:16:11 -05001279
1280 # Set link type
1281 linkType='data'
1282
1283 self.createDataLinkBindings()
1284 c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
1285
1286 x, y = c.coords( target )
1287 c.coords( self.link, self.linkx, self.linky, x, y )
1288 self.addLink( source, dest, linktype=linkType )
1289
1290 # We're done
1291 self.link = self.linkWidget = None
1292
1293 # Menu handlers
1294
1295 def about( self ):
1296 "Display about box."
1297 about = self.aboutBox
1298 if about is None:
1299 bg = 'white'
1300 about = Toplevel( bg='white' )
1301 about.title( 'About' )
ashu7b6ba182015-04-17 15:02:37 -05001302 info = self.appName + ': a simple network editor for MiniNDN - based on Miniedit'
ashu01b62f72015-03-12 15:16:11 -05001303 warning = 'Development version - not entirely functional!'
1304 #version = 'MiniEdit '+MINIEDIT_VERSION
ashu7b6ba182015-04-17 15:02:37 -05001305 author = 'Vince Lehman, Jan 2015'
1306 author2 = 'Ashlesh Gawande, Jan 2015'
1307 author3 = 'Carlos Cabral, Jan 2013'
1308 author4 = 'Caio Elias, Nov 2014'
1309 author5 = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
ashu01b62f72015-03-12 15:16:11 -05001310 enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
1311 www = 'http://gregorygee.wordpress.com/category/miniedit/'
1312 line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
1313 line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
1314 line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
1315 line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
1316 line5 = Label( about, text=author3, font='Helvetica 9', bg=bg )
ashu7b6ba182015-04-17 15:02:37 -05001317 line6 = Label( about, text=author4, font='Helvetica 9', bg=bg )
1318 line7 = Label( about, text=author5, font='Helvetica 9', bg=bg )
1319 line8 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
1320 line9 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
ashu01b62f72015-03-12 15:16:11 -05001321
ashu7b6ba182015-04-17 15:02:37 -05001322
1323 line9.insert(0, www)
1324 line9.configure(state='readonly')
ashu01b62f72015-03-12 15:16:11 -05001325 line1.pack( padx=20, pady=10 )
1326 line2.pack(pady=10 )
1327 line3.pack(pady=10 )
1328 line4.pack(pady=10 )
1329 line5.pack(pady=10 )
1330 line6.pack(pady=10 )
1331 line7.pack(pady=10 )
ashu7b6ba182015-04-17 15:02:37 -05001332 line8.pack(pady=10 )
1333 line9.pack(pady=10 )
ashu01b62f72015-03-12 15:16:11 -05001334 hide = ( lambda about=about: about.withdraw() )
1335 self.aboutBox = about
1336 # Hide on close rather than destroying window
1337 Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
1338 # Show (existing) window
1339 about.deiconify()
1340
1341 def createToolImages( self ):
1342 "Create toolbar (and icon) images."
1343
1344 def hostDetails( self, _ignore=None ):
1345 if ( self.selection is None or
1346 self.net is not None or
1347 self.selection not in self.itemToWidget ):
1348 return
1349 widget = self.itemToWidget[ self.selection ]
1350 name = widget[ 'text' ]
1351 tags = self.canvas.gettags( self.selection )
1352
1353 #print tags
1354 if 'Host' in tags:
1355
1356 prefDefaults = self.hostOpts[name]
1357 hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults, isRouter='False')
1358 self.master.wait_window(hostBox.top)
1359 if hostBox.result:
1360 newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
1361
1362 if len(hostBox.result['startCommand']) > 0:
1363 newHostOpts['startCommand'] = hostBox.result['startCommand']
1364 if hostBox.result['cpu']:
1365 newHostOpts['cpu'] = hostBox.result['cpu']
1366 if hostBox.result['mem']:
1367 newHostOpts['mem'] = hostBox.result['mem']
1368 if len(hostBox.result['hostname']) > 0:
1369 newHostOpts['hostname'] = hostBox.result['hostname']
1370 name = hostBox.result['hostname']
1371 widget[ 'text' ] = name
1372 if len(hostBox.result['cache']) > 0:
1373 newHostOpts['cache'] = hostBox.result['cache']
1374 if len(hostBox.result['fibEntries']) > 0:
1375 newHostOpts['fibEntries'] = hostBox.result['fibEntries']
ashu7b6ba182015-04-17 15:02:37 -05001376
1377 newHostOpts['nlsr'] = hostBox.nlsrFrame.getValues()
Ashlesh Gawande3a4afb12015-07-09 09:23:30 -05001378 newHostOpts['nfd'] = hostBox.nfdFrame.getValues()
ashu7b6ba182015-04-17 15:02:37 -05001379
ashu01b62f72015-03-12 15:16:11 -05001380 self.hostOpts[name] = newHostOpts
1381
1382 print 'New host details for ' + name + ' = ' + str(newHostOpts)
1383
1384 elif 'LegacyRouter' in tags:
1385
1386 prefDefaults = self.routerOpts[name]
1387 hostBox = HostDialog(self, title='Router Details', prefDefaults=prefDefaults, isRouter='True')
1388 self.master.wait_window(hostBox.top)
1389 if hostBox.result:
1390 newRouterOpts = {'nodeNum':self.routerOpts[name]['nodeNum']}
1391
1392 if hostBox.result['cpu']:
1393 newRouterOpts['cpu'] = hostBox.result['cpu']
1394 if hostBox.result['mem']:
1395 newRouterOpts['mem'] = hostBox.result['mem']
1396 if len(hostBox.result['hostname']) > 0:
1397 newRouterOpts['hostname'] = hostBox.result['hostname']
1398 name = hostBox.result['hostname']
1399 widget[ 'text' ] = name
1400 if len(hostBox.result['cache']) > 0:
1401 newRouterOpts['cache'] = hostBox.result['cache']
1402 if len(hostBox.result['fibEntries']) > 0:
1403 newRouterOpts['fibEntries'] = hostBox.result['fibEntries']
1404 self.routerOpts[name] = newRouterOpts
1405
1406 print 'New host details for ' + name + ' = ' + str(newRouterOpts)
1407
1408 def linkDetails( self, _ignore=None ):
1409 if ( self.selection is None or
1410 self.net is not None):
1411 return
1412 link = self.selection
1413
1414 linkDetail = self.links[link]
1415 src = linkDetail['src']
1416 dest = linkDetail['dest']
1417 linkopts = linkDetail['linkOpts']
1418 linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
1419 if linkBox.result is not None:
1420 linkDetail['linkOpts'] = linkBox.result
1421 print 'New link details = ' + str(linkBox.result)
1422
1423 # Model interface
1424 #
1425 # Ultimately we will either want to use a topo or
1426 # mininet object here, probably.
1427
1428 def addLink( self, source, dest, linktype='data', linkopts={} ):
1429 "Add link to model."
1430 source.links[ dest ] = self.link
1431 dest.links[ source ] = self.link
1432 self.links[ self.link ] = {'type' :linktype,
1433 'src':source,
1434 'dest':dest,
1435 'linkOpts':linkopts}
1436
1437 def deleteLink( self, link ):
1438 "Delete link from model."
1439 pair = self.links.get( link, None )
1440 if pair is not None:
1441 source=pair['src']
1442 dest=pair['dest']
1443 del source.links[ dest ]
1444 del dest.links[ source ]
1445 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1446 dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
1447 ltags = self.canvas.gettags( link )
1448
1449 if link is not None:
1450 del self.links[ link ]
1451
1452 def deleteNode( self, item ):
1453 "Delete node (and its links) from model."
1454
1455 widget = self.itemToWidget[ item ]
1456 tags = self.canvas.gettags(item)
1457
1458 for link in widget.links.values():
1459 # Delete from view and model
1460 self.deleteItem( link )
1461 del self.itemToWidget[ item ]
1462 del self.widgetToItem[ widget ]
1463
1464 def do_linkPopup(self, event):
1465 # display the popup menu
1466 if ( self.net is None ):
1467 try:
1468 self.linkPopup.tk_popup(event.x_root, event.y_root)
1469 finally:
1470 # make sure to release the grab (Tk 8.0a1 only)
1471 self.linkPopup.grab_release()
1472
1473 def do_legacyRouterPopup(self, event):
1474 # display the popup menu
1475 if ( self.net is None ):
1476 try:
1477 self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
1478 finally:
1479 # make sure to release the grab (Tk 8.0a1 only)
1480 self.legacyRouterPopup.grab_release()
1481
1482 def do_hostPopup(self, event):
1483 # display the popup menu
1484 if ( self.net is None ):
1485 try:
1486 self.hostPopup.tk_popup(event.x_root, event.y_root)
1487 isHostPopup = True
1488 finally:
1489 # make sure to release the grab (Tk 8.0a1 only)
1490 self.hostPopup.grab_release()
1491
1492 def xterm( self, _ignore=None ):
1493 "Make an xterm when a button is pressed."
1494 if ( self.selection is None or
1495 self.net is None or
1496 self.selection not in self.itemToWidget ):
1497 return
1498 name = self.itemToWidget[ self.selection ][ 'text' ]
1499 if name not in self.net.nameToNode:
1500 return
1501 term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
1502 if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
1503 self.net.terms += term
1504 else:
1505 self.net.terms.append(term)
1506
1507 def iperf( self, _ignore=None ):
1508 "Make an xterm when a button is pressed."
1509 if ( self.selection is None or
1510 self.net is None or
1511 self.selection not in self.itemToWidget ):
1512 return
1513 name = self.itemToWidget[ self.selection ][ 'text' ]
1514 if name not in self.net.nameToNode:
1515 return
1516 self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
1517
1518 """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
1519
1520 def parseArgs( self ):
1521 """Parse command-line args and return options object.
1522 returns: opts parse options dict"""
1523
1524 if '--custom' in sys.argv:
1525 index = sys.argv.index( '--custom' )
1526 if len( sys.argv ) > index + 1:
1527 filename = sys.argv[ index + 1 ]
1528 self.parseCustomFile( filename )
1529 else:
1530 raise Exception( 'Custom file name not found' )
1531
ashu7b6ba182015-04-17 15:02:37 -05001532 desc = ( "The %prog utility creates Minindn network from the\n"
ashu01b62f72015-03-12 15:16:11 -05001533 "command line. It can create parametrized topologies,\n"
ashu7b6ba182015-04-17 15:02:37 -05001534 "invoke the Minindn CLI, and run tests." )
ashu01b62f72015-03-12 15:16:11 -05001535
1536 usage = ( '%prog [options] [template_file]\n'
ashu7b6ba182015-04-17 15:02:37 -05001537 '\nIf no template_file is given, generated template will be written to the file minindn.conf in the current directory.\n'
ashu01b62f72015-03-12 15:16:11 -05001538 'Type %prog -h for details)' )
1539
1540 opts = OptionParser( description=desc, usage=usage )
1541
1542 addDictOption( opts, TOPOS, TOPODEF, 'topo' )
1543 addDictOption( opts, LINKS, LINKDEF, 'link' )
1544
1545 opts.add_option( '--custom', type='string', default=None,
1546 help='read custom topo and node params from .py' +
1547 'file' )
1548
1549 self.options, self.args = opts.parse_args()
1550 # We don't accept extra arguments after the options
1551 if self.args:
1552 if len(self.args) > 1:
1553 opts.print_help()
1554 exit()
1555 else:
1556 self.template_file=self.args[0]
1557
1558 def setCustom( self, name, value ):
1559 "Set custom parameters for MininetRunner."
1560 if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
1561 # Update dictionaries
1562 param = name.upper()
1563 globals()[ param ].update( value )
1564 elif name == 'validate':
1565 # Add custom validate function
1566 self.validate = value
1567 else:
1568 # Add or modify global variable or class
1569 globals()[ name ] = value
1570
1571 def parseCustomFile( self, fileName ):
1572 "Parse custom file and add params before parsing cmd-line options."
1573 customs = {}
1574 if os.path.isfile( fileName ):
1575 execfile( fileName, customs, customs )
1576 for name, val in customs.iteritems():
1577 self.setCustom( name, val )
1578 else:
1579 raise Exception( 'could not find custom file: %s' % fileName )
1580
1581 def importTopo( self ):
1582 print 'topo='+self.options.topo
1583 if self.options.topo == 'none':
1584 return
1585 self.newTopology()
1586 topo = buildTopo( TOPOS, self.options.topo )
1587 link = customConstructor( LINKS, self.options.link )
1588 importNet = Mininet(topo=topo, build=False, link=link)
1589 importNet.build()
1590
1591 c = self.canvas
1592 rowIncrement = 100
1593 currentY = 100
1594
1595 # Add switches
1596 print 'switches:'+str(len(importNet.switches))
1597 columnCount = 0
1598 for switch in importNet.switches:
1599 name = switch.name
1600 self.switchOpts[name] = {}
1601 self.switchOpts[name]['nodeNum']=self.switchCount
1602 self.switchOpts[name]['hostname']=name
1603 self.switchOpts[name]['switchType']='default'
1604 self.switchOpts[name]['controllers']=[]
1605
1606 x = columnCount*100+100
1607 self.addNode('Switch', self.switchCount,
1608 float(x), float(currentY), name=name)
1609 icon = self.findWidgetByName(name)
1610 icon.bind('<Button-3>', self.do_switchPopup )
1611
1612 if columnCount == 9:
1613 columnCount = 0
1614 currentY = currentY + rowIncrement
1615 else:
1616 columnCount =columnCount+1
1617
1618 currentY = currentY + rowIncrement
1619 # Add hosts
1620 print 'hosts:'+str(len(importNet.hosts))
1621 columnCount = 0
1622 for host in importNet.hosts:
1623 name = host.name
1624 self.hostOpts[name] = {'sched':'host'}
1625 self.hostOpts[name]['nodeNum']=self.hostCount
1626 self.hostOpts[name]['hostname']=name
1627 #self.hostOpts[name]['ip']=host.IP()
1628
1629 x = columnCount*100+100
1630 self.addNode('Host', self.hostCount,
1631 float(x), float(currentY), name=name)
1632 icon = self.findWidgetByName(name)
1633 icon.bind('<Button-3>', self.do_hostPopup )
1634 if columnCount == 9:
1635 columnCount = 0
1636 currentY = currentY + rowIncrement
1637 else:
1638 columnCount =columnCount+1
1639
1640 print 'links:'+str(len(topo.links()))
1641 #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
1642 for link in topo.links():
1643 print str(link)
1644 srcNode = link[0]
1645 src = self.findWidgetByName(srcNode)
1646 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
1647
1648 destNode = link[1]
1649 dest = self.findWidgetByName(destNode)
1650 dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
1651
1652 params = topo.linkInfo( srcNode, destNode )
1653 print 'Link Parameters='+str(params)
1654
1655 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
1656 fill='blue', tag='link' )
1657 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
1658 self.addLink( src, dest, linkopts=params )
1659 self.createDataLinkBindings()
1660 self.link = self.linkWidget = None
1661
1662 importNet.stop()
1663
1664def miniEditImages():
1665 "Create and return images for MiniEdit."
1666
1667 # Image data. Git will be unhappy. However, the alternative
1668 # is to keep track of separate binary files, which is also
1669 # unappealing.
1670
1671 return {
1672 'Select': BitmapImage(
1673 file='/usr/include/X11/bitmaps/left_ptr' ),
1674
1675 'LegacyRouter': PhotoImage( data=r"""
1676 R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+
1677 3QFq1DmL3wJMmAMzZZW11dnZ2SFrtyNdmTSO6gIZMUKa8gJVqEOH
1678 zR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq6ymF
1679 4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9
1680 vwNgvwJZsX+69gsXJQFHjTtjizF0tvHx8VOm9z2V736Dhz2N3QM2
1681 acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtgtktjfQFu
1682 3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh3
1683 12Gt+VGm/AQIDTmByAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i5
1684 6gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia7DpeggFt2QNP
1685 m97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er
1686 /yVVhwJJktPh70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVV
1687 hQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18zKfP9wwcLAMHCwFF
1688 iS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh
1689 7cve8pG/7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR
1690 1RMjNTF3vU2X4TZupwRSolNne4nB+T+L2YGz4zJ/zYe99YGHjRdD
1691 cT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6Wl
1692 pW2t7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90
1693 uvPz8wIVKBp42SV5zbfT7wtXpStVfwFWrBVvyTt3swFz5kGBv2+1
1694 /QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
1695 u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T
1696 5yH5BAEAAAAALAAAAAAyABgABwj/AAEIHEiQYJY7Qwg9UsTplRIb
1697 ENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4cHeoI
1698 abJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPA
1699 TqEBoRB9gVJsxRlhPwHI0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o
1700 9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkSrtwADuxC
1701 G/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmD
1702 jhTMmseoKQIFDx7RoxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7
1703 VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq9dGvv09RHFhc
1704 IUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p
1705 9HEUFhxgMSAvjbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJ
1706 ifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CFoVggmEgCyRf01WcF
1707 CYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57Ag
1708 yZckpKKPGFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemM
1709 IQggeaJywSQ/wgHOAmJskQEfWqBlFBEH1P/QaGY3QOpDZXA2+A6m
1710 7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlB
1711 pZdiisd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtCh
1712 RmVPNWgpr+Be+Nc9icARww9TkIEuDAsQ0O7DzGIQzD2QdDEJHTsI
1713 AROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
1714 xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE
1715 42Q9jtFIp8z0Dy1jQMA1AGziz9VoW7310V0znYDTGMQgwUDXLDBO
1716 2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOMLQkc
1717 jvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZ
1718 MDHKCTwI8EcQFHBBAAFcgGPLHwLwcMIo12Qxu0ABAQA7
1719 """),
1720
1721 'Host': PhotoImage( data=r"""
1722 R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
1723 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
1724 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
1725 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
1726 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
1727 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
1728 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
1729 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
1730 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
1731 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
1732 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
1733 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
1734 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
1735 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
1736 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
1737 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
1738 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
1739 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
1740 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
1741 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
1742 ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
1743 BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
1744 HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
1745 p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
1746 C8cSBBAQADs=
1747 """ ),
1748
1749 'NetLink': PhotoImage( data=r"""
1750 R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
1751 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
1752 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
1753 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
1754 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
1755 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
1756 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
1757 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
1758 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
1759 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
1760 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
1761 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
1762 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
1763 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
1764 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
1765 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
1766 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
1767 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
1768 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
1769 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
1770 ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
1771 Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
1772 lBmxI8mSNknm1Dnx5sCAADs=
1773 """ )
1774 }
1775
1776def addDictOption( opts, choicesDict, default, name, helpStr=None ):
1777 """Convenience function to add choices dicts to OptionParser.
1778 opts: OptionParser instance
1779 choicesDict: dictionary of valid choices, must include default
1780 default: default choice key
1781 name: long option name
1782 help: string"""
1783 if default not in choicesDict:
1784 raise Exception( 'Invalid default %s for choices dict: %s' %
1785 ( default, name ) )
1786 if not helpStr:
1787 helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
1788 '[,param=value...]' )
1789 opts.add_option( '--' + name,
1790 type='string',
1791 default = default,
1792 help = helpStr )
1793
1794if __name__ == '__main__':
1795 setLogLevel( 'info' )
1796 app = MiniEdit()
1797 """ import topology if specified """
1798 app.parseArgs()
1799 app.importTopo()
1800
1801 global isHostPopup
1802 global isRouterPopup
1803 global isLinkPopup
1804
1805 app.mainloop()