blob: 2561ad1dc2425407027f44cc73937531854d8e86 [file] [log] [blame]
carlosmscabralf40ecd12013-02-01 18:15:58 -02001#!/usr/bin/python
2
3"""
Caio Eliase904f532014-12-01 16:19:22 -02004MiniCCNxEdit: a simple network editor for MiniCCNx
carlosmscabralf40ecd12013-02-01 18:15:58 -02005
Caio Elias2fa082d2014-12-01 16:38:04 -02006Based on miniedit by:
Caio Elias47b243c2014-11-23 19:25:34 -02007Bob Lantz, April 2010
8Gregory Gee, July 2013
carlosmscabralf40ecd12013-02-01 18:15:58 -02009
Caio Elias2fa082d2014-12-01 16:38:04 -020010Carlos Cabral, Jan 2013
11Caio Elias, Nov 2014
12
carlosmscabralf40ecd12013-02-01 18:15:58 -020013"""
carlosmscabralf40ecd12013-02-01 18:15:58 -020014
Caio Elias47b243c2014-11-23 19:25:34 -020015MINIEDIT_VERSION = '2.2.0.1'
16
17from optparse import OptionParser
18from Tkinter import *
19from ttk import Notebook
20from tkMessageBox import showinfo, showerror, showwarning
21from subprocess import call
22import tkFont
23import csv
24import tkFileDialog
25import tkSimpleDialog
26import re
27import json
28from distutils.version import StrictVersion
29import os
30import sys
31from functools import partial
32
Caio24c0cbd2015-01-19 18:45:53 -020033import pdb
34
Caio Elias47b243c2014-11-23 19:25:34 -020035if 'PYTHONPATH' in os.environ:
36 sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
carlosmscabralf40ecd12013-02-01 18:15:58 -020037
38# someday: from ttk import *
39
Caio Elias47b243c2014-11-23 19:25:34 -020040from 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
carlosmscabralf40ecd12013-02-01 18:15:58 -020045from mininet.term import makeTerm, cleanUpScreens
Caio Elias47b243c2014-11-23 19:25:34 -020046from 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
Caio Eliase904f532014-12-01 16:19:22 -020055print 'MiniCCNxEdit running...' #+VERSION
Caio Elias47b243c2014-11-23 19:25:34 -020056MININET_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)
Caio24c0cbd2015-01-19 18:45:53 -0200103 okButton.grid(row=1, column=0, sticky=E)
Caio Elias47b243c2014-11-23 19:25:34 -0200104
105 canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
106 bd=4, command=self.cancelAction)
Caio24c0cbd2015-01-19 18:45:53 -0200107 canlceButton.grid(row=1, column=1, sticky=W)
Caio Elias47b243c2014-11-23 19:25:34 -0200108
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
Caio24c0cbd2015-01-19 18:45:53 -0200124 def __init__(self, master, title, prefDefaults, isRouter):
Caio Elias47b243c2014-11-23 19:25:34 -0200125
126 self.prefValues = prefDefaults
127 self.result = None
Caio24c0cbd2015-01-19 18:45:53 -0200128 self.isRouter = isRouter
129 self.title = title
Caio Elias47b243c2014-11-23 19:25:34 -0200130
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)
Caio24c0cbd2015-01-19 18:45:53 -0200137 self.fibFrame = Frame(n)
Caio Elias47b243c2014-11-23 19:25:34 -0200138 n.add(self.propFrame, text='Properties')
Caio24c0cbd2015-01-19 18:45:53 -0200139 n.add(self.fibFrame, text='FIB Entries')
Caio Elias47b243c2014-11-23 19:25:34 -0200140 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
Caio Elias47b243c2014-11-23 19:25:34 -0200150 # Field for CPU
Caio24c0cbd2015-01-19 18:45:53 -0200151 Label(self.propFrame, text="Amount CPU:").grid(row=2, sticky=E)
Caio Elias47b243c2014-11-23 19:25:34 -0200152 self.cpuEntry = Entry(self.propFrame)
Caio24c0cbd2015-01-19 18:45:53 -0200153 self.cpuEntry.grid(row=2, column=1)
154 Label(self.propFrame, text="%").grid(row=2, column=2, sticky=W)
Caio Elias47b243c2014-11-23 19:25:34 -0200155 if 'cpu' in self.prefValues:
156 self.cpuEntry.insert(0, str(self.prefValues['cpu']))
Caio24c0cbd2015-01-19 18:45:53 -0200157
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']))
Caio Elias47b243c2014-11-23 19:25:34 -0200173
174 # Start command
Caio24c0cbd2015-01-19 18:45:53 -0200175 #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)
Caio Elias47b243c2014-11-23 19:25:34 -0200185
186 ### TAB 2
Caio24c0cbd2015-01-19 18:45:53 -0200187 # 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)
Caio Elias47b243c2014-11-23 19:25:34 -0200192
Caio24c0cbd2015-01-19 18:45:53 -0200193 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)
Caio Elias47b243c2014-11-23 19:25:34 -0200197
Caio24c0cbd2015-01-19 18:45:53 -0200198 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)
Caio Elias47b243c2014-11-23 19:25:34 -0200204 else:
Caio24c0cbd2015-01-19 18:45:53 -0200205 self.fibTableFrame.addRow(value=[fibEntr,''])
carlosmscabralf40ecd12013-02-01 18:15:58 -0200206
Caio24c0cbd2015-01-19 18:45:53 -0200207 def addEntry( self ):
208 self.fibTableFrame.addRow()
Caio Elias47b243c2014-11-23 19:25:34 -0200209
210 def apply(self):
Caio24c0cbd2015-01-19 18:45:53 -0200211 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)))
Caio Elias47b243c2014-11-23 19:25:34 -0200216 else:
Caio24c0cbd2015-01-19 18:45:53 -0200217 fibEntries.append(self.fibTableFrame.get(row, 0))
Caio Elias47b243c2014-11-23 19:25:34 -0200218
219 results = {'cpu': self.cpuEntry.get(),
Caio24c0cbd2015-01-19 18:45:53 -0200220 'cache': self.cacheEntry.get(),
221 'mem': self.memEntry.get(),
Caio Elias47b243c2014-11-23 19:25:34 -0200222 'hostname':self.hostnameEntry.get(),
Caio Elias47b243c2014-11-23 19:25:34 -0200223 'startCommand':self.startEntry.get(),
Caio24c0cbd2015-01-19 18:45:53 -0200224 'fibEntries':fibEntries}
Caio Elias47b243c2014-11-23 19:25:34 -0200225 self.result = results
226
Caio Elias47b243c2014-11-23 19:25:34 -0200227class 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
carlosmscabralf40ecd12013-02-01 18:15:58 -0200233
Caio Elias47b243c2014-11-23 19:25:34 -0200234 """
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)
carlosmscabralf40ecd12013-02-01 18:15:58 -0200237
Caio Elias47b243c2014-11-23 19:25:34 -0200238 # 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)
Caio24c0cbd2015-01-19 18:45:53 -0200326 Label(master, text="[1-1000] Mbps").grid(row=0, column=2, sticky=W)
Caio Elias47b243c2014-11-23 19:25:34 -0200327 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)
Caio24c0cbd2015-01-19 18:45:53 -0200333 Label(master, text="[0-1000] ms").grid(row=1, column=2, sticky=W)
Caio Elias47b243c2014-11-23 19:25:34 -0200334 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
Caio Elias47b243c2014-11-23 19:25:34 -0200344 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())
Caio Elias47b243c2014-11-23 19:25:34 -0200354
Caio Elias47b243c2014-11-23 19:25:34 -0200355class 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()
carlosmscabralf40ecd12013-02-01 18:15:58 -0200391
392class MiniEdit( Frame ):
393
Caio Eliase904f532014-12-01 16:19:22 -0200394 "A simple network editor for MiniCCNx."
carlosmscabralf40ecd12013-02-01 18:15:58 -0200395
Caio Eliase904f532014-12-01 16:19:22 -0200396 def __init__( self, parent=None, cheight=600, cwidth=1000, template_file='miniccnx.conf' ):
Caio Eliase904f532014-12-01 16:19:22 -0200397
398 self.template_file = template_file
carlosmscabralf40ecd12013-02-01 18:15:58 -0200399
400 Frame.__init__( self, parent )
401 self.action = None
Caio Eliase904f532014-12-01 16:19:22 -0200402 self.appName = 'MiniccnxEdit'
Caio Elias47b243c2014-11-23 19:25:34 -0200403 self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
carlosmscabralf40ecd12013-02-01 18:15:58 -0200404
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
Caio Elias47b243c2014-11-23 19:25:34 -0200422 self.controllers = {}
423
424 # Toolbar
carlosmscabralf40ecd12013-02-01 18:15:58 -0200425 self.images = miniEditImages()
426 self.buttons = {}
427 self.active = None
Caio Elias47b243c2014-11-23 19:25:34 -0200428 self.tools = ( 'Select', 'Host', 'LegacyRouter', 'NetLink' )
429 self.customColors = { 'LegacyRouter': 'darkGreen', 'Host': 'blue' }
carlosmscabralf40ecd12013-02-01 18:15:58 -0200430 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()
Caio Elias47b243c2014-11-23 19:25:34 -0200444 self.nodePrefixes = { 'LegacyRouter': 'r', 'Host': 'h'}
carlosmscabralf40ecd12013-02-01 18:15:58 -0200445 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
Caio Elias47b243c2014-11-23 19:25:34 -0200460 #Mouse bindings
Caio Elias47b243c2014-11-23 19:25:34 -0200461 self.bind( '<Button-1>', lambda event: self.clearPopups )
462
463 self.hostPopup = Menu(self.top, tearoff=0)
Caio24c0cbd2015-01-19 18:45:53 -0200464 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 )
Caio Elias47b243c2014-11-23 19:25:34 -0200467
468 self.legacyRouterPopup = Menu(self.top, tearoff=0)
Caio24c0cbd2015-01-19 18:45:53 -0200469 self.legacyRouterPopup.add_command(label='Router Options', font=self.font, command=self.hostDetails)
Caio Elias47b243c2014-11-23 19:25:34 -0200470
471 self.linkPopup = Menu(self.top, tearoff=0)
Caio24c0cbd2015-01-19 18:45:53 -0200472 self.linkPopup.add_command(label='Link Options', font=self.font, command=self.linkDetails)
Caio Eliase904f532014-12-01 16:19:22 -0200473 #self.linkPopup.add_separator()
474 #self.linkPopup.add_command(label='Properties', font=self.font, command=self.linkDetails )
Caio Elias47b243c2014-11-23 19:25:34 -0200475
carlosmscabralf40ecd12013-02-01 18:15:58 -0200476 # Event handling initalization
477 self.linkx = self.linky = self.linkItem = None
478 self.lastSelection = None
479
480 # Model initialization
481 self.links = {}
Caio Elias47b243c2014-11-23 19:25:34 -0200482 self.hostOpts = {}
483 self.switchOpts = {}
Caio24c0cbd2015-01-19 18:45:53 -0200484 self.routerOpts = {}
Caio Elias47b243c2014-11-23 19:25:34 -0200485 self.hostCount = 0
Caio24c0cbd2015-01-19 18:45:53 -0200486 self.routerCount = 0
carlosmscabralf40ecd12013-02-01 18:15:58 -0200487 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."
Caio24c0cbd2015-01-19 18:45:53 -0200494 #sself.stop()
carlosmscabralf40ecd12013-02-01 18:15:58 -0200495 Frame.quit( self )
496
Caio Elias47b243c2014-11-23 19:25:34 -0200497 def createMenubar( self ): # MODIFICADO - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200498 "Create our menu bar."
499
500 font = self.font
501
502 mbar = Menu( self.top, font=font )
503 self.top.configure( menu=mbar )
504
Caio Eliase904f532014-12-01 16:19:22 -0200505 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 )
Caio24c0cbd2015-01-19 18:45:53 -0200510 fileMenu.add_command( label="Generate", font=font, command=self.doGenerate )
Caio Eliase904f532014-12-01 16:19:22 -0200511 fileMenu.add_separator()
512 fileMenu.add_command( label='Quit', command=self.quit, font=font )
Caio Elias47b243c2014-11-23 19:25:34 -0200513
carlosmscabralf40ecd12013-02-01 18:15:58 -0200514 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
Caio Eliase904f532014-12-01 16:19:22 -0200519 # 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
Caio Elias47b243c2014-11-23 19:25:34 -0200527 # Canvas - TUDO IGUAL - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200528
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
Caio Elias47b243c2014-11-23 19:25:34 -0200582 def activate( self, toolName ): #IGUAL - OK
carlosmscabralf40ecd12013-02-01 18:15:58 -0200583 "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
Caio Elias47b243c2014-11-23 19:25:34 -0200591
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
carlosmscabralf40ecd12013-02-01 18:15:58 -0200602 "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:
Caio Elias47b243c2014-11-23 19:25:34 -0200611 b.config( height=35, image=self.images[ tool ] )
612 self.createToolTip(b, str(tool))
carlosmscabralf40ecd12013-02-01 18:15:58 -0200613 # 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
Caio Elias47b243c2014-11-23 19:25:34 -0200621 # abaixo copiado Mini-CCNx para criar botao Generate
622
623 for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200624 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
carlosmscabralf40ecd12013-02-01 18:15:58 -0200629 return toolbar
630
Caio Elias47b243c2014-11-23 19:25:34 -0200631 def doGenerate( self ): #COPIA Mini-CCNx - GERA TEMPLATE
carlosmscabralf40ecd12013-02-01 18:15:58 -0200632 "Generate template."
633 self.activate( 'Select' )
634 for tool in self.tools:
635 self.buttons[ tool ].config( state='disabled' )
636
637 self.buildTemplate()
638
Caio Elias47b243c2014-11-23 19:25:34 -0200639 for tool in self.tools:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200640 self.buttons[ tool ].config( state='normal' )
641
Caio24c0cbd2015-01-19 18:45:53 -0200642 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)
Caio Elias47b243c2014-11-23 19:25:34 -0200647
Caio24c0cbd2015-01-19 18:45:53 -0200648 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
carlosmscabralf40ecd12013-02-01 18:15:58 -0200657
Caio Elias47b243c2014-11-23 19:25:34 -0200658 def buildTemplate( self ): #COPIA Mini-CCNx para criar Template
659 "Generate template"
carlosmscabralf40ecd12013-02-01 18:15:58 -0200660
Caio Elias47b243c2014-11-23 19:25:34 -0200661 template = open(self.template_file, 'w')
662
663 # hosts
664 template.write('[hosts]\n')
665 for widget in self.widgetToItem:
carlosmscabralf40ecd12013-02-01 18:15:58 -0200666 name = widget[ 'text' ]
667 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
Caio24c0cbd2015-01-19 18:45:53 -0200668 #print self.hostOpts[name]
Caio Elias47b243c2014-11-23 19:25:34 -0200669 if 'Host' in tags:
Caio24c0cbd2015-01-19 18:45:53 -0200670 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')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200688
Caio Elias47b243c2014-11-23 19:25:34 -0200689 # switches/routers
690 template.write('[routers]\n')
Caio Eliase904f532014-12-01 16:19:22 -0200691
Caio24c0cbd2015-01-19 18:45:53 -0200692 for router in self.routerOpts.values():
Caio Eliase904f532014-12-01 16:19:22 -0200693
Caio24c0cbd2015-01-19 18:45:53 -0200694 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')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200722
723 # Make links
Caio Elias47b243c2014-11-23 19:25:34 -0200724 template.write('[links]\n')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200725 for link in self.links.values():
Caio Eliase904f532014-12-01 16:19:22 -0200726 dst=link['dest']
727 src=link['src']
728 linkopts=link['linkOpts']
729 linktype=link['type']
Caio Eliase904f532014-12-01 16:19:22 -0200730
carlosmscabralf40ecd12013-02-01 18:15:58 -0200731 srcName, dstName = src[ 'text' ], dst[ 'text' ]
Caio24c0cbd2015-01-19 18:45:53 -0200732 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')
carlosmscabralf40ecd12013-02-01 18:15:58 -0200741
Caio Eliase904f532014-12-01 16:19:22 -0200742 template.close()
Caio Elias47b243c2014-11-23 19:25:34 -0200743
744 def addNode( self, node, nodeNum, x, y, name=None):
745 "Add a new node to our canvas."
Caio24c0cbd2015-01-19 18:45:53 -0200746
747 if 'LegacyRouter' == node:
748 self.routerCount += 1
Caio Elias47b243c2014-11-23 19:25:34 -0200749 if 'Host' == node:
Caio24c0cbd2015-01-19 18:45:53 -0200750 print "Host!"
Caio Elias47b243c2014-11-23 19:25:34 -0200751 self.hostCount += 1
Caio Elias47b243c2014-11-23 19:25:34 -0200752 if name is None:
753 name = self.nodePrefixes[ node ] + nodeNum
754 self.addNamedNode(node, name, x, y)
755
756 def addNamedNode( self, node, name, x, y):
757 "Add a new node to our canvas."
758 c = self.canvas
759 icon = self.nodeIcon( node, name )
760 item = self.canvas.create_window( x, y, anchor='c', window=icon,
761 tags=node )
762 self.widgetToItem[ icon ] = item
763 self.itemToWidget[ item ] = icon
764 icon.links = {}
765
766 def convertJsonUnicode(self, input):
767 "Some part of Mininet don't like Unicode"
768 if isinstance(input, dict):
769 return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
770 elif isinstance(input, list):
771 return [self.convertJsonUnicode(element) for element in input]
772 elif isinstance(input, unicode):
773 return input.encode('utf-8')
774 else:
775 return input
776
777 def loadTopology( self ):
778 "Load command."
779 c = self.canvas
780
781 myFormats = [
Caio Eliase904f532014-12-01 16:19:22 -0200782 ('Miniccnx Topology','*.mnccnx'),
Caio Elias47b243c2014-11-23 19:25:34 -0200783 ('All Files','*'),
784 ]
785 f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
786 if f == None:
787 return
788 self.newTopology()
789 loadedTopology = self.convertJsonUnicode(json.load(f))
790
Caio Elias47b243c2014-11-23 19:25:34 -0200791 # Load hosts
792 hosts = loadedTopology['hosts']
793 for host in hosts:
794 nodeNum = host['number']
795 hostname = 'h'+nodeNum
796 if 'hostname' in host['opts']:
797 hostname = host['opts']['hostname']
798 else:
799 host['opts']['hostname'] = hostname
800 if 'nodeNum' not in host['opts']:
801 host['opts']['nodeNum'] = int(nodeNum)
802 x = host['x']
803 y = host['y']
804 self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
805
806 # Fix JSON converting tuple to list when saving
807 if 'privateDirectory' in host['opts']:
808 newDirList = []
809 for privateDir in host['opts']['privateDirectory']:
810 if isinstance( privateDir, list ):
811 newDirList.append((privateDir[0],privateDir[1]))
812 else:
813 newDirList.append(privateDir)
814 host['opts']['privateDirectory'] = newDirList
815 self.hostOpts[hostname] = host['opts']
816 icon = self.findWidgetByName(hostname)
817 icon.bind('<Button-3>', self.do_hostPopup )
818
Caio24c0cbd2015-01-19 18:45:53 -0200819 # Load routers
820 routers = loadedTopology['routers']
821 for router in routers:
822 nodeNum = router['number']
823 hostname = 'r'+nodeNum
824 #print router
825 if 'nodeType' not in router['opts']:
826 router['opts']['nodeType'] = 'legacyRouter'
827 if 'hostname' in router['opts']:
828 hostname = router['opts']['hostname']
Caio Elias47b243c2014-11-23 19:25:34 -0200829 else:
Caio24c0cbd2015-01-19 18:45:53 -0200830 router['opts']['hostname'] = hostname
831 if 'nodeNum' not in router['opts']:
832 router['opts']['nodeNum'] = int(nodeNum)
833 x = router['x']
834 y = router['y']
835 if router['opts']['nodeType'] == "legacyRouter":
Caio Elias47b243c2014-11-23 19:25:34 -0200836 self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
837 icon = self.findWidgetByName(hostname)
838 icon.bind('<Button-3>', self.do_legacyRouterPopup )
Caio24c0cbd2015-01-19 18:45:53 -0200839 self.routerOpts[hostname] = router['opts']
Caio Elias47b243c2014-11-23 19:25:34 -0200840
841 # Load links
842 links = loadedTopology['links']
843 for link in links:
844 srcNode = link['src']
845 src = self.findWidgetByName(srcNode)
846 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
847
848 destNode = link['dest']
849 dest = self.findWidgetByName(destNode)
Caio24c0cbd2015-01-19 18:45:53 -0200850 dx, dy = self.canvas.coords( self.widgetToItem[ dest ] )
Caio Elias47b243c2014-11-23 19:25:34 -0200851
852 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
853 fill='blue', tag='link' )
854 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
855 self.addLink( src, dest, linkopts=link['opts'] )
856 self.createDataLinkBindings()
857 self.link = self.linkWidget = None
858
859 f.close
860
861 def findWidgetByName( self, name ):
862 for widget in self.widgetToItem:
863 if name == widget[ 'text' ]:
864 return widget
865
866 def newTopology( self ):
867 "New command."
868 for widget in self.widgetToItem.keys():
869 self.deleteItem( self.widgetToItem[ widget ] )
870 self.hostCount = 0
Caio24c0cbd2015-01-19 18:45:53 -0200871 self.routerCount = 0
Caio Elias47b243c2014-11-23 19:25:34 -0200872 self.links = {}
873 self.hostOpts = {}
Caio24c0cbd2015-01-19 18:45:53 -0200874 self.routerOpts = {}
Caio Elias47b243c2014-11-23 19:25:34 -0200875
876 def saveTopology( self ):
877 "Save command."
878 myFormats = [
Caio Eliase904f532014-12-01 16:19:22 -0200879 ('Miniccnx Topology','*.mnccnx'),
Caio Elias47b243c2014-11-23 19:25:34 -0200880 ('All Files','*'),
881 ]
882
883 savingDictionary = {}
884 fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
885 if len(fileName ) > 0:
886 # Save Application preferences
887 savingDictionary['version'] = '2'
888
Caio24c0cbd2015-01-19 18:45:53 -0200889 # Save routers and Hosts
Caio Elias47b243c2014-11-23 19:25:34 -0200890 hostsToSave = []
Caio24c0cbd2015-01-19 18:45:53 -0200891 routersToSave = []
892
Caio Elias47b243c2014-11-23 19:25:34 -0200893 for widget in self.widgetToItem:
894 name = widget[ 'text' ]
895 tags = self.canvas.gettags( self.widgetToItem[ widget ] )
896 x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
897 if 'LegacyRouter' in tags:
Caio24c0cbd2015-01-19 18:45:53 -0200898 nodeNum = self.routerOpts[name]['nodeNum']
Caio Elias47b243c2014-11-23 19:25:34 -0200899 nodeToSave = {'number':str(nodeNum),
900 'x':str(x1),
901 'y':str(y1),
Caio24c0cbd2015-01-19 18:45:53 -0200902 'opts':self.routerOpts[name] }
903 routersToSave.append(nodeToSave)
Caio Elias47b243c2014-11-23 19:25:34 -0200904 elif 'Host' in tags:
905 nodeNum = self.hostOpts[name]['nodeNum']
906 nodeToSave = {'number':str(nodeNum),
907 'x':str(x1),
908 'y':str(y1),
909 'opts':self.hostOpts[name] }
910 hostsToSave.append(nodeToSave)
911 else:
912 raise Exception( "Cannot create mystery node: " + name )
913 savingDictionary['hosts'] = hostsToSave
Caio24c0cbd2015-01-19 18:45:53 -0200914 savingDictionary['routers'] = routersToSave
Caio Elias47b243c2014-11-23 19:25:34 -0200915
916 # Save Links
917 linksToSave = []
918 for link in self.links.values():
919 src = link['src']
920 dst = link['dest']
921 linkopts = link['linkOpts']
922
923 srcName, dstName = src[ 'text' ], dst[ 'text' ]
924 linkToSave = {'src':srcName,
925 'dest':dstName,
926 'opts':linkopts}
927 if link['type'] == 'data':
928 linksToSave.append(linkToSave)
929 savingDictionary['links'] = linksToSave
930
931 # Save Application preferences
Caio24c0cbd2015-01-19 18:45:53 -0200932 #savingDictionary['application'] = self.appPrefs
Caio Elias47b243c2014-11-23 19:25:34 -0200933
934 try:
935 f = open(fileName, 'wb')
936 f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
937 except Exception as er:
938 print er
939 finally:
940 f.close()
941
carlosmscabralf40ecd12013-02-01 18:15:58 -0200942 # Generic canvas handler
943 #
944 # We could have used bindtags, as in nodeIcon, but
945 # the dynamic approach used here
946 # may actually require less code. In any case, it's an
947 # interesting introspection-based alternative to bindtags.
948
949 def canvasHandle( self, eventName, event ):
950 "Generic canvas event handler"
951 if self.active is None:
952 return
953 toolName = self.active
954 handler = getattr( self, eventName + toolName, None )
955 if handler is not None:
956 handler( event )
957
958 def clickCanvas( self, event ):
959 "Canvas click handler."
960 self.canvasHandle( 'click', event )
961
962 def dragCanvas( self, event ):
963 "Canvas drag handler."
964 self.canvasHandle( 'drag', event )
965
966 def releaseCanvas( self, event ):
967 "Canvas mouse up handler."
968 self.canvasHandle( 'release', event )
969
970 # Currently the only items we can select directly are
971 # links. Nodes are handled by bindings in the node icon.
972
973 def findItem( self, x, y ):
974 "Find items at a location in our canvas."
975 items = self.canvas.find_overlapping( x, y, x, y )
976 if len( items ) == 0:
977 return None
978 else:
979 return items[ 0 ]
980
Caio24c0cbd2015-01-19 18:45:53 -0200981 # Canvas bindings for Select, Host, Router and Link tools
carlosmscabralf40ecd12013-02-01 18:15:58 -0200982
983 def clickSelect( self, event ):
984 "Select an item."
985 self.selectItem( self.findItem( event.x, event.y ) )
986
987 def deleteItem( self, item ):
988 "Delete an item."
989 # Don't delete while network is running
990 if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
991 return
992 # Delete from model
993 if item in self.links:
994 self.deleteLink( item )
995 if item in self.itemToWidget:
996 self.deleteNode( item )
997 # Delete from view
998 self.canvas.delete( item )
999
1000 def deleteSelection( self, _event ):
1001 "Delete the selected item."
1002 if self.selection is not None:
1003 self.deleteItem( self.selection )
1004 self.selectItem( None )
1005
Caio Elias47b243c2014-11-23 19:25:34 -02001006 def clearPopups(self):
1007 print 'Entrou funcao clear_popups'
1008
1009 if isHostPopup == True:
1010 print 'Hostpopup = true'
1011 self.hostPopup.unpost
1012 isHostPopup = False
1013 #if isRouterPopup == True
1014 #if isLinkPopup == True
1015
carlosmscabralf40ecd12013-02-01 18:15:58 -02001016 def nodeIcon( self, node, name ):
1017 "Create a new node icon."
1018 icon = Button( self.canvas, image=self.images[ node ],
1019 text=name, compound='top' )
1020 # Unfortunately bindtags wants a tuple
1021 bindtags = [ str( self.nodeBindings ) ]
1022 bindtags += list( icon.bindtags() )
1023 icon.bindtags( tuple( bindtags ) )
1024 return icon
1025
1026 def newNode( self, node, event ):
1027 "Add a new node to our canvas."
1028 c = self.canvas
1029 x, y = c.canvasx( event.x ), c.canvasy( event.y )
Caio Elias47b243c2014-11-23 19:25:34 -02001030 name = self.nodePrefixes[ node ]
Caio24c0cbd2015-01-19 18:45:53 -02001031
Caio Elias47b243c2014-11-23 19:25:34 -02001032 if 'LegacyRouter' == node:
Caio24c0cbd2015-01-19 18:45:53 -02001033 self.routerCount += 1
1034 name = self.nodePrefixes[ node ] + str( self.routerCount )
1035 self.routerOpts[name] = {}
1036 self.routerOpts[name]['nodeNum']=self.routerCount
1037 self.routerOpts[name]['hostname']=name
1038 self.routerOpts[name]['nodeType']='legacyRouter'
1039
Caio Elias47b243c2014-11-23 19:25:34 -02001040 if 'Host' == node:
1041 self.hostCount += 1
1042 name = self.nodePrefixes[ node ] + str( self.hostCount )
1043 self.hostOpts[name] = {'sched':'host'}
1044 self.hostOpts[name]['nodeNum']=self.hostCount
1045 self.hostOpts[name]['hostname']=name
Caio Elias47b243c2014-11-23 19:25:34 -02001046
carlosmscabralf40ecd12013-02-01 18:15:58 -02001047 icon = self.nodeIcon( node, name )
1048 item = self.canvas.create_window( x, y, anchor='c', window=icon,
1049 tags=node )
1050 self.widgetToItem[ icon ] = item
1051 self.itemToWidget[ item ] = icon
1052 self.selectItem( item )
1053 icon.links = {}
Caio Elias47b243c2014-11-23 19:25:34 -02001054 if 'LegacyRouter' == node:
1055 icon.bind('<Button-3>', self.do_legacyRouterPopup )
1056 if 'Host' == node:
1057 icon.bind('<Button-3>', self.do_hostPopup )
1058
carlosmscabralf40ecd12013-02-01 18:15:58 -02001059 def clickHost( self, event ):
1060 "Add a new host to our canvas."
1061 self.newNode( 'Host', event )
1062
Caio Elias47b243c2014-11-23 19:25:34 -02001063 def clickLegacyRouter( self, event ):
Caio24c0cbd2015-01-19 18:45:53 -02001064 "Add a new router to our canvas."
Caio Elias47b243c2014-11-23 19:25:34 -02001065 self.newNode( 'LegacyRouter', event )
1066
Caio Elias47b243c2014-11-23 19:25:34 -02001067 def dragNetLink( self, event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001068 "Drag a link's endpoint to another node."
1069 if self.link is None:
1070 return
1071 # Since drag starts in widget, we use root coords
1072 x = self.canvasx( event.x_root )
1073 y = self.canvasy( event.y_root )
1074 c = self.canvas
1075 c.coords( self.link, self.linkx, self.linky, x, y )
1076
Caio Elias47b243c2014-11-23 19:25:34 -02001077 def releaseNetLink( self, _event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001078 "Give up on the current link."
1079 if self.link is not None:
1080 self.canvas.delete( self.link )
1081 self.linkWidget = self.linkItem = self.link = None
1082
1083 # Generic node handlers
1084
1085 def createNodeBindings( self ):
1086 "Create a set of bindings for nodes."
1087 bindings = {
1088 '<ButtonPress-1>': self.clickNode,
1089 '<B1-Motion>': self.dragNode,
1090 '<ButtonRelease-1>': self.releaseNode,
1091 '<Enter>': self.enterNode,
Caio Elias47b243c2014-11-23 19:25:34 -02001092 '<Leave>': self.leaveNode
carlosmscabralf40ecd12013-02-01 18:15:58 -02001093 }
1094 l = Label() # lightweight-ish owner for bindings
1095 for event, binding in bindings.items():
1096 l.bind( event, binding )
1097 return l
1098
1099 def selectItem( self, item ):
1100 "Select an item and remember old selection."
1101 self.lastSelection = self.selection
1102 self.selection = item
1103
1104 def enterNode( self, event ):
1105 "Select node on entry."
1106 self.selectNode( event )
1107
1108 def leaveNode( self, _event ):
1109 "Restore old selection on exit."
1110 self.selectItem( self.lastSelection )
1111
1112 def clickNode( self, event ):
1113 "Node click handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001114 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001115 self.startLink( event )
1116 else:
1117 self.selectNode( event )
1118 return 'break'
1119
1120 def dragNode( self, event ):
1121 "Node drag handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001122 if self.active is 'NetLink':
1123 self.dragNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001124 else:
1125 self.dragNodeAround( event )
1126
1127 def releaseNode( self, event ):
1128 "Node release handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001129 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001130 self.finishLink( event )
1131
1132 # Specific node handlers
1133
1134 def selectNode( self, event ):
1135 "Select the node that was clicked on."
1136 item = self.widgetToItem.get( event.widget, None )
1137 self.selectItem( item )
1138
1139 def dragNodeAround( self, event ):
1140 "Drag a node around on the canvas."
1141 c = self.canvas
1142 # Convert global to local coordinates;
1143 # Necessary since x, y are widget-relative
1144 x = self.canvasx( event.x_root )
1145 y = self.canvasy( event.y_root )
1146 w = event.widget
1147 # Adjust node position
1148 item = self.widgetToItem[ w ]
1149 c.coords( item, x, y )
1150 # Adjust link positions
1151 for dest in w.links:
1152 link = w.links[ dest ]
1153 item = self.widgetToItem[ dest ]
1154 x1, y1 = c.coords( item )
1155 c.coords( link, x, y, x1, y1 )
Caio Elias47b243c2014-11-23 19:25:34 -02001156 self.updateScrollRegion()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001157
Caio Elias47b243c2014-11-23 19:25:34 -02001158 def createDataLinkBindings( self ):
1159 "Create a set of bindings for nodes."
1160 # Link bindings
1161 # Selection still needs a bit of work overall
1162 # Callbacks ignore event
1163
1164 def select( _event, link=self.link ):
1165 "Select item on mouse entry."
1166 self.selectItem( link )
1167
1168 def highlight( _event, link=self.link ):
1169 "Highlight item on mouse entry."
1170 self.selectItem( link )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001171 self.canvas.itemconfig( link, fill='green' )
1172
1173 def unhighlight( _event, link=self.link ):
1174 "Unhighlight item on mouse exit."
1175 self.canvas.itemconfig( link, fill='blue' )
Caio Elias47b243c2014-11-23 19:25:34 -02001176 #self.selectItem( None )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001177
1178 self.canvas.tag_bind( self.link, '<Enter>', highlight )
1179 self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
1180 self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
Caio Elias47b243c2014-11-23 19:25:34 -02001181 self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
1182
Caio Elias47b243c2014-11-23 19:25:34 -02001183 def startLink( self, event ):
1184 "Start a new link."
1185 if event.widget not in self.widgetToItem:
1186 # Didn't click on a node
1187 return
1188
1189 w = event.widget
1190 item = self.widgetToItem[ w ]
1191 x, y = self.canvas.coords( item )
1192 self.link = self.canvas.create_line( x, y, x, y, width=4,
1193 fill='blue', tag='link' )
1194 self.linkx, self.linky = x, y
1195 self.linkWidget = w
1196 self.linkItem = item
1197
carlosmscabralf40ecd12013-02-01 18:15:58 -02001198 def finishLink( self, event ):
1199 "Finish creating a link"
1200 if self.link is None:
1201 return
1202 source = self.linkWidget
1203 c = self.canvas
1204 # Since we dragged from the widget, use root coords
1205 x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
1206 target = self.findItem( x, y )
1207 dest = self.itemToWidget.get( target, None )
1208 if ( source is None or dest is None or source == dest
1209 or dest in source.links or source in dest.links ):
Caio Elias47b243c2014-11-23 19:25:34 -02001210 self.releaseNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001211 return
1212 # For now, don't allow hosts to be directly linked
Caio Elias47b243c2014-11-23 19:25:34 -02001213 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1214 dtags = self.canvas.gettags( target )
Caio Eliase904f532014-12-01 16:19:22 -02001215 if (('Host' in stags and 'Host' in dtags)):
Caio Elias47b243c2014-11-23 19:25:34 -02001216 self.releaseNetLink( event )
1217 return
1218
1219 # Set link type
1220 linkType='data'
Caio Eliase904f532014-12-01 16:19:22 -02001221
1222 self.createDataLinkBindings()
Caio Elias47b243c2014-11-23 19:25:34 -02001223 c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
1224
carlosmscabralf40ecd12013-02-01 18:15:58 -02001225 x, y = c.coords( target )
1226 c.coords( self.link, self.linkx, self.linky, x, y )
Caio Elias47b243c2014-11-23 19:25:34 -02001227 self.addLink( source, dest, linktype=linkType )
Caio Eliase904f532014-12-01 16:19:22 -02001228
carlosmscabralf40ecd12013-02-01 18:15:58 -02001229 # We're done
1230 self.link = self.linkWidget = None
1231
1232 # Menu handlers
1233
1234 def about( self ):
1235 "Display about box."
1236 about = self.aboutBox
1237 if about is None:
1238 bg = 'white'
1239 about = Toplevel( bg='white' )
1240 about.title( 'About' )
Caio Elias2fa082d2014-12-01 16:38:04 -02001241 info = self.appName + ': a simple network editor for MiniCCNx - based on Miniedit'
1242 warning = 'Development version - not entirely functional!'
1243 #version = 'MiniEdit '+MINIEDIT_VERSION
1244 author = 'Carlos Cabral, Jan 2013'
1245 author2 = 'Caio Elias, Nov 2014'
1246 author3 = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
Caio Elias47b243c2014-11-23 19:25:34 -02001247 enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
1248 www = 'http://gregorygee.wordpress.com/category/miniedit/'
carlosmscabralf40ecd12013-02-01 18:15:58 -02001249 line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
Caio Elias2fa082d2014-12-01 16:38:04 -02001250 line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001251 line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
Caio Elias2fa082d2014-12-01 16:38:04 -02001252 line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
1253 line5 = Label( about, text=author3, font='Helvetica 9', bg=bg )
1254 line6 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
1255 line7 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
1256
1257
1258 line7.insert(0, www)
1259 line7.configure(state='readonly')
carlosmscabralf40ecd12013-02-01 18:15:58 -02001260 line1.pack( padx=20, pady=10 )
1261 line2.pack(pady=10 )
1262 line3.pack(pady=10 )
Caio Elias47b243c2014-11-23 19:25:34 -02001263 line4.pack(pady=10 )
1264 line5.pack(pady=10 )
Caio Elias2fa082d2014-12-01 16:38:04 -02001265 line6.pack(pady=10 )
1266 line7.pack(pady=10 )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001267 hide = ( lambda about=about: about.withdraw() )
1268 self.aboutBox = about
1269 # Hide on close rather than destroying window
1270 Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
1271 # Show (existing) window
1272 about.deiconify()
1273
1274 def createToolImages( self ):
1275 "Create toolbar (and icon) images."
1276
Caio Elias47b243c2014-11-23 19:25:34 -02001277 def hostDetails( self, _ignore=None ):
1278 if ( self.selection is None or
1279 self.net is not None or
1280 self.selection not in self.itemToWidget ):
1281 return
1282 widget = self.itemToWidget[ self.selection ]
1283 name = widget[ 'text' ]
1284 tags = self.canvas.gettags( self.selection )
Caio Elias47b243c2014-11-23 19:25:34 -02001285
Caio24c0cbd2015-01-19 18:45:53 -02001286 #print tags
1287 if 'Host' in tags:
Caio Elias47b243c2014-11-23 19:25:34 -02001288
Caio24c0cbd2015-01-19 18:45:53 -02001289 prefDefaults = self.hostOpts[name]
1290 hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults, isRouter='False')
1291 self.master.wait_window(hostBox.top)
1292 if hostBox.result:
1293 newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
Caio Elias47b243c2014-11-23 19:25:34 -02001294
Caio24c0cbd2015-01-19 18:45:53 -02001295 if len(hostBox.result['startCommand']) > 0:
1296 newHostOpts['startCommand'] = hostBox.result['startCommand']
1297 if hostBox.result['cpu']:
1298 newHostOpts['cpu'] = hostBox.result['cpu']
1299 if hostBox.result['mem']:
1300 newHostOpts['mem'] = hostBox.result['mem']
1301 if len(hostBox.result['hostname']) > 0:
1302 newHostOpts['hostname'] = hostBox.result['hostname']
1303 name = hostBox.result['hostname']
1304 widget[ 'text' ] = name
1305 if len(hostBox.result['cache']) > 0:
1306 newHostOpts['cache'] = hostBox.result['cache']
1307 if len(hostBox.result['fibEntries']) > 0:
1308 newHostOpts['fibEntries'] = hostBox.result['fibEntries']
1309 self.hostOpts[name] = newHostOpts
1310
1311 print 'New host details for ' + name + ' = ' + str(newHostOpts)
1312
1313 elif 'LegacyRouter' in tags:
1314
1315 prefDefaults = self.routerOpts[name]
1316 hostBox = HostDialog(self, title='Router Details', prefDefaults=prefDefaults, isRouter='True')
1317 self.master.wait_window(hostBox.top)
1318 if hostBox.result:
1319 newRouterOpts = {'nodeNum':self.routerOpts[name]['nodeNum']}
1320
1321 if hostBox.result['cpu']:
1322 newRouterOpts['cpu'] = hostBox.result['cpu']
1323 if hostBox.result['mem']:
1324 newRouterOpts['mem'] = hostBox.result['mem']
1325 if len(hostBox.result['hostname']) > 0:
1326 newRouterOpts['hostname'] = hostBox.result['hostname']
1327 name = hostBox.result['hostname']
1328 widget[ 'text' ] = name
1329 if len(hostBox.result['cache']) > 0:
1330 newRouterOpts['cache'] = hostBox.result['cache']
1331 if len(hostBox.result['fibEntries']) > 0:
1332 newRouterOpts['fibEntries'] = hostBox.result['fibEntries']
1333 self.routerOpts[name] = newRouterOpts
1334
1335 print 'New host details for ' + name + ' = ' + str(newRouterOpts)
Caio Elias47b243c2014-11-23 19:25:34 -02001336
1337 def linkDetails( self, _ignore=None ):
1338 if ( self.selection is None or
1339 self.net is not None):
1340 return
1341 link = self.selection
1342
1343 linkDetail = self.links[link]
1344 src = linkDetail['src']
1345 dest = linkDetail['dest']
1346 linkopts = linkDetail['linkOpts']
1347 linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
1348 if linkBox.result is not None:
1349 linkDetail['linkOpts'] = linkBox.result
1350 print 'New link details = ' + str(linkBox.result)
1351
carlosmscabralf40ecd12013-02-01 18:15:58 -02001352 # Model interface
1353 #
1354 # Ultimately we will either want to use a topo or
1355 # mininet object here, probably.
1356
Caio Elias47b243c2014-11-23 19:25:34 -02001357 def addLink( self, source, dest, linktype='data', linkopts={} ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001358 "Add link to model."
1359 source.links[ dest ] = self.link
1360 dest.links[ source ] = self.link
Caio Elias47b243c2014-11-23 19:25:34 -02001361 self.links[ self.link ] = {'type' :linktype,
1362 'src':source,
1363 'dest':dest,
1364 'linkOpts':linkopts}
carlosmscabralf40ecd12013-02-01 18:15:58 -02001365
1366 def deleteLink( self, link ):
1367 "Delete link from model."
1368 pair = self.links.get( link, None )
1369 if pair is not None:
Caio Elias47b243c2014-11-23 19:25:34 -02001370 source=pair['src']
1371 dest=pair['dest']
carlosmscabralf40ecd12013-02-01 18:15:58 -02001372 del source.links[ dest ]
1373 del dest.links[ source ]
Caio Elias47b243c2014-11-23 19:25:34 -02001374 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1375 dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
1376 ltags = self.canvas.gettags( link )
1377
carlosmscabralf40ecd12013-02-01 18:15:58 -02001378 if link is not None:
1379 del self.links[ link ]
1380
1381 def deleteNode( self, item ):
1382 "Delete node (and its links) from model."
Caio Elias47b243c2014-11-23 19:25:34 -02001383
carlosmscabralf40ecd12013-02-01 18:15:58 -02001384 widget = self.itemToWidget[ item ]
Caio Elias47b243c2014-11-23 19:25:34 -02001385 tags = self.canvas.gettags(item)
Caio Elias47b243c2014-11-23 19:25:34 -02001386
carlosmscabralf40ecd12013-02-01 18:15:58 -02001387 for link in widget.links.values():
1388 # Delete from view and model
1389 self.deleteItem( link )
1390 del self.itemToWidget[ item ]
1391 del self.widgetToItem[ widget ]
1392
Caio Elias47b243c2014-11-23 19:25:34 -02001393 def do_linkPopup(self, event):
1394 # display the popup menu
1395 if ( self.net is None ):
1396 try:
1397 self.linkPopup.tk_popup(event.x_root, event.y_root)
1398 finally:
1399 # make sure to release the grab (Tk 8.0a1 only)
1400 self.linkPopup.grab_release()
Caio Elias47b243c2014-11-23 19:25:34 -02001401
1402 def do_legacyRouterPopup(self, event):
1403 # display the popup menu
1404 if ( self.net is None ):
1405 try:
1406 self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
1407 finally:
1408 # make sure to release the grab (Tk 8.0a1 only)
1409 self.legacyRouterPopup.grab_release()
1410
1411 def do_hostPopup(self, event):
1412 # display the popup menu
Caio Eliase904f532014-12-01 16:19:22 -02001413 if ( self.net is None ):
1414 try:
1415 self.hostPopup.tk_popup(event.x_root, event.y_root)
1416 isHostPopup = True
1417 finally:
1418 # make sure to release the grab (Tk 8.0a1 only)
1419 self.hostPopup.grab_release()
Caio Elias47b243c2014-11-23 19:25:34 -02001420
carlosmscabralf40ecd12013-02-01 18:15:58 -02001421 def xterm( self, _ignore=None ):
1422 "Make an xterm when a button is pressed."
1423 if ( self.selection is None or
1424 self.net is None or
1425 self.selection not in self.itemToWidget ):
1426 return
1427 name = self.itemToWidget[ self.selection ][ 'text' ]
1428 if name not in self.net.nameToNode:
1429 return
Caio Elias47b243c2014-11-23 19:25:34 -02001430 term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
1431 if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
1432 self.net.terms += term
1433 else:
1434 self.net.terms.append(term)
carlosmscabralf40ecd12013-02-01 18:15:58 -02001435
Caio Elias47b243c2014-11-23 19:25:34 -02001436 def iperf( self, _ignore=None ):
1437 "Make an xterm when a button is pressed."
1438 if ( self.selection is None or
1439 self.net is None or
1440 self.selection not in self.itemToWidget ):
1441 return
1442 name = self.itemToWidget[ self.selection ][ 'text' ]
1443 if name not in self.net.nameToNode:
1444 return
1445 self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
1446
1447 """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
1448
1449 def parseArgs( self ):
1450 """Parse command-line args and return options object.
1451 returns: opts parse options dict"""
1452
1453 if '--custom' in sys.argv:
1454 index = sys.argv.index( '--custom' )
1455 if len( sys.argv ) > index + 1:
1456 filename = sys.argv[ index + 1 ]
1457 self.parseCustomFile( filename )
1458 else:
1459 raise Exception( 'Custom file name not found' )
1460
Caio Eliase904f532014-12-01 16:19:22 -02001461 desc = ( "The %prog utility creates Miniccnx network from the\n"
Caio Elias47b243c2014-11-23 19:25:34 -02001462 "command line. It can create parametrized topologies,\n"
Caio Eliase904f532014-12-01 16:19:22 -02001463 "invoke the Miniccnx CLI, and run tests." )
Caio Elias47b243c2014-11-23 19:25:34 -02001464
Caio Eliase904f532014-12-01 16:19:22 -02001465 usage = ( '%prog [options] [template_file]\n'
1466 '\nIf no template_file is given, generated template will be written to the file miniccnx.conf in the current directory.\n'
1467 'Type %prog -h for details)' )
Caio Elias47b243c2014-11-23 19:25:34 -02001468
1469 opts = OptionParser( description=desc, usage=usage )
1470
1471 addDictOption( opts, TOPOS, TOPODEF, 'topo' )
1472 addDictOption( opts, LINKS, LINKDEF, 'link' )
1473
1474 opts.add_option( '--custom', type='string', default=None,
1475 help='read custom topo and node params from .py' +
1476 'file' )
1477
1478 self.options, self.args = opts.parse_args()
1479 # We don't accept extra arguments after the options
1480 if self.args:
Caio Eliase904f532014-12-01 16:19:22 -02001481 if len(self.args) > 1:
1482 opts.print_help()
1483 exit()
1484 else:
1485 self.template_file=self.args[0]
Caio Elias47b243c2014-11-23 19:25:34 -02001486
1487 def setCustom( self, name, value ):
1488 "Set custom parameters for MininetRunner."
1489 if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
1490 # Update dictionaries
1491 param = name.upper()
1492 globals()[ param ].update( value )
1493 elif name == 'validate':
1494 # Add custom validate function
1495 self.validate = value
1496 else:
1497 # Add or modify global variable or class
1498 globals()[ name ] = value
1499
1500 def parseCustomFile( self, fileName ):
1501 "Parse custom file and add params before parsing cmd-line options."
1502 customs = {}
1503 if os.path.isfile( fileName ):
1504 execfile( fileName, customs, customs )
1505 for name, val in customs.iteritems():
1506 self.setCustom( name, val )
1507 else:
1508 raise Exception( 'could not find custom file: %s' % fileName )
1509
1510 def importTopo( self ):
1511 print 'topo='+self.options.topo
1512 if self.options.topo == 'none':
1513 return
1514 self.newTopology()
1515 topo = buildTopo( TOPOS, self.options.topo )
1516 link = customConstructor( LINKS, self.options.link )
1517 importNet = Mininet(topo=topo, build=False, link=link)
1518 importNet.build()
1519
1520 c = self.canvas
1521 rowIncrement = 100
1522 currentY = 100
1523
Caio Elias47b243c2014-11-23 19:25:34 -02001524 # Add switches
1525 print 'switches:'+str(len(importNet.switches))
1526 columnCount = 0
1527 for switch in importNet.switches:
1528 name = switch.name
1529 self.switchOpts[name] = {}
1530 self.switchOpts[name]['nodeNum']=self.switchCount
1531 self.switchOpts[name]['hostname']=name
1532 self.switchOpts[name]['switchType']='default'
1533 self.switchOpts[name]['controllers']=[]
1534
1535 x = columnCount*100+100
1536 self.addNode('Switch', self.switchCount,
1537 float(x), float(currentY), name=name)
1538 icon = self.findWidgetByName(name)
1539 icon.bind('<Button-3>', self.do_switchPopup )
Caio Eliase904f532014-12-01 16:19:22 -02001540
Caio Elias47b243c2014-11-23 19:25:34 -02001541 if columnCount == 9:
1542 columnCount = 0
1543 currentY = currentY + rowIncrement
1544 else:
1545 columnCount =columnCount+1
1546
Caio Elias47b243c2014-11-23 19:25:34 -02001547 currentY = currentY + rowIncrement
1548 # Add hosts
1549 print 'hosts:'+str(len(importNet.hosts))
1550 columnCount = 0
1551 for host in importNet.hosts:
1552 name = host.name
1553 self.hostOpts[name] = {'sched':'host'}
1554 self.hostOpts[name]['nodeNum']=self.hostCount
1555 self.hostOpts[name]['hostname']=name
Caio24c0cbd2015-01-19 18:45:53 -02001556 #self.hostOpts[name]['ip']=host.IP()
Caio Elias47b243c2014-11-23 19:25:34 -02001557
1558 x = columnCount*100+100
1559 self.addNode('Host', self.hostCount,
1560 float(x), float(currentY), name=name)
1561 icon = self.findWidgetByName(name)
1562 icon.bind('<Button-3>', self.do_hostPopup )
1563 if columnCount == 9:
1564 columnCount = 0
1565 currentY = currentY + rowIncrement
1566 else:
1567 columnCount =columnCount+1
1568
1569 print 'links:'+str(len(topo.links()))
1570 #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
1571 for link in topo.links():
1572 print str(link)
1573 srcNode = link[0]
1574 src = self.findWidgetByName(srcNode)
1575 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
1576
1577 destNode = link[1]
1578 dest = self.findWidgetByName(destNode)
1579 dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
1580
1581 params = topo.linkInfo( srcNode, destNode )
1582 print 'Link Parameters='+str(params)
1583
1584 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
1585 fill='blue', tag='link' )
1586 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
1587 self.addLink( src, dest, linkopts=params )
1588 self.createDataLinkBindings()
1589 self.link = self.linkWidget = None
1590
1591 importNet.stop()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001592
1593def miniEditImages():
1594 "Create and return images for MiniEdit."
1595
1596 # Image data. Git will be unhappy. However, the alternative
1597 # is to keep track of separate binary files, which is also
1598 # unappealing.
1599
1600 return {
1601 'Select': BitmapImage(
1602 file='/usr/include/X11/bitmaps/left_ptr' ),
1603
Caio Elias47b243c2014-11-23 19:25:34 -02001604 'LegacyRouter': PhotoImage( data=r"""
Caio24c0cbd2015-01-19 18:45:53 -02001605 R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+
1606 3QFq1DmL3wJMmAMzZZW11dnZ2SFrtyNdmTSO6gIZMUKa8gJVqEOH
1607 zR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq6ymF
1608 4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9
1609 vwNgvwJZsX+69gsXJQFHjTtjizF0tvHx8VOm9z2V736Dhz2N3QM2
1610 acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtgtktjfQFu
1611 3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh3
1612 12Gt+VGm/AQIDTmByAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i5
1613 6gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia7DpeggFt2QNP
1614 m97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er
1615 /yVVhwJJktPh70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVV
1616 hQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18zKfP9wwcLAMHCwFF
1617 iS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh
1618 7cve8pG/7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR
1619 1RMjNTF3vU2X4TZupwRSolNne4nB+T+L2YGz4zJ/zYe99YGHjRdD
1620 cT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6Wl
1621 pW2t7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90
1622 uvPz8wIVKBp42SV5zbfT7wtXpStVfwFWrBVvyTt3swFz5kGBv2+1
1623 /QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
1624 u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T
1625 5yH5BAEAAAAALAAAAAAyABgABwj/AAEIHEiQYJY7Qwg9UsTplRIb
1626 ENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4cHeoI
1627 abJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPA
1628 TqEBoRB9gVJsxRlhPwHI0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o
1629 9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkSrtwADuxC
1630 G/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmD
1631 jhTMmseoKQIFDx7RoxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7
1632 VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq9dGvv09RHFhc
1633 IUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p
1634 9HEUFhxgMSAvjbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJ
1635 ifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CFoVggmEgCyRf01WcF
1636 CYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57Ag
1637 yZckpKKPGFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemM
1638 IQggeaJywSQ/wgHOAmJskQEfWqBlFBEH1P/QaGY3QOpDZXA2+A6m
1639 7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlB
1640 pZdiisd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtCh
1641 RmVPNWgpr+Be+Nc9icARww9TkIEuDAsQ0O7DzGIQzD2QdDEJHTsI
1642 AROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
1643 xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE
1644 42Q9jtFIp8z0Dy1jQMA1AGziz9VoW7310V0znYDTGMQgwUDXLDBO
1645 2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOMLQkc
1646 jvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZ
1647 MDHKCTwI8EcQFHBBAAFcgGPLHwLwcMIo12Qxu0ABAQA7
Caio Elias47b243c2014-11-23 19:25:34 -02001648 """),
1649
1650 'Host': PhotoImage( data=r"""
1651 R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
1652 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
1653 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
1654 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
1655 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
1656 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
1657 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
1658 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
1659 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
1660 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
1661 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
1662 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
1663 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
1664 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
1665 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
1666 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
1667 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
1668 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
1669 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
1670 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
1671 ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
1672 BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
1673 HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
1674 p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
1675 C8cSBBAQADs=
1676 """ ),
1677
1678 'NetLink': PhotoImage( data=r"""
carlosmscabralf40ecd12013-02-01 18:15:58 -02001679 R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
1680 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
1681 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
1682 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
1683 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
1684 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
1685 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
1686 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
1687 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
1688 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
1689 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
1690 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
1691 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
1692 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
1693 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
1694 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
1695 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
1696 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
1697 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
1698 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
1699 ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
1700 Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
1701 lBmxI8mSNknm1Dnx5sCAADs=
1702 """ )
1703 }
1704
Caio Elias47b243c2014-11-23 19:25:34 -02001705def addDictOption( opts, choicesDict, default, name, helpStr=None ):
1706 """Convenience function to add choices dicts to OptionParser.
1707 opts: OptionParser instance
1708 choicesDict: dictionary of valid choices, must include default
1709 default: default choice key
1710 name: long option name
1711 help: string"""
1712 if default not in choicesDict:
1713 raise Exception( 'Invalid default %s for choices dict: %s' %
1714 ( default, name ) )
1715 if not helpStr:
1716 helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
1717 '[,param=value...]' )
1718 opts.add_option( '--' + name,
1719 type='string',
1720 default = default,
1721 help = helpStr )
1722
carlosmscabralf40ecd12013-02-01 18:15:58 -02001723if __name__ == '__main__':
1724 setLogLevel( 'info' )
Caio Elias47b243c2014-11-23 19:25:34 -02001725 app = MiniEdit()
1726 """ import topology if specified """
1727 app.parseArgs()
1728 app.importTopo()
1729
1730 global isHostPopup
1731 global isRouterPopup
1732 global isLinkPopup
1733
carlosmscabralf40ecd12013-02-01 18:15:58 -02001734 app.mainloop()