blob: 8669dde15ea39b83c9a9c8d14744f46c7d37dbbc [file] [log] [blame]
# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
#
# Copyright (C) 2015-2017, The University of Memphis,
# Arizona Board of Regents,
# Regents of the University of California.
#
# This file is part of Mini-NDN.
# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
#
# Mini-NDN is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Mini-NDN is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mini-NDN, e.g., in COPYING.md file.
# If not, see <http://www.gnu.org/licenses/>.
#
# This file incorporates work covered by the following copyright and
# permission notice:
#
# Mininet 2.2.1 License
#
# Copyright (c) 2013-2015 Open Networking Laboratory
# Copyright (c) 2009-2012 Bob Lantz and The Board of Trustees of
# The Leland Stanford Junior University
#
# Original authors: Bob Lantz and Brandon Heller
#
# We are making Mininet available for public use and benefit with the
# expectation that others will use, modify and enhance the Software and
# contribute those enhancements back to the community. However, since we
# would like to make the Software available for broadest use, with as few
# restrictions as possible permission is hereby granted, free of charge, to
# any person obtaining a copy of this Software to deal in the Software
# under the copyrights without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# The name and trademarks of copyright holder(s) may NOT be used in
# advertising or publicity pertaining to the Software or any derivatives
# without specific, written prior permission.
"""
MiniNDNEdit: a simple network editor for MiniNDN
Based on miniccnxedit by:
Carlos Cabral, Jan 2013
Caio Elias, Nov 2014
Based on miniedit by:
Bob Lantz, April 2010
Gregory Gee, July 2013
"""
MINIEDIT_VERSION = '2.2.0.1'
from optparse import OptionParser
from Tkinter import *
from ttk import Notebook
from tkMessageBox import showinfo, showerror, showwarning
from subprocess import call, Popen
import tkFont
import csv
import tkFileDialog
import tkSimpleDialog
import re
import json
from distutils.version import StrictVersion
import os
import sys
import threading
from functools import partial
import pdb
if 'PYTHONPATH' in os.environ:
sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
# someday: from ttk import *
from mininet.log import info, error, debug, output, setLogLevel
from mininet.net import Mininet, VERSION
from mininet.util import ipStr, netParse, ipAdd, quietRun
from mininet.util import buildTopo
from mininet.util import custom
from mininet.term import makeTerm, cleanUpScreens
from mininet.node import Controller, RemoteController, NOX, OVSController
from mininet.node import CPULimitedHost, Host, Node
from mininet.node import OVSKernelSwitch, OVSSwitch, UserSwitch
from mininet.link import TCLink, Intf, Link
from mininet.cli import CLI
from mininet.moduledeps import moduleDeps, pathCheck
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
from mininet.topolib import TreeTopo
print 'MiniNDNEdit running...' #+VERSION
MININET_VERSION = re.sub(r'[^\d\.]', '', VERSION)
if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
from mininet.node import IVSSwitch
from ndn.gui import NfdFrame, NlsrFrame
TOPODEF = 'none'
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
'linear': LinearTopo,
'reversed': SingleSwitchReversedTopo,
'single': SingleSwitchTopo,
'none': None,
'tree': TreeTopo }
LINKDEF = 'default'
LINKS = { 'default': Link,
'tc': TCLink }
HOSTDEF = 'proc'
HOSTS = { 'proc': Host,
'rt': custom( CPULimitedHost, sched='rt' ),
'cfs': custom( CPULimitedHost, sched='cfs' ) }
def runMiniNdn(window, template):
# Hide window
window.withdraw()
proc = Popen("sudo minindn %s" % template, shell=True)
proc.wait()
# Restore window
window.deiconify()
class LegacyRouter( Node ):
def __init__( self, name, inNamespace=True, **params ):
Node.__init__( self, name, inNamespace, **params )
def config( self, **_params ):
if self.intfs:
self.setParam( _params, 'setIP', ip='0.0.0.0' )
r = Node.config( self, **_params )
self.cmd('sysctl -w net.ipv4.ip_forward=1')
return r
class CustomDialog(object):
# TODO: Fix button placement and Title and window focus lock
def __init__(self, master, title):
self.top=Toplevel(master)
self.bodyFrame = Frame(self.top)
self.bodyFrame.grid(row=0, column=0, sticky='nswe')
self.body(self.bodyFrame)
#return self.b # initial focus
buttonFrame = Frame(self.top, relief='ridge', bd=3, bg='lightgrey')
buttonFrame.grid(row=1 , column=0, sticky='nswe')
okButton = Button(buttonFrame, width=8, text='OK', relief='groove',
bd=4, command=self.okAction)
okButton.grid(row=1, column=0, sticky=E)
canlceButton = Button(buttonFrame, width=8, text='Cancel', relief='groove',
bd=4, command=self.cancelAction)
canlceButton.grid(row=1, column=1, sticky=W)
def body(self, master):
self.rootFrame = master
def apply(self):
self.top.destroy()
def cancelAction(self):
self.top.destroy()
def okAction(self):
self.apply()
self.top.destroy()
class HostDialog(CustomDialog):
def __init__(self, master, title, prefDefaults, isRouter):
self.prefValues = prefDefaults
self.result = None
self.isRouter = isRouter
self.title = title
CustomDialog.__init__(self, master, title)
def body(self, master):
self.rootFrame = master
n = Notebook(self.rootFrame)
self.propFrame = Frame(n)
# NDN
self.nfdFrame = NfdFrame(n, self.prefValues)
self.nlsrFrame = NlsrFrame(n,self.prefValues)
n.add(self.propFrame, text='Properties')
n.add(self.nfdFrame, text=self.nfdFrame.frameLabel)
n.add(self.nlsrFrame, text=self.nlsrFrame.frameLabel)
n.pack()
### TAB 1
# Field for Hostname
Label(self.propFrame, text="Hostname:").grid(row=0, sticky=E)
self.hostnameEntry = Entry(self.propFrame)
self.hostnameEntry.grid(row=0, column=1)
if 'hostname' in self.prefValues:
self.hostnameEntry.insert(0, self.prefValues['hostname'])
# Field for CPU
Label(self.propFrame, text="Amount CPU:").grid(row=2, sticky=E)
self.cpuEntry = Entry(self.propFrame)
self.cpuEntry.grid(row=2, column=1)
Label(self.propFrame, text="%").grid(row=2, column=2, sticky=W)
if 'cpu' in self.prefValues:
self.cpuEntry.insert(0, str(self.prefValues['cpu']))
# Field for Memory
Label(self.propFrame, text="Amount MEM:").grid(row=3, sticky=E)
self.memEntry = Entry(self.propFrame)
self.memEntry.grid(row=3, column=1)
Label(self.propFrame, text="%").grid(row=3, column=2, sticky=W)
if 'mem' in self.prefValues:
self.memEntry.insert(0, str(self.prefValues['mem']))
# Field for Cache
Label(self.propFrame, text="Amount CACHE:").grid(row=4, sticky=E)
self.cacheEntry = Entry(self.propFrame)
self.cacheEntry.grid(row=4, column=1)
Label(self.propFrame, text="KBytes").grid(row=4, column=2, sticky=W)
if 'cache' in self.prefValues:
self.cacheEntry.insert(0, str(self.prefValues['cache']))
# Start command
#print self.isRouter
if self.isRouter == 'False':
Label(self.propFrame, text="Start Command(s):").grid(row=5, sticky=E)
self.scrollbar = Scrollbar(self.propFrame, orient="horizontal")
self.startEntry = Entry(self.propFrame, xscrollcommand=self.scrollbar.set,)
self.startEntry.grid(row=5, column=1)
self.scrollbar.grid(row=6, column=1, sticky=N+S+E+W)
self.scrollbar.config(command=self.startEntry.xview)
Label(self.propFrame, text="[Use bash syntax]").grid(row=5, column=2, sticky=W)
if 'startCommand' in self.prefValues:
self.startEntry.insert(0, str(self.prefValues['startCommand']))
else:
self.startEntry= Entry(self.propFrame)
def apply(self):
results = {'cpu': self.cpuEntry.get(),
'cache': self.cacheEntry.get(),
'mem': self.memEntry.get(),
'hostname':self.hostnameEntry.get(),
'startCommand':self.startEntry.get(),
'nfd': self.nfdFrame.getValues(),
'nlsr': self.nlsrFrame.getValues()
}
self.result = results
class VerticalScrolledTable(LabelFrame):
"""A pure Tkinter scrollable frame that actually works!
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def __init__(self, parent, rows=2, columns=2, title=None, *args, **kw):
LabelFrame.__init__(self, parent, text=title, padx=5, pady=5, *args, **kw)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = TableFrame(canvas, rows=rows, columns=columns)
interior_id = canvas.create_window(0, 0, window=interior,
anchor=NW)
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
return
class TableFrame(Frame):
def __init__(self, parent, rows=2, columns=2):
Frame.__init__(self, parent, background="black")
self._widgets = []
self.rows = rows
self.columns = columns
for row in range(rows):
current_row = []
for column in range(columns):
label = Entry(self, borderwidth=0)
label.grid(row=row, column=column, sticky="wens", padx=1, pady=1)
current_row.append(label)
self._widgets.append(current_row)
def set(self, row, column, value):
widget = self._widgets[row][column]
widget.insert(0, value)
def get(self, row, column):
widget = self._widgets[row][column]
return widget.get()
def addRow( self, value=None, readonly=False ):
#print "Adding row " + str(self.rows +1)
current_row = []
for column in range(self.columns):
label = Entry(self, borderwidth=0)
label.grid(row=self.rows, column=column, sticky="wens", padx=1, pady=1)
if value is not None:
label.insert(0, value[column])
if (readonly == True):
label.configure(state='readonly')
current_row.append(label)
self._widgets.append(current_row)
self.update_idletasks()
self.rows += 1
class LinkDialog(tkSimpleDialog.Dialog):
def __init__(self, parent, title, linkDefaults):
self.linkValues = linkDefaults
tkSimpleDialog.Dialog.__init__(self, parent, title)
def body(self, master):
self.var = StringVar(master)
Label(master, text="Bandwidth:").grid(row=0, sticky=E)
self.e1 = Entry(master)
self.e1.grid(row=0, column=1)
Label(master, text="[1-1000] Mbps").grid(row=0, column=2, sticky=W)
if 'bw' in self.linkValues:
self.e1.insert(0,str(self.linkValues['bw']))
Label(master, text="Delay:").grid(row=1, sticky=E)
self.e2 = Entry(master)
self.e2.grid(row=1, column=1)
Label(master, text="[0-1000] ms").grid(row=1, column=2, sticky=W)
if 'delay' in self.linkValues:
self.e2.insert(0, self.linkValues['delay'])
Label(master, text="Loss:").grid(row=2, sticky=E)
self.e3 = Entry(master)
self.e3.grid(row=2, column=1)
Label(master, text="%").grid(row=2, column=2, sticky=W)
if 'loss' in self.linkValues:
self.e3.insert(0, str(self.linkValues['loss']))
return self.e1 # initial focus
def apply(self):
self.result = {}
if (len(self.e1.get()) > 0):
self.result['bw'] = int(self.e1.get())
if (len(self.e2.get()) > 0):
self.result['delay'] = self.e2.get()
if (len(self.e3.get()) > 0):
self.result['loss'] = int(self.e3.get())
class ToolTip(object):
def __init__(self, widget):
self.widget = widget
self.tipwindow = None
self.id = None
self.x = self.y = 0
def showtip(self, text):
"Display text in tooltip window"
self.text = text
if self.tipwindow or not self.text:
return
x, y, cx, cy = self.widget.bbox("insert")
x = x + self.widget.winfo_rootx() + 27
y = y + cy + self.widget.winfo_rooty() +27
self.tipwindow = tw = Toplevel(self.widget)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d" % (x, y))
try:
# For Mac OS
tw.tk.call("::tk::unsupported::MacWindowStyle",
"style", tw._w,
"help", "noActivates")
except TclError:
pass
label = Label(tw, text=self.text, justify=LEFT,
background="#ffffe0", relief=SOLID, borderwidth=1,
font=("tahoma", "8", "normal"))
label.pack(ipadx=1)
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw:
tw.destroy()
class MiniEdit( Frame ):
"A simple network editor for MiniNDN."
def __init__( self, parent=None, cheight=600, cwidth=1000, template_file='minindn.conf' ):
self.template_file = template_file
Frame.__init__( self, parent )
self.action = None
self.appName = 'MiniNDNEdit'
self.fixedFont = tkFont.Font ( family="DejaVu Sans Mono", size="14" )
# Style
self.font = ( 'Geneva', 9 )
self.smallFont = ( 'Geneva', 7 )
self.bg = 'white'
# Title
self.top = self.winfo_toplevel()
self.top.title( self.appName )
# Menu bar
self.createMenubar()
# Editing canvas
self.cheight, self.cwidth = cheight, cwidth
self.cframe, self.canvas = self.createCanvas()
# Toolbar
self.controllers = {}
# Toolbar
self.images = miniEditImages()
self.buttons = {}
self.active = None
self.tools = ( 'Select', 'Host', 'Switch', 'NetLink' )
#self.customColors = { 'LegacyRouter': 'darkGreen', 'Host': 'blue' }
self.toolbar = self.createToolbar()
# Layout
self.toolbar.grid( column=0, row=0, sticky='nsew')
self.cframe.grid( column=1, row=0 )
self.columnconfigure( 1, weight=1 )
self.rowconfigure( 0, weight=1 )
self.pack( expand=True, fill='both' )
# About box
self.aboutBox = None
# Initialize node data
self.nodeBindings = self.createNodeBindings()
self.nodePrefixes = { 'LegacyRouter': 'r', 'Host': 'h', 'Switch': 's'}
self.widgetToItem = {}
self.itemToWidget = {}
# Initialize link tool
self.link = self.linkWidget = None
# Selection support
self.selection = None
# Keyboard bindings
self.bind( '<Control-q>', lambda event: self.quit() )
self.bind( '<KeyPress-Delete>', self.deleteSelection )
self.bind( '<KeyPress-BackSpace>', self.deleteSelection )
self.focus()
#Mouse bindings
self.bind( '<Button-1>', lambda event: self.clearPopups )
self.hostPopup = Menu(self.top, tearoff=0)
self.hostPopup.add_command(label='Host Options', font=self.font, command=self.hostDetails)
#self.hostPopup.add_separator()
#self.hostPopup.add_command(label='Properties', font=self.font, command=self.hostDetails )
self.legacyRouterPopup = Menu(self.top, tearoff=0)
self.legacyRouterPopup.add_command(label='Router Options', font=self.font, command=self.hostDetails)
self.linkPopup = Menu(self.top, tearoff=0)
self.linkPopup.add_command(label='Link Options', font=self.font, command=self.linkDetails)
#self.linkPopup.add_separator()
#self.linkPopup.add_command(label='Properties', font=self.font, command=self.linkDetails )
# Event handling initalization
self.linkx = self.linky = self.linkItem = None
self.lastSelection = None
# Model initialization
self.links = {}
self.hostOpts = {}
self.switchOpts = {}
self.routerOpts = {}
self.hostCount = 0
self.switchCount = 0
self.routerCount = 0
self.net = None
# Close window gracefully
Wm.wm_protocol( self.top, name='WM_DELETE_WINDOW', func=self.quit )
def quit( self ):
"Stop our network, if any, then quit."
#sself.stop()
Frame.quit( self )
def createMenubar( self ): # MODIFICADO - OK
"Create our menu bar."
font = self.font
mbar = Menu( self.top, font=font )
self.top.configure( menu=mbar )
fileMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label="File", font=font, menu=fileMenu )
fileMenu.add_command( label="New", font=font, command=self.newTopology )
fileMenu.add_command( label="Open", font=font, command=self.loadTopology )
fileMenu.add_command( label="Save", font=font, command=self.saveTopology )
fileMenu.add_command( label="Generate", font=font, command=self.doGenerate )
fileMenu.add_command( label="Run", font=font, command=self.doRun )
fileMenu.add_separator()
fileMenu.add_command( label='Quit', command=self.quit, font=font )
editMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label="Edit", font=font, menu=editMenu )
editMenu.add_command( label="Cut", font=font,
command=lambda: self.deleteSelection( None ) )
# Application menu
appMenu = Menu( mbar, tearoff=False )
mbar.add_cascade( label=self.appName, font=font, menu=appMenu )
appMenu.add_command( label='About Mini-NDN', command=self.about,
font=font)
#appMenu.add_separator()
#appMenu.add_command( label='Quit', command=self.quit, font=font )
# Canvas - TUDO IGUAL - OK
def createCanvas( self ):
"Create and return our scrolling canvas frame."
f = Frame( self )
canvas = Canvas( f, width=self.cwidth, height=self.cheight,
bg=self.bg )
# Scroll bars
xbar = Scrollbar( f, orient='horizontal', command=canvas.xview )
ybar = Scrollbar( f, orient='vertical', command=canvas.yview )
canvas.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set )
# Resize box
resize = Label( f, bg='white' )
# Layout
canvas.grid( row=0, column=1, sticky='nsew')
ybar.grid( row=0, column=2, sticky='ns')
xbar.grid( row=1, column=1, sticky='ew' )
resize.grid( row=1, column=2, sticky='nsew' )
# Resize behavior
f.rowconfigure( 0, weight=1 )
f.columnconfigure( 1, weight=1 )
f.grid( row=0, column=0, sticky='nsew' )
f.bind( '<Configure>', lambda event: self.updateScrollRegion() )
# Mouse bindings
canvas.bind( '<ButtonPress-1>', self.clickCanvas )
canvas.bind( '<B1-Motion>', self.dragCanvas )
canvas.bind( '<ButtonRelease-1>', self.releaseCanvas )
return f, canvas
def updateScrollRegion( self ):
"Update canvas scroll region to hold everything."
bbox = self.canvas.bbox( 'all' )
if bbox is not None:
self.canvas.configure( scrollregion=( 0, 0, bbox[ 2 ],
bbox[ 3 ] ) )
def canvasx( self, x_root ):
"Convert root x coordinate to canvas coordinate."
c = self.canvas
return c.canvasx( x_root ) - c.winfo_rootx()
def canvasy( self, y_root ):
"Convert root y coordinate to canvas coordinate."
c = self.canvas
return c.canvasy( y_root ) - c.winfo_rooty()
# Toolbar
def activate( self, toolName ): #IGUAL - OK
"Activate a tool and press its button."
# Adjust button appearance
if self.active:
self.buttons[ self.active ].configure( relief='raised' )
self.buttons[ toolName ].configure( relief='sunken' )
# Activate dynamic bindings
self.active = toolName
def createToolTip(self, widget, text): #NOVA - CRIA HINTS E TIPS
toolTip = ToolTip(widget)
def enter(event):
toolTip.showtip(text)
def leave(event):
toolTip.hidetip()
widget.bind('<Enter>', enter)
widget.bind('<Leave>', leave)
def createToolbar( self ): #MODIFICADO - OK
"Create and return our toolbar frame."
toolbar = Frame( self )
# Tools
for tool in self.tools:
cmd = ( lambda t=tool: self.activate( t ) )
b = Button( toolbar, text=tool, font=self.smallFont, command=cmd)
if tool in self.images:
b.config( height=35, image=self.images[ tool ] )
self.createToolTip(b, str(tool))
# b.config( compound='top' )
b.pack( fill='x' )
self.buttons[ tool ] = b
self.activate( self.tools[ 0 ] )
# Spacer
Label( toolbar, text='' ).pack()
# abaixo copiado Mini-NDN para criar botao Generate
for cmd, color in [ ( 'Generate', 'darkGreen' ) ]:
doCmd = getattr( self, 'do' + cmd )
b = Button( toolbar, text=cmd, font=self.smallFont,
fg=color, command=doCmd )
b.pack( fill='x', side='bottom' )
return toolbar
def doGenerate( self ): #COPIA Mini-NDN - GERA TEMPLATE
"Generate template."
self.activate( 'Select' )
for tool in self.tools:
self.buttons[ tool ].config( state='disabled' )
self.buildTemplate()
for tool in self.tools:
self.buttons[ tool ].config( state='normal' )
toplevel = Toplevel()
label1 = Label(toplevel, text="Template file generated successfully", height=0, width=30)
label1.pack()
b=Button(toplevel, text="Ok", width=5, command=toplevel.destroy)
b.pack(side='bottom', padx=0,pady=0)
def doRun( self ):
"Use current configuration to generate a template and run the topology"
# Generate temporary template file
old_template_file = self.template_file
self.template_file = "/tmp/tmp.conf"
self.doGenerate()
thread = threading.Thread(target=runMiniNdn, args=(self.master, self.template_file))
thread.start()
self.template_file = old_template_file
def buildTemplate( self ): #COPIA Mini-NDN para criar Template
"Generate template"
template = open(self.template_file, 'w')
# hosts
template.write('[nodes]\n')
for widget in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
if 'Host' in tags:
hOpts=self.hostOpts[name]
print hOpts
template.write(name + ': ')
if 'startCommand' in hOpts:
cmds = hOpts['startCommand'].replace("\"", "\\\"")
template.write('apps="%s" ' % cmds)
else:
template.write('_ ')
if 'cache' in hOpts:
template.write('cache=' + hOpts['cache'] + ' ')
if 'cpu' in hOpts:
cpu=float(hOpts['cpu'])/100
template.write('cpu=' + repr(cpu) + ' ')
if 'mem' in hOpts:
mem=float(hOpts['mem'])/100
template.write('mem=' + repr(mem) + ' ')
if 'nlsr' in hOpts:
values = hOpts['nlsr']
template.write('hyperbolic-state=' + values['hyperbolic-state'] + ' ')
template.write('radius=' + values['radius'] + ' ')
template.write('angle=' + values['angle'] + ' ')
template.write('network=' + values['network'] + ' ')
template.write('router=' + values['router'] + ' ')
template.write('site=' + values['site'] + ' ')
template.write('nlsr-log-level=' + values['log-level'] + ' ')
template.write('max-faces-per-prefix=' + values['max-faces-per-prefix'] + ' ')
if 'nfd' in hOpts:
values = hOpts['nfd']
template.write('nfd-log-level=' + values['log-level'] + ' ')
template.write('\n')
# switches/routers
#template.write('[routers]\n')
for router in self.routerOpts.values():
hasOpt='False'
routerName=router['hostname']
#nodetype=router['nodetype']
#nodenum=router['nodenum']
rOpts=self.routerOpts[routerName]
template.write(routerName + ': ')
if 'cpu' in rOpts:
cpu=float(rOpts['cpu'])/100
template.write('cpu=' + repr(cpu) + ' ')
hasOpt='True'
if 'mem' in rOpts:
mem=float(rOpts['mem'])/100
template.write('mem=' + repr(mem) + ' ')
hasOpt='True'
if 'cache' in rOpts:
template.write('cache=' + rOpts['cache'] + ' ')
hasOpt='True'
if hasOpt == 'False':
template.write('_')
template.write('\n')
# Write switches
template.write('[switches]\n')
for switch in self.switchOpts.values():
print "Switch%s" % switch
name = switch['hostname']
template.write(name + ': _\n')
# Make links
template.write('[links]\n')
for link in self.links.values():
dst=link['dest']
src=link['src']
linkopts=link['linkOpts']
linktype=link['type']
srcName, dstName = src[ 'text' ], dst[ 'text' ]
template.write(srcName + ':' + dstName + ' ')
if 'bw' in linkopts:
template.write('bw=' + str(linkopts['bw']) + ' ' )
if 'loss' in linkopts:
template.write('loss=' + repr(linkopts['loss']) + ' ' )
if 'delay' in linkopts:
template.write('delay=' + str(linkopts['delay'])+ 'ms' )
template.write('\n')
template.close()
def addNode( self, node, nodeNum, x, y, name=None):
"Add a new node to our canvas."
if 'LegacyRouter' == node:
self.routerCount += 1
if 'Host' == node:
self.hostCount += 1
if 'Switch' == node:
self.switchCount += 1
if name is None:
name = self.nodePrefixes[ node ] + nodeNum
self.addNamedNode(node, name, x, y)
def addNamedNode( self, node, name, x, y):
"Add a new node to our canvas."
c = self.canvas
icon = self.nodeIcon( node, name )
item = self.canvas.create_window( x, y, anchor='c', window=icon,
tags=node )
self.widgetToItem[ icon ] = item
self.itemToWidget[ item ] = icon
icon.links = {}
def convertJsonUnicode(self, input):
"Some part of Mininet don't like Unicode"
if isinstance(input, dict):
return {self.convertJsonUnicode(key): self.convertJsonUnicode(value) for key, value in input.iteritems()}
elif isinstance(input, list):
return [self.convertJsonUnicode(element) for element in input]
elif isinstance(input, unicode):
return input.encode('utf-8')
else:
return input
def loadTopology( self ):
"Load command."
c = self.canvas
myFormats = [
('MiniNDN Topology','*.mnndn'),
('All Files','*'),
]
f = tkFileDialog.askopenfile(filetypes=myFormats, mode='rb')
if f == None:
return
self.newTopology()
loadedTopology = self.convertJsonUnicode(json.load(f))
# Load hosts
hosts = loadedTopology['hosts']
for host in hosts:
nodeNum = host['number']
hostname = 'h'+nodeNum
if 'hostname' in host['opts']:
hostname = host['opts']['hostname']
else:
host['opts']['hostname'] = hostname
if 'nodeNum' not in host['opts']:
host['opts']['nodeNum'] = int(nodeNum)
x = host['x']
y = host['y']
self.addNode('Host', nodeNum, float(x), float(y), name=hostname)
# Fix JSON converting tuple to list when saving
if 'privateDirectory' in host['opts']:
newDirList = []
for privateDir in host['opts']['privateDirectory']:
if isinstance( privateDir, list ):
newDirList.append((privateDir[0],privateDir[1]))
else:
newDirList.append(privateDir)
host['opts']['privateDirectory'] = newDirList
self.hostOpts[hostname] = host['opts']
icon = self.findWidgetByName(hostname)
icon.bind('<Button-3>', self.do_hostPopup )
# Load routers
routers = loadedTopology['routers']
for router in routers:
nodeNum = router['number']
hostname = 'r'+nodeNum
#print router
if 'nodeType' not in router['opts']:
router['opts']['nodeType'] = 'legacyRouter'
if 'hostname' in router['opts']:
hostname = router['opts']['hostname']
else:
router['opts']['hostname'] = hostname
if 'nodeNum' not in router['opts']:
router['opts']['nodeNum'] = int(nodeNum)
x = router['x']
y = router['y']
if router['opts']['nodeType'] == "legacyRouter":
self.addNode('LegacyRouter', nodeNum, float(x), float(y), name=hostname)
icon = self.findWidgetByName(hostname)
icon.bind('<Button-3>', self.do_legacyRouterPopup )
self.routerOpts[hostname] = router['opts']
# Load switches
switches = loadedTopology['switches']
for switch in switches:
nodeNum = switch['number']
hostname = 's' + nodeNum
if 'hostname' in switch['opts']:
hostname = switch['opts']['hostname']
else:
switch['opts']['hostname'] = hostname
if 'nodeNum' not in switch['opts']:
switch['opts']['nodeNum'] = int(nodeNum)
x = switch['x']
y = switch['y']
self.addNode('Switch', nodeNum, float(x), float(y), name=hostname)
icon = self.findWidgetByName(hostname)
self.switchOpts[hostname] = switch['opts']
# Load links
links = loadedTopology['links']
for link in links:
srcNode = link['src']
src = self.findWidgetByName(srcNode)
sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
destNode = link['dest']
dest = self.findWidgetByName(destNode)
dx, dy = self.canvas.coords( self.widgetToItem[ dest ] )
self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
fill='blue', tag='link' )
c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
self.addLink( src, dest, linkopts=link['opts'] )
self.createDataLinkBindings()
self.link = self.linkWidget = None
f.close
def findWidgetByName( self, name ):
for widget in self.widgetToItem:
if name == widget[ 'text' ]:
return widget
def newTopology( self ):
"New command."
for widget in self.widgetToItem.keys():
self.deleteItem( self.widgetToItem[ widget ] )
self.hostCount = 0
self.routerCount = 0
self.switchCount = 0
self.links = {}
self.hostOpts = {}
self.routerOpts = {}
def saveTopology( self ):
"Save command."
myFormats = [
('MiniNDN Topology','*.mnndn'),
('All Files','*'),
]
savingDictionary = {}
fileName = tkFileDialog.asksaveasfilename(filetypes=myFormats ,title="Save the topology as...")
if len(fileName ) > 0:
# Save Application preferences
savingDictionary['version'] = '2'
# Save routers and Hosts
hostsToSave = []
routersToSave = []
switchesToSave = []
for widget in self.widgetToItem:
name = widget[ 'text' ]
tags = self.canvas.gettags( self.widgetToItem[ widget ] )
x1, y1 = self.canvas.coords( self.widgetToItem[ widget ] )
if 'LegacyRouter' in tags:
nodeNum = self.routerOpts[name]['nodeNum']
nodeToSave = {'number':str(nodeNum),
'x':str(x1),
'y':str(y1),
'opts':self.routerOpts[name] }
routersToSave.append(nodeToSave)
elif 'Host' in tags:
nodeNum = self.hostOpts[name]['nodeNum']
nodeToSave = {'number':str(nodeNum),
'x':str(x1),
'y':str(y1),
'opts':self.hostOpts[name] }
hostsToSave.append(nodeToSave)
elif 'Switch' in tags:
nodeNum = self.switchOpts[name]['nodeNum']
nodeToSave = {'number':str(nodeNum),
'x':str(x1),
'y':str(y1),
'opts':self.switchOpts[name] }
switchesToSave.append(nodeToSave)
else:
raise Exception( "Cannot create mystery node: " + name )
savingDictionary['hosts'] = hostsToSave
savingDictionary['routers'] = routersToSave
savingDictionary['switches'] = switchesToSave
# Save Links
linksToSave = []
for link in self.links.values():
src = link['src']
dst = link['dest']
linkopts = link['linkOpts']
srcName, dstName = src[ 'text' ], dst[ 'text' ]
linkToSave = {'src':srcName,
'dest':dstName,
'opts':linkopts}
if link['type'] == 'data':
linksToSave.append(linkToSave)
savingDictionary['links'] = linksToSave
# Save Application preferences
#savingDictionary['application'] = self.appPrefs
try:
f = open(fileName, 'wb')
f.write(json.dumps(savingDictionary, sort_keys=True, indent=4, separators=(',', ': ')))
except Exception as er:
print er
finally:
f.close()
# Generic canvas handler
#
# We could have used bindtags, as in nodeIcon, but
# the dynamic approach used here
# may actually require less code. In any case, it's an
# interesting introspection-based alternative to bindtags.
def canvasHandle( self, eventName, event ):
"Generic canvas event handler"
if self.active is None:
return
toolName = self.active
handler = getattr( self, eventName + toolName, None )
if handler is not None:
handler( event )
def clickCanvas( self, event ):
"Canvas click handler."
self.canvasHandle( 'click', event )
def dragCanvas( self, event ):
"Canvas drag handler."
self.canvasHandle( 'drag', event )
def releaseCanvas( self, event ):
"Canvas mouse up handler."
self.canvasHandle( 'release', event )
# Currently the only items we can select directly are
# links. Nodes are handled by bindings in the node icon.
def findItem( self, x, y ):
"Find items at a location in our canvas."
items = self.canvas.find_overlapping( x, y, x, y )
if len( items ) == 0:
return None
else:
return items[ 0 ]
# Canvas bindings for Select, Host, Router and Link tools
def clickSelect( self, event ):
"Select an item."
self.selectItem( self.findItem( event.x, event.y ) )
def deleteItem( self, item ):
"Delete an item."
# Don't delete while network is running
if self.buttons[ 'Select' ][ 'state' ] == 'disabled':
return
# Delete from model
if item in self.links:
self.deleteLink( item )
if item in self.itemToWidget:
self.deleteNode( item )
# Delete from view
self.canvas.delete( item )
def deleteSelection( self, _event ):
"Delete the selected item."
if self.selection is not None:
self.deleteItem( self.selection )
self.selectItem( None )
def clearPopups(self):
print 'Entrou funcao clear_popups'
if isHostPopup == True:
print 'Hostpopup = true'
self.hostPopup.unpost
isHostPopup = False
#if isRouterPopup == True
#if isLinkPopup == True
def nodeIcon( self, node, name ):
"Create a new node icon."
icon = Button( self.canvas, image=self.images[ node ],
text=name, compound='top' )
# Unfortunately bindtags wants a tuple
bindtags = [ str( self.nodeBindings ) ]
bindtags += list( icon.bindtags() )
icon.bindtags( tuple( bindtags ) )
return icon
def newNode( self, node, event ):
"Add a new node to our canvas."
c = self.canvas
x, y = c.canvasx( event.x ), c.canvasy( event.y )
name = self.nodePrefixes[ node ]
if 'LegacyRouter' == node:
self.routerCount += 1
name = self.nodePrefixes[ node ] + str( self.routerCount )
self.routerOpts[name] = {}
self.routerOpts[name]['nodeNum']=self.routerCount
self.routerOpts[name]['hostname']=name
self.routerOpts[name]['nodeType']='legacyRouter'
if 'Host' == node:
self.hostCount += 1
name = self.nodePrefixes[ node ] + str( self.hostCount )
self.hostOpts[name] = {'sched':'host'}
self.hostOpts[name]['nodeNum']=self.hostCount
self.hostOpts[name]['hostname']=name
if 'Switch' == node:
self.switchCount += 1
name = self.nodePrefixes[ node ] + str( self.switchCount )
self.switchOpts[name] = {}
self.switchOpts[name]['nodeNum']=self.switchCount
self.switchOpts[name]['hostname']=name
icon = self.nodeIcon( node, name )
item = self.canvas.create_window( x, y, anchor='c', window=icon,
tags=node )
self.widgetToItem[ icon ] = item
self.itemToWidget[ item ] = icon
self.selectItem( item )
icon.links = {}
if 'LegacyRouter' == node:
icon.bind('<Button-3>', self.do_legacyRouterPopup )
if 'Host' == node:
icon.bind('<Button-3>', self.do_hostPopup )
def clickHost( self, event ):
"Add a new host to our canvas."
self.newNode( 'Host', event )
def clickSwitch( self, event ):
"Add a new switch to the canvas."
self.newNode( 'Switch', event )
def clickLegacyRouter( self, event ):
"Add a new router to our canvas."
self.newNode( 'LegacyRouter', event )
def dragNetLink( self, event ):
"Drag a link's endpoint to another node."
if self.link is None:
return
# Since drag starts in widget, we use root coords
x = self.canvasx( event.x_root )
y = self.canvasy( event.y_root )
c = self.canvas
c.coords( self.link, self.linkx, self.linky, x, y )
def releaseNetLink( self, _event ):
"Give up on the current link."
if self.link is not None:
self.canvas.delete( self.link )
self.linkWidget = self.linkItem = self.link = None
# Generic node handlers
def createNodeBindings( self ):
"Create a set of bindings for nodes."
bindings = {
'<ButtonPress-1>': self.clickNode,
'<B1-Motion>': self.dragNode,
'<ButtonRelease-1>': self.releaseNode,
'<Enter>': self.enterNode,
'<Leave>': self.leaveNode
}
l = Label() # lightweight-ish owner for bindings
for event, binding in bindings.items():
l.bind( event, binding )
return l
def selectItem( self, item ):
"Select an item and remember old selection."
self.lastSelection = self.selection
self.selection = item
def enterNode( self, event ):
"Select node on entry."
self.selectNode( event )
def leaveNode( self, _event ):
"Restore old selection on exit."
self.selectItem( self.lastSelection )
def clickNode( self, event ):
"Node click handler."
if self.active is 'NetLink':
self.startLink( event )
else:
self.selectNode( event )
return 'break'
def dragNode( self, event ):
"Node drag handler."
if self.active is 'NetLink':
self.dragNetLink( event )
else:
self.dragNodeAround( event )
def releaseNode( self, event ):
"Node release handler."
if self.active is 'NetLink':
self.finishLink( event )
# Specific node handlers
def selectNode( self, event ):
"Select the node that was clicked on."
item = self.widgetToItem.get( event.widget, None )
self.selectItem( item )
def dragNodeAround( self, event ):
"Drag a node around on the canvas."
c = self.canvas
# Convert global to local coordinates;
# Necessary since x, y are widget-relative
x = self.canvasx( event.x_root )
y = self.canvasy( event.y_root )
w = event.widget
# Adjust node position
item = self.widgetToItem[ w ]
c.coords( item, x, y )
# Adjust link positions
for dest in w.links:
link = w.links[ dest ]
item = self.widgetToItem[ dest ]
x1, y1 = c.coords( item )
c.coords( link, x, y, x1, y1 )
self.updateScrollRegion()
def createDataLinkBindings( self ):
"Create a set of bindings for nodes."
# Link bindings
# Selection still needs a bit of work overall
# Callbacks ignore event
def select( _event, link=self.link ):
"Select item on mouse entry."
self.selectItem( link )
def highlight( _event, link=self.link ):
"Highlight item on mouse entry."
self.selectItem( link )
self.canvas.itemconfig( link, fill='green' )
def unhighlight( _event, link=self.link ):
"Unhighlight item on mouse exit."
self.canvas.itemconfig( link, fill='blue' )
#self.selectItem( None )
self.canvas.tag_bind( self.link, '<Enter>', highlight )
self.canvas.tag_bind( self.link, '<Leave>', unhighlight )
self.canvas.tag_bind( self.link, '<ButtonPress-1>', select )
self.canvas.tag_bind( self.link, '<Button-3>', self.do_linkPopup )
def startLink( self, event ):
"Start a new link."
if event.widget not in self.widgetToItem:
# Didn't click on a node
return
w = event.widget
item = self.widgetToItem[ w ]
x, y = self.canvas.coords( item )
self.link = self.canvas.create_line( x, y, x, y, width=4,
fill='blue', tag='link' )
self.linkx, self.linky = x, y
self.linkWidget = w
self.linkItem = item
def finishLink( self, event ):
"Finish creating a link"
if self.link is None:
return
source = self.linkWidget
c = self.canvas
# Since we dragged from the widget, use root coords
x, y = self.canvasx( event.x_root ), self.canvasy( event.y_root )
target = self.findItem( x, y )
dest = self.itemToWidget.get( target, None )
if ( source is None or dest is None or source == dest
or dest in source.links or source in dest.links ):
self.releaseNetLink( event )
return
# For now, don't allow hosts to be directly linked
stags = self.canvas.gettags( self.widgetToItem[ source ] )
dtags = self.canvas.gettags( target )
#if (('Host' in stags and 'Host' in dtags)):
#self.releaseNetLink( event )
#return
# Set link type
linkType='data'
self.createDataLinkBindings()
c.itemconfig(self.link, tags=c.gettags(self.link)+(linkType,))
x, y = c.coords( target )
c.coords( self.link, self.linkx, self.linky, x, y )
self.addLink( source, dest, linktype=linkType )
# We're done
self.link = self.linkWidget = None
# Menu handlers
def about( self ):
"Display about box."
about = self.aboutBox
if about is None:
bg = 'white'
about = Toplevel( bg='white' )
about.title( 'About' )
info = self.appName + ': a simple network editor for MiniNDN - based on Miniedit'
warning = 'Development version - not entirely functional!'
#version = 'MiniEdit '+MINIEDIT_VERSION
author = 'Vince Lehman, Jan 2015'
author2 = 'Ashlesh Gawande, Jan 2015'
author3 = 'Carlos Cabral, Jan 2013'
author4 = 'Caio Elias, Nov 2014'
author5 = 'Originally by: Bob Lantz <rlantz@cs>, April 2010'
enhancements = 'Enhancements by: Gregory Gee, Since July 2013'
www = 'http://gregorygee.wordpress.com/category/miniedit/'
line1 = Label( about, text=info, font='Helvetica 10 bold', bg=bg )
line2 = Label( about, text=warning, font='Helvetica 9', bg=bg )
line3 = Label( about, text=author, font='Helvetica 9', bg=bg )
line4 = Label( about, text=author2, font='Helvetica 9', bg=bg )
line5 = Label( about, text=author3, font='Helvetica 9', bg=bg )
line6 = Label( about, text=author4, font='Helvetica 9', bg=bg )
line7 = Label( about, text=author5, font='Helvetica 9', bg=bg )
line8 = Label( about, text=enhancements, font='Helvetica 9', bg=bg )
line9 = Entry( about, font='Helvetica 9', bg=bg, width=len(www), justify=CENTER )
line9.insert(0, www)
line9.configure(state='readonly')
line1.pack( padx=20, pady=10 )
line2.pack(pady=10 )
line3.pack(pady=10 )
line4.pack(pady=10 )
line5.pack(pady=10 )
line6.pack(pady=10 )
line7.pack(pady=10 )
line8.pack(pady=10 )
line9.pack(pady=10 )
hide = ( lambda about=about: about.withdraw() )
self.aboutBox = about
# Hide on close rather than destroying window
Wm.wm_protocol( about, name='WM_DELETE_WINDOW', func=hide )
# Show (existing) window
about.deiconify()
def createToolImages( self ):
"Create toolbar (and icon) images."
def hostDetails( self, _ignore=None ):
if ( self.selection is None or
self.net is not None or
self.selection not in self.itemToWidget ):
return
widget = self.itemToWidget[ self.selection ]
name = widget[ 'text' ]
tags = self.canvas.gettags( self.selection )
#print tags
if 'Host' in tags:
prefDefaults = self.hostOpts[name]
hostBox = HostDialog(self, title='Host Details', prefDefaults=prefDefaults, isRouter='False')
self.master.wait_window(hostBox.top)
if hostBox.result:
newHostOpts = {'nodeNum':self.hostOpts[name]['nodeNum']}
if len(hostBox.result['startCommand']) > 0:
newHostOpts['startCommand'] = hostBox.result['startCommand']
if hostBox.result['cpu']:
newHostOpts['cpu'] = hostBox.result['cpu']
if hostBox.result['mem']:
newHostOpts['mem'] = hostBox.result['mem']
if len(hostBox.result['hostname']) > 0:
newHostOpts['hostname'] = hostBox.result['hostname']
name = hostBox.result['hostname']
widget[ 'text' ] = name
if len(hostBox.result['cache']) > 0:
newHostOpts['cache'] = hostBox.result['cache']
newHostOpts['nlsr'] = hostBox.nlsrFrame.getValues()
newHostOpts['nfd'] = hostBox.nfdFrame.getValues()
self.hostOpts[name] = newHostOpts
print 'New host details for ' + name + ' = ' + str(newHostOpts)
elif 'LegacyRouter' in tags:
prefDefaults = self.routerOpts[name]
hostBox = HostDialog(self, title='Router Details', prefDefaults=prefDefaults, isRouter='True')
self.master.wait_window(hostBox.top)
if hostBox.result:
newRouterOpts = {'nodeNum':self.routerOpts[name]['nodeNum']}
if hostBox.result['cpu']:
newRouterOpts['cpu'] = hostBox.result['cpu']
if hostBox.result['mem']:
newRouterOpts['mem'] = hostBox.result['mem']
if len(hostBox.result['hostname']) > 0:
newRouterOpts['hostname'] = hostBox.result['hostname']
name = hostBox.result['hostname']
widget[ 'text' ] = name
if len(hostBox.result['cache']) > 0:
newRouterOpts['cache'] = hostBox.result['cache']
self.routerOpts[name] = newRouterOpts
print 'New host details for ' + name + ' = ' + str(newRouterOpts)
def linkDetails( self, _ignore=None ):
if ( self.selection is None or
self.net is not None):
return
link = self.selection
linkDetail = self.links[link]
src = linkDetail['src']
dest = linkDetail['dest']
linkopts = linkDetail['linkOpts']
linkBox = LinkDialog(self, title='Link Details', linkDefaults=linkopts)
if linkBox.result is not None:
linkDetail['linkOpts'] = linkBox.result
print 'New link details = ' + str(linkBox.result)
# Model interface
#
# Ultimately we will either want to use a topo or
# mininet object here, probably.
def addLink( self, source, dest, linktype='data', linkopts={} ):
"Add link to model."
source.links[ dest ] = self.link
dest.links[ source ] = self.link
self.links[ self.link ] = {'type' :linktype,
'src':source,
'dest':dest,
'linkOpts':linkopts}
def deleteLink( self, link ):
"Delete link from model."
pair = self.links.get( link, None )
if pair is not None:
source=pair['src']
dest=pair['dest']
del source.links[ dest ]
del dest.links[ source ]
stags = self.canvas.gettags( self.widgetToItem[ source ] )
dtags = self.canvas.gettags( self.widgetToItem[ dest ] )
ltags = self.canvas.gettags( link )
if link is not None:
del self.links[ link ]
def deleteNode( self, item ):
"Delete node (and its links) from model."
widget = self.itemToWidget[ item ]
tags = self.canvas.gettags(item)
for link in widget.links.values():
# Delete from view and model
self.deleteItem( link )
del self.itemToWidget[ item ]
del self.widgetToItem[ widget ]
def do_linkPopup(self, event):
# display the popup menu
if ( self.net is None ):
try:
self.linkPopup.tk_popup(event.x_root, event.y_root)
finally:
# make sure to release the grab (Tk 8.0a1 only)
self.linkPopup.grab_release()
def do_legacyRouterPopup(self, event):
# display the popup menu
if ( self.net is None ):
try:
self.legacyRouterPopup.tk_popup(event.x_root, event.y_root)
finally:
# make sure to release the grab (Tk 8.0a1 only)
self.legacyRouterPopup.grab_release()
def do_hostPopup(self, event):
# display the popup menu
if ( self.net is None ):
try:
self.hostPopup.tk_popup(event.x_root, event.y_root)
isHostPopup = True
finally:
# make sure to release the grab (Tk 8.0a1 only)
self.hostPopup.grab_release()
def xterm( self, _ignore=None ):
"Make an xterm when a button is pressed."
if ( self.selection is None or
self.net is None or
self.selection not in self.itemToWidget ):
return
name = self.itemToWidget[ self.selection ][ 'text' ]
if name not in self.net.nameToNode:
return
term = makeTerm( self.net.nameToNode[ name ], 'Host', term=self.appPrefs['terminalType'] )
if StrictVersion(MININET_VERSION) > StrictVersion('2.0'):
self.net.terms += term
else:
self.net.terms.append(term)
def iperf( self, _ignore=None ):
"Make an xterm when a button is pressed."
if ( self.selection is None or
self.net is None or
self.selection not in self.itemToWidget ):
return
name = self.itemToWidget[ self.selection ][ 'text' ]
if name not in self.net.nameToNode:
return
self.net.nameToNode[ name ].cmd( 'iperf -s -p 5001 &' )
""" BELOW HERE IS THE TOPOLOGY IMPORT CODE """
def parseArgs( self ):
"""Parse command-line args and return options object.
returns: opts parse options dict"""
if '--custom' in sys.argv:
index = sys.argv.index( '--custom' )
if len( sys.argv ) > index + 1:
filename = sys.argv[ index + 1 ]
self.parseCustomFile( filename )
else:
raise Exception( 'Custom file name not found' )
desc = ( "The %prog utility creates Minindn network from the\n"
"command line. It can create parametrized topologies,\n"
"invoke the Minindn CLI, and run tests." )
usage = ( '%prog [options] [template_file]\n'
'\nIf no template_file is given, generated template will be written to the file minindn.conf in the current directory.\n'
'Type %prog -h for details)' )
opts = OptionParser( description=desc, usage=usage )
addDictOption( opts, TOPOS, TOPODEF, 'topo' )
addDictOption( opts, LINKS, LINKDEF, 'link' )
opts.add_option( '--custom', type='string', default=None,
help='read custom topo and node params from .py' +
'file' )
self.options, self.args = opts.parse_args()
# We don't accept extra arguments after the options
if self.args:
if len(self.args) > 1:
opts.print_help()
exit()
else:
self.template_file=self.args[0]
def setCustom( self, name, value ):
"Set custom parameters for MininetRunner."
if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
# Update dictionaries
param = name.upper()
globals()[ param ].update( value )
elif name == 'validate':
# Add custom validate function
self.validate = value
else:
# Add or modify global variable or class
globals()[ name ] = value
def parseCustomFile( self, fileName ):
"Parse custom file and add params before parsing cmd-line options."
customs = {}
if os.path.isfile( fileName ):
execfile( fileName, customs, customs )
for name, val in customs.iteritems():
self.setCustom( name, val )
else:
raise Exception( 'could not find custom file: %s' % fileName )
def importTopo( self ):
print 'topo='+self.options.topo
if self.options.topo == 'none':
return
self.newTopology()
topo = buildTopo( TOPOS, self.options.topo )
link = customConstructor( LINKS, self.options.link )
importNet = Mininet(topo=topo, build=False, link=link)
importNet.build()
c = self.canvas
rowIncrement = 100
currentY = 100
# Add switches
print 'switches:'+str(len(importNet.switches))
columnCount = 0
for switch in importNet.switches:
name = switch.name
self.switchOpts[name] = {}
self.switchOpts[name]['nodeNum']=self.switchCount
self.switchOpts[name]['hostname']=name
self.switchOpts[name]['switchType']='default'
self.switchOpts[name]['controllers']=[]
x = columnCount*100+100
self.addNode('Switch', self.switchCount,
float(x), float(currentY), name=name)
icon = self.findWidgetByName(name)
icon.bind('<Button-3>', self.do_switchPopup )
if columnCount == 9:
columnCount = 0
currentY = currentY + rowIncrement
else:
columnCount =columnCount+1
currentY = currentY + rowIncrement
# Add hosts
print 'hosts:'+str(len(importNet.hosts))
columnCount = 0
for host in importNet.hosts:
name = host.name
self.hostOpts[name] = {'sched':'host'}
self.hostOpts[name]['nodeNum']=self.hostCount
self.hostOpts[name]['hostname']=name
#self.hostOpts[name]['ip']=host.IP()
x = columnCount*100+100
self.addNode('Host', self.hostCount,
float(x), float(currentY), name=name)
icon = self.findWidgetByName(name)
icon.bind('<Button-3>', self.do_hostPopup )
if columnCount == 9:
columnCount = 0
currentY = currentY + rowIncrement
else:
columnCount =columnCount+1
print 'links:'+str(len(topo.links()))
#[('h1', 's3'), ('h2', 's4'), ('s3', 's4')]
for link in topo.links():
print str(link)
srcNode = link[0]
src = self.findWidgetByName(srcNode)
sx, sy = self.canvas.coords( self.widgetToItem[ src ] )
destNode = link[1]
dest = self.findWidgetByName(destNode)
dx, dy = self.canvas.coords( self.widgetToItem[ dest] )
params = topo.linkInfo( srcNode, destNode )
print 'Link Parameters='+str(params)
self.link = self.canvas.create_line( sx, sy, dx, dy, width=4,
fill='blue', tag='link' )
c.itemconfig(self.link, tags=c.gettags(self.link)+('data',))
self.addLink( src, dest, linkopts=params )
self.createDataLinkBindings()
self.link = self.linkWidget = None
importNet.stop()
def miniEditImages():
"Create and return images for MiniEdit."
# Image data. Git will be unhappy. However, the alternative
# is to keep track of separate binary files, which is also
# unappealing.
return {
'Select': BitmapImage(
file='/usr/include/X11/bitmaps/left_ptr' ),
'LegacyRouter': PhotoImage( data=r"""
R0lGODlhMgAYAPcAAAEBAXZ8gQNAgL29vQNctjl/xVSa4j1dfCF+
3QFq1DmL3wJMmAMzZZW11dnZ2SFrtyNdmTSO6gIZMUKa8gJVqEOH
zR9Pf5W74wFjxgFx4jltn+np6Eyi+DuT6qKiohdtwwUPGWiq6ymF
4LHH3Rh11CV81kKT5AMoUA9dq1ap/mV0gxdXlytRdR1ptRNPjTt9
vwNgvwJZsX+69gsXJQFHjTtjizF0tvHx8VOm9z2V736Dhz2N3QM2
acPZ70qe8gFo0HS19wVRnTiR6hMpP0eP1i6J5iNlqAtgtktjfQFu
3TNxryx4xAMTIzOE1XqAh1uf5SWC4AcfNy1XgQJny93n8a2trRh3
12Gt+VGm/AQIDTmByAF37QJasydzvxM/ayF3zhdLf8zLywFdu4i5
6gFlyi2J4yV/1w8wUo2/8j+X8D2Q5Eee9jeR7Uia7DpeggFt2QNP
m97e3jRong9bpziH2DuT7aipqQoVICmG45vI9R5720eT4Q1hs1er
/yVVhwJJktPh70tfdbHP7Xev5xs5V7W1sz9jhz11rUVZcQ9WoCVV
hQk7cRdtwWuw9QYOFyFHbSBnr0dznxtWkS18zKfP9wwcLAMHCwFF
iS5UeqGtuRNNiwMfPS1hlQMtWRE5XzGM5yhxusLCwCljnwMdOFWh
7cve8pG/7Tlxp+Tr8g9bpXF3f0lheStrrYu13QEXLS1ppTV3uUuR
1RMjNTF3vU2X4TZupwRSolNne4nB+T+L2YGz4zJ/zYe99YGHjRdD
cT95sx09XQldsgMLEwMrVc/X3yN3yQ1JhTRbggsdMQNfu9HPz6Wl
pW2t7RctQ0GFyeHh4dvl8SBZklCb5kOO2kWR3Vmt/zdjkQIQHi90
uvPz8wIVKBp42SV5zbfT7wtXpStVfwFWrBVvyTt3swFz5kGBv2+1
/QlbrVFjdQM7d1+j54i67UmX51qn9i1vsy+D2TuR5zddhQsjOR1t
u0GV6ghbsDVZf4+76RRisent8Xd9hQFBgwFNmwJLlcPDwwFr1z2T
5yH5BAEAAAAALAAAAAAyABgABwj/AAEIHEiQYJY7Qwg9UsTplRIb
ENuxEiXJgpcz8e5YKsixY8Essh7JcbbOBwcOa1JOmJAmTY4cHeoI
abJrCShI0XyB8YRso0eOjoAdWpciBZajJ1GuWcnSZY46Ed5N8hPA
TqEBoRB9gVJsxRlhPwHI0kDkVywcRpGe9LF0adOnMpt8CxDnxg1o
9lphKoEACoIvmlxxvHOKVg0n/Tzku2WoVoU2J1P6WNkSrtwADuxC
G/MOjwgRUEIjGG3FhaOBzaThiDSCil27G8Isc3LLjZwXsA6YYJmD
jhTMmseoKQIFDx7RoxHo2abnwygAlUj1mV6tWjlelEpRwfd6gzI7
VeJQ/2vZoVaDUqigqftXpH0R46H9Kl++zUo4JnKq9dGvv09RHFhc
IUMe0NiFDyql0OJUHWywMc87TXRhhCRGiHAccvNZUR8JxpDTH38p
9HEUFhxgMSAvjbBjQge8PSXEC6uo0IsHA6gAAShmgCbffNtsQwIJ
ifhRHX/TpUUiSijlUk8AqgQixSwdNBjCa7CFoVggmEgCyRf01WcF
CYvYUgB104k4YlK5HONEXXfpokYdMrXRAzMhmNINNNzB9p0T57Ag
yZckpKKPGFNgw06ZWKR10jTw6MAmFWj4AJcQQkQQwSefvFeGCemM
IQggeaJywSQ/wgHOAmJskQEfWqBlFBEH1P/QaGY3QOpDZXA2+A6m
7hl3IRQKGDCIAj6iwE8yGKC6xbJv8IHNHgACQQybN2QiTi5NwdlB
pZdiisd7vyanByOJ7CMGGRhgwE+qyy47DhnBPLDLEzLIAEQjBtCh
RmVPNWgpr+Be+Nc9icARww9TkIEuDAsQ0O7DzGIQzD2QdDEJHTsI
AROc3F7qWQncyHPPHN5QQAAG/vjzw8oKp8sPPxDH3O44/kwBQzLB
xBCMOTzzHEMMBMBARgJvZJBBEm/4k0ACKydMBgwYoKNNEjJXbTXE
42Q9jtFIp8z0Dy1jQMA1AGziz9VoW7310V0znYDTGMQgwUDXLDBO
2nhvoTXbbyRk/XXL+pxWkAT8UJ331WsbnbTSK8MggDZhCTOMLQkc
jvXeSPedAAw0nABWWARZIgEDfyTzxt15Z53BG1PEcEknrvgEelhZ
MDHKCTwI8EcQFHBBAAFcgGPLHwLwcMIo12Qxu0ABAQA7
"""),
'Host': PhotoImage( data=r"""
R0lGODlhIAAYAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
/8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
/2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
ACH5BAEAAAAALAAAAAAgABgAAAiNAAH8G0iwoMGDCAcKTMiw4UBw
BPXVm0ixosWLFvVBHFjPoUeC9Tb+6/jRY0iQ/8iVbHiS40CVKxG2
HEkQZsyCM0mmvGkw50uePUV2tEnOZkyfQA8iTYpTKNOgKJ+C3AhO
p9SWVaVOfWj1KdauTL9q5UgVbFKsEjGqXVtP40NwcBnCjXtw7tx/
C8cSBBAQADs=
""" ),
'Switch': PhotoImage( data=r"""
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
4BE2eIRYeHAEIBwBP0Y4Qn41YWRSCQgAOw==
""" ),
'NetLink': PhotoImage( data=r"""
R0lGODlhFgAWAPcAMf//////zP//mf//Zv//M///AP/M///MzP/M
mf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9m
Zv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8A
M/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zM
AMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz
/8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/
zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZ
mZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkz
ZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/
M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZ
AGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA
/2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPM
zDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNm
mTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMA
ZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM
MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBm
AAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAM+4AAN0A
ALsAAKoAAIgAAHcAAFUAAEQAACIAABEAAADuAADdAAC7AACqAACI
AAB3AABVAABEAAAiAAARAAAA7gAA3QAAuwAAqgAAiAAAdwAAVQAA
RAAAIgAAEe7u7t3d3bu7u6qqqoiIiHd3d1VVVURERCIiIhEREQAA
ACH5BAEAAAAALAAAAAAWABYAAAhIAAEIHEiwoEGBrhIeXEgwoUKG
Cx0+hGhQoiuKBy1irChxY0GNHgeCDAlgZEiTHlFuVImRJUWXEGEy
lBmxI8mSNknm1Dnx5sCAADs=
""" )
}
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
"""Convenience function to add choices dicts to OptionParser.
opts: OptionParser instance
choicesDict: dictionary of valid choices, must include default
default: default choice key
name: long option name
help: string"""
if default not in choicesDict:
raise Exception( 'Invalid default %s for choices dict: %s' %
( default, name ) )
if not helpStr:
helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
'[,param=value...]' )
opts.add_option( '--' + name,
type='string',
default = default,
help = helpStr )
if __name__ == '__main__':
setLogLevel( 'info' )
app = MiniEdit()
""" import topology if specified """
app.parseArgs()
app.importTopo()
global isHostPopup
global isRouterPopup
global isLinkPopup
app.mainloop()