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