blob: 6141102ab297ab0349366380d18d64904c0aae58 [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:
750 self.hostCount += 1
Caio Elias47b243c2014-11-23 19:25:34 -0200751 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 = [
Caio Eliase904f532014-12-01 16:19:22 -0200781 ('Miniccnx Topology','*.mnccnx'),
Caio Elias47b243c2014-11-23 19:25:34 -0200782 ('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
Caio Elias47b243c2014-11-23 19:25:34 -0200790 # 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
Caio24c0cbd2015-01-19 18:45:53 -0200818 # 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']
Caio Elias47b243c2014-11-23 19:25:34 -0200828 else:
Caio24c0cbd2015-01-19 18:45:53 -0200829 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":
Caio Elias47b243c2014-11-23 19:25:34 -0200835 self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
836 icon = self.findWidgetByName(hostname)
837 icon.bind('<Button-3>', self.do_legacyRouterPopup )
Caio24c0cbd2015-01-19 18:45:53 -0200838 self.routerOpts[hostname] = router['opts']
Caio Elias47b243c2014-11-23 19:25:34 -0200839
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)
Caio24c0cbd2015-01-19 18:45:53 -0200849 dx, dy = self.canvas.coords( self.widgetToItem[ dest ] )
Caio Elias47b243c2014-11-23 19:25:34 -0200850
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
Caio24c0cbd2015-01-19 18:45:53 -0200870 self.routerCount = 0
Caio Elias47b243c2014-11-23 19:25:34 -0200871 self.links = {}
872 self.hostOpts = {}
Caio24c0cbd2015-01-19 18:45:53 -0200873 self.routerOpts = {}
Caio Elias47b243c2014-11-23 19:25:34 -0200874
875 def saveTopology( self ):
876 "Save command."
877 myFormats = [
Caio Eliase904f532014-12-01 16:19:22 -0200878 ('Miniccnx Topology','*.mnccnx'),
Caio Elias47b243c2014-11-23 19:25:34 -0200879 ('All Files','*'),
880 ]
881
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
Caio24c0cbd2015-01-19 18:45:53 -0200888 # Save routers and Hosts
Caio Elias47b243c2014-11-23 19:25:34 -0200889 hostsToSave = []
Caio24c0cbd2015-01-19 18:45:53 -0200890 routersToSave = []
891
Caio Elias47b243c2014-11-23 19:25:34 -0200892 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:
Caio24c0cbd2015-01-19 18:45:53 -0200897 nodeNum = self.routerOpts[name]['nodeNum']
Caio Elias47b243c2014-11-23 19:25:34 -0200898 nodeToSave = {'number':str(nodeNum),
899 'x':str(x1),
900 'y':str(y1),
Caio24c0cbd2015-01-19 18:45:53 -0200901 'opts':self.routerOpts[name] }
902 routersToSave.append(nodeToSave)
Caio Elias47b243c2014-11-23 19:25:34 -0200903 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
Caio24c0cbd2015-01-19 18:45:53 -0200913 savingDictionary['routers'] = routersToSave
Caio Elias47b243c2014-11-23 19:25:34 -0200914
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
Caio24c0cbd2015-01-19 18:45:53 -0200931 #savingDictionary['application'] = self.appPrefs
Caio Elias47b243c2014-11-23 19:25:34 -0200932
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
carlosmscabralf40ecd12013-02-01 18:15:58 -0200941 # 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
Caio24c0cbd2015-01-19 18:45:53 -0200980 # Canvas bindings for Select, Host, Router and Link tools
carlosmscabralf40ecd12013-02-01 18:15:58 -0200981
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
Caio Elias47b243c2014-11-23 19:25:34 -02001005 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
carlosmscabralf40ecd12013-02-01 18:15:58 -02001015 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 )
Caio Elias47b243c2014-11-23 19:25:34 -02001029 name = self.nodePrefixes[ node ]
Caio24c0cbd2015-01-19 18:45:53 -02001030
Caio Elias47b243c2014-11-23 19:25:34 -02001031 if 'LegacyRouter' == node:
Caio24c0cbd2015-01-19 18:45:53 -02001032 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
Caio Elias47b243c2014-11-23 19:25:34 -02001039 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
Caio Elias47b243c2014-11-23 19:25:34 -02001045
carlosmscabralf40ecd12013-02-01 18:15:58 -02001046 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 = {}
Caio Elias47b243c2014-11-23 19:25:34 -02001053 if 'LegacyRouter' == node:
1054 icon.bind('<Button-3>', self.do_legacyRouterPopup )
1055 if 'Host' == node:
1056 icon.bind('<Button-3>', self.do_hostPopup )
1057
carlosmscabralf40ecd12013-02-01 18:15:58 -02001058 def clickHost( self, event ):
1059 "Add a new host to our canvas."
1060 self.newNode( 'Host', event )
1061
Caio Elias47b243c2014-11-23 19:25:34 -02001062 def clickLegacyRouter( self, event ):
Caio24c0cbd2015-01-19 18:45:53 -02001063 "Add a new router to our canvas."
Caio Elias47b243c2014-11-23 19:25:34 -02001064 self.newNode( 'LegacyRouter', event )
1065
Caio Elias47b243c2014-11-23 19:25:34 -02001066 def dragNetLink( self, event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001067 "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
Caio Elias47b243c2014-11-23 19:25:34 -02001076 def releaseNetLink( self, _event ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001077 "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,
Caio Elias47b243c2014-11-23 19:25:34 -02001091 '<Leave>': self.leaveNode
carlosmscabralf40ecd12013-02-01 18:15:58 -02001092 }
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."
Caio Elias47b243c2014-11-23 19:25:34 -02001113 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001114 self.startLink( event )
1115 else:
1116 self.selectNode( event )
1117 return 'break'
1118
1119 def dragNode( self, event ):
1120 "Node drag handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001121 if self.active is 'NetLink':
1122 self.dragNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001123 else:
1124 self.dragNodeAround( event )
1125
1126 def releaseNode( self, event ):
1127 "Node release handler."
Caio Elias47b243c2014-11-23 19:25:34 -02001128 if self.active is 'NetLink':
carlosmscabralf40ecd12013-02-01 18:15:58 -02001129 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 )
Caio Elias47b243c2014-11-23 19:25:34 -02001155 self.updateScrollRegion()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001156
Caio Elias47b243c2014-11-23 19:25:34 -02001157 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 )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001170 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' )
Caio Elias47b243c2014-11-23 19:25:34 -02001175 #self.selectItem( None )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001176
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 )
Caio Elias47b243c2014-11-23 19:25:34 -02001180 self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
1181
Caio Elias47b243c2014-11-23 19:25:34 -02001182 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
carlosmscabralf40ecd12013-02-01 18:15:58 -02001197 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 ):
Caio Elias47b243c2014-11-23 19:25:34 -02001209 self.releaseNetLink( event )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001210 return
1211 # For now, don't allow hosts to be directly linked
Caio Elias47b243c2014-11-23 19:25:34 -02001212 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1213 dtags = self.canvas.gettags( target )
Caio Eliase904f532014-12-01 16:19:22 -02001214 if (('Host' in stags and 'Host' in dtags)):
Caio Elias47b243c2014-11-23 19:25:34 -02001215 self.releaseNetLink( event )
1216 return
1217
1218 # Set link type
1219 linkType='data'
Caio Eliase904f532014-12-01 16:19:22 -02001220
1221 self.createDataLinkBindings()
Caio Elias47b243c2014-11-23 19:25:34 -02001222 c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
1223
carlosmscabralf40ecd12013-02-01 18:15:58 -02001224 x, y = c.coords( target )
1225 c.coords( self.link, self.linkx, self.linky, x, y )
Caio Elias47b243c2014-11-23 19:25:34 -02001226 self.addLink( source, dest, linktype=linkType )
Caio Eliase904f532014-12-01 16:19:22 -02001227
carlosmscabralf40ecd12013-02-01 18:15:58 -02001228 # 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' )
Caio Elias2fa082d2014-12-01 16:38:04 -02001240 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'
Caio Elias47b243c2014-11-23 19:25:34 -02001246 enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
1247 www = 'http://gregorygee.wordpress.com/category/miniedit/'
carlosmscabralf40ecd12013-02-01 18:15:58 -02001248 line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
Caio Elias2fa082d2014-12-01 16:38:04 -02001249 line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001250 line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
Caio Elias2fa082d2014-12-01 16:38:04 -02001251 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
1257 line7.insert(0, www)
1258 line7.configure(state='readonly')
carlosmscabralf40ecd12013-02-01 18:15:58 -02001259 line1.pack( padx=20, pady=10 )
1260 line2.pack(pady=10 )
1261 line3.pack(pady=10 )
Caio Elias47b243c2014-11-23 19:25:34 -02001262 line4.pack(pady=10 )
1263 line5.pack(pady=10 )
Caio Elias2fa082d2014-12-01 16:38:04 -02001264 line6.pack(pady=10 )
1265 line7.pack(pady=10 )
carlosmscabralf40ecd12013-02-01 18:15:58 -02001266 hide = ( lambda about=about: about.withdraw() )
1267 self.aboutBox = about
1268 # Hide on close rather than destroying window
1269 Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
1270 # Show (existing) window
1271 about.deiconify()
1272
1273 def createToolImages( self ):
1274 "Create toolbar (and icon) images."
1275
Caio Elias47b243c2014-11-23 19:25:34 -02001276 def hostDetails( self, _ignore=None ):
1277 if ( self.selection is None or
1278 self.net is not None or
1279 self.selection not in self.itemToWidget ):
1280 return
1281 widget = self.itemToWidget[ self.selection ]
1282 name = widget[ 'text' ]
1283 tags = self.canvas.gettags( self.selection )
Caio Elias47b243c2014-11-23 19:25:34 -02001284
Caio24c0cbd2015-01-19 18:45:53 -02001285 #print tags
1286 if 'Host' in tags:
Caio Elias47b243c2014-11-23 19:25:34 -02001287
Caio24c0cbd2015-01-19 18:45:53 -02001288 prefDefaults = self.hostOpts[name]
1289 hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults, isRouter='False')
1290 self.master.wait_window(hostBox.top)
1291 if hostBox.result:
1292 newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
Caio Elias47b243c2014-11-23 19:25:34 -02001293
Caio24c0cbd2015-01-19 18:45:53 -02001294 if len(hostBox.result['startCommand']) > 0:
1295 newHostOpts['startCommand'] = hostBox.result['startCommand']
1296 if hostBox.result['cpu']:
1297 newHostOpts['cpu'] = hostBox.result['cpu']
1298 if hostBox.result['mem']:
1299 newHostOpts['mem'] = hostBox.result['mem']
1300 if len(hostBox.result['hostname']) > 0:
1301 newHostOpts['hostname'] = hostBox.result['hostname']
1302 name = hostBox.result['hostname']
1303 widget[ 'text' ] = name
1304 if len(hostBox.result['cache']) > 0:
1305 newHostOpts['cache'] = hostBox.result['cache']
1306 if len(hostBox.result['fibEntries']) > 0:
1307 newHostOpts['fibEntries'] = hostBox.result['fibEntries']
1308 self.hostOpts[name] = newHostOpts
1309
1310 print 'New host details for ' + name + ' = ' + str(newHostOpts)
1311
1312 elif 'LegacyRouter' in tags:
1313
1314 prefDefaults = self.routerOpts[name]
1315 hostBox = HostDialog(self, title='Router Details', prefDefaults=prefDefaults, isRouter='True')
1316 self.master.wait_window(hostBox.top)
1317 if hostBox.result:
1318 newRouterOpts = {'nodeNum':self.routerOpts[name]['nodeNum']}
1319
1320 if hostBox.result['cpu']:
1321 newRouterOpts['cpu'] = hostBox.result['cpu']
1322 if hostBox.result['mem']:
1323 newRouterOpts['mem'] = hostBox.result['mem']
1324 if len(hostBox.result['hostname']) > 0:
1325 newRouterOpts['hostname'] = hostBox.result['hostname']
1326 name = hostBox.result['hostname']
1327 widget[ 'text' ] = name
1328 if len(hostBox.result['cache']) > 0:
1329 newRouterOpts['cache'] = hostBox.result['cache']
1330 if len(hostBox.result['fibEntries']) > 0:
1331 newRouterOpts['fibEntries'] = hostBox.result['fibEntries']
1332 self.routerOpts[name] = newRouterOpts
1333
1334 print 'New host details for ' + name + ' = ' + str(newRouterOpts)
Caio Elias47b243c2014-11-23 19:25:34 -02001335
1336 def linkDetails( self, _ignore=None ):
1337 if ( self.selection is None or
1338 self.net is not None):
1339 return
1340 link = self.selection
1341
1342 linkDetail = self.links[link]
1343 src = linkDetail['src']
1344 dest = linkDetail['dest']
1345 linkopts = linkDetail['linkOpts']
1346 linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
1347 if linkBox.result is not None:
1348 linkDetail['linkOpts'] = linkBox.result
1349 print 'New link details = ' + str(linkBox.result)
1350
carlosmscabralf40ecd12013-02-01 18:15:58 -02001351 # Model interface
1352 #
1353 # Ultimately we will either want to use a topo or
1354 # mininet object here, probably.
1355
Caio Elias47b243c2014-11-23 19:25:34 -02001356 def addLink( self, source, dest, linktype='data', linkopts={} ):
carlosmscabralf40ecd12013-02-01 18:15:58 -02001357 "Add link to model."
1358 source.links[ dest ] = self.link
1359 dest.links[ source ] = self.link
Caio Elias47b243c2014-11-23 19:25:34 -02001360 self.links[ self.link ] = {'type' :linktype,
1361 'src':source,
1362 'dest':dest,
1363 'linkOpts':linkopts}
carlosmscabralf40ecd12013-02-01 18:15:58 -02001364
1365 def deleteLink( self, link ):
1366 "Delete link from model."
1367 pair = self.links.get( link, None )
1368 if pair is not None:
Caio Elias47b243c2014-11-23 19:25:34 -02001369 source=pair['src']
1370 dest=pair['dest']
carlosmscabralf40ecd12013-02-01 18:15:58 -02001371 del source.links[ dest ]
1372 del dest.links[ source ]
Caio Elias47b243c2014-11-23 19:25:34 -02001373 stags = self.canvas.gettags( self.widgetToItem[ source ] )
1374 dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
1375 ltags = self.canvas.gettags( link )
1376
carlosmscabralf40ecd12013-02-01 18:15:58 -02001377 if link is not None:
1378 del self.links[ link ]
1379
1380 def deleteNode( self, item ):
1381 "Delete node (and its links) from model."
Caio Elias47b243c2014-11-23 19:25:34 -02001382
carlosmscabralf40ecd12013-02-01 18:15:58 -02001383 widget = self.itemToWidget[ item ]
Caio Elias47b243c2014-11-23 19:25:34 -02001384 tags = self.canvas.gettags(item)
Caio Elias47b243c2014-11-23 19:25:34 -02001385
carlosmscabralf40ecd12013-02-01 18:15:58 -02001386 for link in widget.links.values():
1387 # Delete from view and model
1388 self.deleteItem( link )
1389 del self.itemToWidget[ item ]
1390 del self.widgetToItem[ widget ]
1391
Caio Elias47b243c2014-11-23 19:25:34 -02001392 def do_linkPopup(self, event):
1393 # display the popup menu
1394 if ( self.net is None ):
1395 try:
1396 self.linkPopup.tk_popup(event.x_root, event.y_root)
1397 finally:
1398 # make sure to release the grab (Tk 8.0a1 only)
1399 self.linkPopup.grab_release()
Caio Elias47b243c2014-11-23 19:25:34 -02001400
1401 def do_legacyRouterPopup(self, event):
1402 # display the popup menu
1403 if ( self.net is None ):
1404 try:
1405 self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
1406 finally:
1407 # make sure to release the grab (Tk 8.0a1 only)
1408 self.legacyRouterPopup.grab_release()
1409
1410 def do_hostPopup(self, event):
1411 # display the popup menu
Caio Eliase904f532014-12-01 16:19:22 -02001412 if ( self.net is None ):
1413 try:
1414 self.hostPopup.tk_popup(event.x_root, event.y_root)
1415 isHostPopup = True
1416 finally:
1417 # make sure to release the grab (Tk 8.0a1 only)
1418 self.hostPopup.grab_release()
Caio Elias47b243c2014-11-23 19:25:34 -02001419
carlosmscabralf40ecd12013-02-01 18:15:58 -02001420 def xterm( self, _ignore=None ):
1421 "Make an xterm when a button is pressed."
1422 if ( self.selection is None or
1423 self.net is None or
1424 self.selection not in self.itemToWidget ):
1425 return
1426 name = self.itemToWidget[ self.selection ][ 'text' ]
1427 if name not in self.net.nameToNode:
1428 return
Caio Elias47b243c2014-11-23 19:25:34 -02001429 term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
1430 if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
1431 self.net.terms += term
1432 else:
1433 self.net.terms.append(term)
carlosmscabralf40ecd12013-02-01 18:15:58 -02001434
Caio Elias47b243c2014-11-23 19:25:34 -02001435 def iperf( self, _ignore=None ):
1436 "Make an xterm when a button is pressed."
1437 if ( self.selection is None or
1438 self.net is None or
1439 self.selection not in self.itemToWidget ):
1440 return
1441 name = self.itemToWidget[ self.selection ][ 'text' ]
1442 if name not in self.net.nameToNode:
1443 return
1444 self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
1445
1446 """ BELOW HERE IS THE TOPOLOGY IMPORT CODE """
1447
1448 def parseArgs( self ):
1449 """Parse command-line args and return options object.
1450 returns: opts parse options dict"""
1451
1452 if '--custom' in sys.argv:
1453 index = sys.argv.index( '--custom' )
1454 if len( sys.argv ) > index + 1:
1455 filename = sys.argv[ index + 1 ]
1456 self.parseCustomFile( filename )
1457 else:
1458 raise Exception( 'Custom file name not found' )
1459
Caio Eliase904f532014-12-01 16:19:22 -02001460 desc = ( "The %prog utility creates Miniccnx network from the\n"
Caio Elias47b243c2014-11-23 19:25:34 -02001461 "command line. It can create parametrized topologies,\n"
Caio Eliase904f532014-12-01 16:19:22 -02001462 "invoke the Miniccnx CLI, and run tests." )
Caio Elias47b243c2014-11-23 19:25:34 -02001463
Caio Eliase904f532014-12-01 16:19:22 -02001464 usage = ( '%prog [options] [template_file]\n'
1465 '\nIf no template_file is given, generated template will be written to the file miniccnx.conf in the current directory.\n'
1466 'Type %prog -h for details)' )
Caio Elias47b243c2014-11-23 19:25:34 -02001467
1468 opts = OptionParser( description=desc, usage=usage )
1469
1470 addDictOption( opts, TOPOS, TOPODEF, 'topo' )
1471 addDictOption( opts, LINKS, LINKDEF, 'link' )
1472
1473 opts.add_option( '--custom', type='string', default=None,
1474 help='read custom topo and node params from .py' +
1475 'file' )
1476
1477 self.options, self.args = opts.parse_args()
1478 # We don't accept extra arguments after the options
1479 if self.args:
Caio Eliase904f532014-12-01 16:19:22 -02001480 if len(self.args) > 1:
1481 opts.print_help()
1482 exit()
1483 else:
1484 self.template_file=self.args[0]
Caio Elias47b243c2014-11-23 19:25:34 -02001485
1486 def setCustom( self, name, value ):
1487 "Set custom parameters for MininetRunner."
1488 if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
1489 # Update dictionaries
1490 param = name.upper()
1491 globals()[ param ].update( value )
1492 elif name == 'validate':
1493 # Add custom validate function
1494 self.validate = value
1495 else:
1496 # Add or modify global variable or class
1497 globals()[ name ] = value
1498
1499 def parseCustomFile( self, fileName ):
1500 "Parse custom file and add params before parsing cmd-line options."
1501 customs = {}
1502 if os.path.isfile( fileName ):
1503 execfile( fileName, customs, customs )
1504 for name, val in customs.iteritems():
1505 self.setCustom( name, val )
1506 else:
1507 raise Exception( 'could not find custom file: %s' % fileName )
1508
1509 def importTopo( self ):
1510 print 'topo='+self.options.topo
1511 if self.options.topo == 'none':
1512 return
1513 self.newTopology()
1514 topo = buildTopo( TOPOS, self.options.topo )
1515 link = customConstructor( LINKS, self.options.link )
1516 importNet = Mininet(topo=topo, build=False, link=link)
1517 importNet.build()
1518
1519 c = self.canvas
1520 rowIncrement = 100
1521 currentY = 100
1522
Caio Elias47b243c2014-11-23 19:25:34 -02001523 # Add switches
1524 print 'switches:'+str(len(importNet.switches))
1525 columnCount = 0
1526 for switch in importNet.switches:
1527 name = switch.name
1528 self.switchOpts[name] = {}
1529 self.switchOpts[name]['nodeNum']=self.switchCount
1530 self.switchOpts[name]['hostname']=name
1531 self.switchOpts[name]['switchType']='default'
1532 self.switchOpts[name]['controllers']=[]
1533
1534 x = columnCount*100+100
1535 self.addNode('Switch', self.switchCount,
1536 float(x), float(currentY), name=name)
1537 icon = self.findWidgetByName(name)
1538 icon.bind('<Button-3>', self.do_switchPopup )
Caio Eliase904f532014-12-01 16:19:22 -02001539
Caio Elias47b243c2014-11-23 19:25:34 -02001540 if columnCount == 9:
1541 columnCount = 0
1542 currentY = currentY + rowIncrement
1543 else:
1544 columnCount =columnCount+1
1545
Caio Elias47b243c2014-11-23 19:25:34 -02001546 currentY = currentY + rowIncrement
1547 # Add hosts
1548 print 'hosts:'+str(len(importNet.hosts))
1549 columnCount = 0
1550 for host in importNet.hosts:
1551 name = host.name
1552 self.hostOpts[name] = {'sched':'host'}
1553 self.hostOpts[name]['nodeNum']=self.hostCount
1554 self.hostOpts[name]['hostname']=name
Caio24c0cbd2015-01-19 18:45:53 -02001555 #self.hostOpts[name]['ip']=host.IP()
Caio Elias47b243c2014-11-23 19:25:34 -02001556
1557 x = columnCount*100+100
1558 self.addNode('Host', self.hostCount,
1559 float(x), float(currentY), name=name)
1560 icon = self.findWidgetByName(name)
1561 icon.bind('<Button-3>', self.do_hostPopup )
1562 if columnCount == 9:
1563 columnCount = 0
1564 currentY = currentY + rowIncrement
1565 else:
1566 columnCount =columnCount+1
1567
1568 print 'links:'+str(len(topo.links()))
1569 #[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
1570 for link in topo.links():
1571 print str(link)
1572 srcNode = link[0]
1573 src = self.findWidgetByName(srcNode)
1574 sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
1575
1576 destNode = link[1]
1577 dest = self.findWidgetByName(destNode)
1578 dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
1579
1580 params = topo.linkInfo( srcNode, destNode )
1581 print 'Link Parameters='+str(params)
1582
1583 self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
1584 fill='blue', tag='link' )
1585 c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
1586 self.addLink( src, dest, linkopts=params )
1587 self.createDataLinkBindings()
1588 self.link = self.linkWidget = None
1589
1590 importNet.stop()
carlosmscabralf40ecd12013-02-01 18:15:58 -02001591
1592def miniEditImages():
1593 "Create and return images for MiniEdit."
1594
1595 # Image data. Git will be unhappy. However, the alternative
1596 # is to keep track of separate binary files, which is also
1597 # unappealing.
1598
1599 return {
1600 'Select': BitmapImage(
1601 file='/usr/include/X11/bitmaps/left_ptr' ),
1602
Caio Elias47b243c2014-11-23 19:25:34 -02001603 'LegacyRouter': PhotoImage( data=r"""
Caio24c0cbd2015-01-19 18:45:53 -02001604 R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+
1605 3QFq1DmL3wJMmAMzZZW11dnZ2SFrtyNdmTSO6gIZMUKa8gJVqEOH
1606 zR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq6ymF
1607 4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9
1608 vwNgvwJZsX+69gsXJQFHjTtjizF0tvHx8VOm9z2V736Dhz2N3QM2
1609 acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtgtktjfQFu
1610 3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh3
1611 12Gt+VGm/AQIDTmByAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i5
1612 6gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia7DpeggFt2QNP
1613 m97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er
1614 /yVVhwJJktPh70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVV
1615 hQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18zKfP9wwcLAMHCwFF
1616 iS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh
1617 7cve8pG/7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR
1618 1RMjNTF3vU2X4TZupwRSolNne4nB+T+L2YGz4zJ/zYe99YGHjRdD
1619 cT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6Wl
1620 pW2t7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90
1621 uvPz8wIVKBp42SV5zbfT7wtXpStVfwFWrBVvyTt3swFz5kGBv2+1
1622 /QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
1623 u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T
1624 5yH5BAEAAAAALAAAAAAyABgABwj/AAEIHEiQYJY7Qwg9UsTplRIb
1625 ENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4cHeoI
1626 abJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPA
1627 TqEBoRB9gVJsxRlhPwHI0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o
1628 9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkSrtwADuxC
1629 G/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmD
1630 jhTMmseoKQIFDx7RoxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7
1631 VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq9dGvv09RHFhc
1632 IUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p
1633 9HEUFhxgMSAvjbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJ
1634 ifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CFoVggmEgCyRf01WcF
1635 CYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57Ag
1636 yZckpKKPGFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemM
1637 IQggeaJywSQ/wgHOAmJskQEfWqBlFBEH1P/QaGY3QOpDZXA2+A6m
1638 7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlB
1639 pZdiisd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtCh
1640 RmVPNWgpr+Be+Nc9icARww9TkIEuDAsQ0O7DzGIQzD2QdDEJHTsI
1641 AROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
1642 xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE
1643 42Q9jtFIp8z0Dy1jQMA1AGziz9VoW7310V0znYDTGMQgwUDXLDBO
1644 2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOMLQkc
1645 jvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZ
1646 MDHKCTwI8EcQFHBBAAFcgGPLHwLwcMIo12Qxu0ABAQA7
Caio Elias47b243c2014-11-23 19:25:34 -02001647 """),
1648
1649 'Host': PhotoImage( data=r"""
1650 R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
1651 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
1652 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
1653 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
1654 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
1655 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
1656 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
1657 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
1658 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
1659 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
1660 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
1661 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
1662 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
1663 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
1664 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
1665 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
1666 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
1667 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
1668 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
1669 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
1670 ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
1671 BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
1672 HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
1673 p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
1674 C8cSBBAQADs=
1675 """ ),
1676
1677 'NetLink': PhotoImage( data=r"""
carlosmscabralf40ecd12013-02-01 18:15:58 -02001678 R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
1679 mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
1680 Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
1681 M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
1682 AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
1683 /8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
1684 zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
1685 mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
1686 ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
1687 M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
1688 AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
1689 /2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
1690 zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
1691 mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
1692 ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
1693 MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
1694 AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
1695 ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
1696 AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
1697 RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
1698 ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
1699 Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
1700 lBmxI8mSNknm1Dnx5sCAADs=
1701 """ )
1702 }
1703
Caio Elias47b243c2014-11-23 19:25:34 -02001704def addDictOption( opts, choicesDict, default, name, helpStr=None ):
1705 """Convenience function to add choices dicts to OptionParser.
1706 opts: OptionParser instance
1707 choicesDict: dictionary of valid choices, must include default
1708 default: default choice key
1709 name: long option name
1710 help: string"""
1711 if default not in choicesDict:
1712 raise Exception( 'Invalid default %s for choices dict: %s' %
1713 ( default, name ) )
1714 if not helpStr:
1715 helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
1716 '[,param=value...]' )
1717 opts.add_option( '--' + name,
1718 type='string',
1719 default = default,
1720 help = helpStr )
1721
carlosmscabralf40ecd12013-02-01 18:15:58 -02001722if __name__ == '__main__':
1723 setLogLevel( 'info' )
Caio Elias47b243c2014-11-23 19:25:34 -02001724 app = MiniEdit()
1725 """ import topology if specified """
1726 app.parseArgs()
1727 app.importTopo()
1728
1729 global isHostPopup
1730 global isRouterPopup
1731 global isLinkPopup
1732
carlosmscabralf40ecd12013-02-01 18:15:58 -02001733 app.mainloop()