def assignResourceModel(self, resource_model): if self.resource_model is not None: raise Exception( "There is already an resource model assigned to this VI.") self.resource_model = resource_model self.res_table.addResourceModel(self, resource_model) info("Assigned RM: %r to VI: %r\n" % (self.resource_model, self))
def run(self): "Run our cmdloop(), catching KeyboardInterrupt" while True: try: # Make sure no nodes are still waiting for node in self.mn.values(): while node.waiting: info('stopping', node, '\n') node.sendInt() node.waitOutput() if self.isatty(): quietRun('stty echo sane intr ^C') self.cmdloop() break except KeyboardInterrupt: # Output a message - unless it's also interrupted # pylint: disable=broad-except try: output('\nInterrupt\n') except Exception: pass # pylint: enable=broad-except except Exception as e: info("Exiting cmd loop due to error.\n") self.mn.stop() raise
def addSAPNAT(self, SAPSwitch): """ Add NAT to the Containernet, so external SAPs can reach the outside internet through the host :param SAPSwitch: Instance of the external SAP switch :param SAPNet: Subnet of the external SAP as str (eg. '10.10.1.0/30') :return: """ SAPip = SAPSwitch.ip SAPNet = str(ipaddress.IPv4Network(unicode(SAPip), strict=False)) # due to a bug with python-iptables, removing and finding rules does not succeed when the mininet CLI is running # so we use the iptables tool # create NAT rule rule0_ = "iptables -t nat -A POSTROUTING ! -o {0} -s {1} -j MASQUERADE".format(SAPSwitch.deployed_name, SAPNet) p = Popen(shlex.split(rule0_)) p.communicate() # create FORWARD rule rule1_ = "iptables -A FORWARD -o {0} -j ACCEPT".format(SAPSwitch.deployed_name) p = Popen(shlex.split(rule1_)) p.communicate() rule2_ = "iptables -A FORWARD -i {0} -j ACCEPT".format(SAPSwitch.deployed_name) p = Popen(shlex.split(rule2_)) p.communicate() info("added SAP NAT rules for: {0} - {1}\n".format(SAPSwitch.name, SAPNet))
def removeExtSAP(self, sapName): SAPswitch = self.SAPswitches[sapName] info('stopping external SAP:' + SAPswitch.name + ' \n') SAPswitch.stop() SAPswitch.terminate() self.removeSAPNAT(SAPswitch)
def waitListening(client=None, server='127.0.0.1', port=80, timeout=None): """Wait until server is listening on port. returns True if server is listening""" runCmd = (client.cmd if client else partial(quietRun, shell=True)) if not runCmd('which telnet'): raise Exception('Could not find telnet') # pylint: disable=maybe-no-member serverIP = server if isinstance(server, basestring) else server.IP() cmd = ('echo A | telnet -e A %s %s' % (serverIP, port)) time = 0 result = runCmd(cmd) while 'Connected' not in result: if 'No route' in result: rtable = runCmd('route') error('no route to %s:\n%s' % (server, rtable)) return False if timeout and time >= timeout: error('could not connect to %s on port %d\n' % (server, port)) return False debug('waiting for', server, 'to listen on port', port, '\n') info('.') sleep(.5) time += .5 result = runCmd(cmd) return True
def moduleDeps(subtract=None, add=None): """Handle module dependencies. subtract: string or list of module names to remove, if already loaded add: string or list of module names to add, if not already loaded""" subtract = subtract if subtract is not None else [] add = add if add is not None else [] if isinstance(subtract, basestring): subtract = [subtract] if isinstance(add, basestring): add = [add] for mod in subtract: if mod in lsmod(): info('*** Removing ' + mod + '\n') rmmodOutput = rmmod(mod) if rmmodOutput: error('Error removing ' + mod + ': "%s">\n' % rmmodOutput) exit(1) if mod in lsmod(): error('Failed to remove ' + mod + '; still there!\n') exit(1) for mod in add: if mod not in lsmod(): info('*** Loading ' + mod + '\n') modprobeOutput = modprobe(mod) if modprobeOutput: error('Error inserting ' + mod + ' - is it installed and available via modprobe?\n' + 'Error was: "%s"\n' % modprobeOutput) if mod not in lsmod(): error('Failed to insert ' + mod + ' - quitting.\n') exit(1) else: debug('*** ' + mod + ' already loaded\n')
def stop(self): super(Containernet, self).stop() info('*** Removing NAT rules of %i SAPs\n' % len(self.SAPswitches)) for SAPswitch in self.SAPswitches: self.removeSAPNAT(self.SAPswitches[SAPswitch]) info("\n")
def run(self, test, *args, **kwargs): "Perform a complete start/test/stop cycle." self.start() info('*** Running test\n') result = test(*args, **kwargs) self.stop() return result
def placeNodes( self ): """Place nodes on servers (if they don't have a server), and start shell processes""" if not self.servers or not self.topo: # No shirt, no shoes, no service return nodes = self.topo.nodes() placer = self.placement( servers=self.servers, nodes=self.topo.nodes(), hosts=self.topo.hosts(), switches=self.topo.switches(), links=self.topo.links() ) for node in nodes: config = self.topo.nodeInfo( node ) # keep local server name consistent accross nodes if 'server' in config.keys() and config[ 'server' ] is None: config[ 'server' ] = 'localhost' server = config.setdefault( 'server', placer.place( node ) ) if server: config.setdefault( 'serverIP', self.serverIP[ server ] ) info( '%s:%s ' % ( node, server ) ) key = ( None, server ) _dest, cfile, _conn = self.connections.get( key, ( None, None, None ) ) if cfile: config.setdefault( 'controlPath', cfile )
def errRun(*cmd, **kwargs): """Run a command and return stdout, stderr and return code cmd: string or list of command and args stderr: STDOUT to merge stderr with stdout shell: run command using shell echo: monitor output to console""" # By default we separate stderr, don't run in a shell, and don't echo stderr = kwargs.get('stderr', PIPE) shell = kwargs.get('shell', False) echo = kwargs.get('echo', False) if echo: # cmd goes to stderr, output goes to stdout info(cmd, '\n') if len(cmd) == 1: cmd = cmd[0] # Allow passing in a list or a string if isinstance(cmd, str) and not shell: cmd = cmd.split(' ') cmd = [str(arg) for arg in cmd] elif isinstance(cmd, list) and shell: cmd = " ".join(arg for arg in cmd) debug('*** errRun:', cmd, '\n') popen = Popen(cmd, stdout=PIPE, stderr=stderr, shell=shell) # We use poll() because select() doesn't work with large fd numbers, # and thus communicate() doesn't work either out, err = '', '' poller = poll() poller.register(popen.stdout, POLLIN) fdtofile = {popen.stdout.fileno(): popen.stdout} outDone, errDone = False, True if popen.stderr: fdtofile[popen.stderr.fileno()] = popen.stderr poller.register(popen.stderr, POLLIN) errDone = False while not outDone or not errDone: readable = poller.poll() for fd, event in readable: f = fdtofile[fd] if event & POLLIN: data = f.read(1024) if echo: output(data) if f == popen.stdout: out += data if data == '': outDone = True elif f == popen.stderr: err += data if data == '': errDone = True else: # POLLHUP or something unexpected if f == popen.stdout: outDone = True elif f == popen.stderr: errDone = True poller.unregister(fd) returncode = popen.wait() debug(out, err, returncode) return out, err, returncode
def _update_hosts_resources(self): for node in self.topo.hosts(): host = self.exp.get(node) try: resources = self.topo.calculateRealResources(node) host.update_resources(**resources) except Exception as err: info(err)
def batchShutdown( cls, switches, **_kwargs ): "Stop switches in per-server batches" key = attrgetter( 'server' ) for server, switchGroup in groupby( sorted( switches, key=key ), key ): info( '(%s)' % server ) group = tuple( switchGroup ) switch = group[ 0 ] OVSSwitch.batchShutdown( group, run=switch.rcmd ) return switches
def startTerms(self): "Start a terminal for each node." if 'DISPLAY' not in os.environ: error("Error starting terms: Cannot connect to display\n") return info("*** Running terms on %s\n" % os.environ['DISPLAY']) cleanUpScreens() self.terms += makeTerms(self.controllers, 'controller') self.terms += makeTerms(self.switches, 'switch') self.terms += makeTerms(self.hosts, 'host')
def addResourceModel(self, vi, rm): if vi in self._resource_models: raise Exception( "There is already an resource model assigned to this VI.\n") self._resource_models[vi] = rm rm.setResourcesTable(self) rm.vis.append(vi) info("Network Resources: added resource model: %r\n" % rm)
def __init__(self, emulation_max_cpu=1.0, emulation_max_mem=2048): self.e_cpu = emulation_max_cpu self.e_mem = emulation_max_mem self._resource_models = dict() self._allocated_resources = dict() self._device_to_vi = dict() info("Network Resources initiated with max_cpu=%r and max_mem=%r\n" % (self.e_cpu, self.e_mem))
def provideResources(self, vi, d, resources): if vi in self._allocated_resources: self._allocated_resources[vi][d] = resources else: self._allocated_resources[vi] = {d: resources} self._device_to_vi[d] = vi info("Resources %s provided to device %s in VI %s\n" % (resources, d, vi))
def build(self): "Build mininet." if self.topo: self.buildFromTopo(self.topo) if self.inNamespace: self.configureControlNetwork() info('*** Configuring hosts\n') self.configHosts() if self.xterms: self.startTerms() if self.autoStaticArp: self.staticArp() self.built = True
def __init__( self, *args, **kwargs ): Placer.__init__( self, *args, **kwargs ) # Calculate bin sizes scount = len( self.servers ) self.hbin = max( int( len( self.hosts ) / scount ), 1 ) self.sbin = max( int( len( self.switches ) / scount ), 1 ) self.cbin = max( int( len( self.controllers ) / scount ), 1 ) info( 'scount:', scount ) info( 'bins:', self.hbin, self.sbin, self.cbin, '\n' ) self.servdict = dict( enumerate( self.servers ) ) self.hset = frozenset( self.hosts ) self.sset = frozenset( self.switches ) self.cset = frozenset( self.controllers ) self.hind, self.sind, self.cind = 0, 0, 0
def addVirtualInstance(self, label): if label in self.vinsts: raise Exception('Virtual Instance label already exists: %s' % label) if ' ' in label: label = self.removeSpace(label) info("Replacing label empty spaces, new label: %s" % label) vi = VirtualInstance(label, self) self.vinsts[label] = vi info("added virtual instance: %s\n" % label) return vi
def testHostWithPrivateDirs(): "Test bind mounts" topo = SingleSwitchTopo( 10 ) privateDirs = [ ( '/var/log', '/tmp/%(name)s/var/log' ), ( '/var/run', '/tmp/%(name)s/var/run' ), '/var/mn' ] host = partial( Host, privateDirs=privateDirs ) net = Mininet( topo=topo, host=host ) net.start() directories = [ directory[ 0 ] if isinstance( directory, tuple ) else directory for directory in privateDirs ] info( 'Private Directories:', directories, '\n' ) CLI( net ) net.stop()
def place( self, nodename ): """Simple placement algorithm: place nodes into evenly sized bins""" # Place nodes into bins if nodename in self.hset: server = self.servdict[ self.hind / self.hbin ] self.hind += 1 elif nodename in self.sset: server = self.servdict[ self.sind / self.sbin ] self.sind += 1 elif nodename in self.cset: server = self.servdict[ self.cind / self.cbin ] self.cind += 1 else: info( 'warning: unknown node', nodename ) server = self.servdict[ 0 ] return server
def configHosts(self): "Configure a set of hosts." for host in self.hosts: info(host.name + ' ') intf = host.defaultIntf() if intf: host.configDefault() else: # Don't configure nonexistent intf host.configDefault(ip=None, mac=None) # You're low priority, dude! # BL: do we want to do this here or not? # May not make sense if we have CPU lmiting... # quietRun( 'renice +18 -p ' + repr( host.pid ) ) # This may not be the right place to do this, but # it needs to be done somewhere. info('\n')
def runCpuLimitTest(self, cpu, duration=5): """run CPU limit test with 'while true' processes. cpu: desired CPU fraction of each host duration: test duration in seconds (integer) returns a single list of measured CPU fractions as floats. """ cores = int(quietRun('nproc')) pct = cpu * 100 info('*** Testing CPU %.0f%% bandwidth limit\n' % pct) hosts = self.hosts cores = int(quietRun('nproc')) # number of processes to run a while loop on per host num_procs = int(ceil(cores * cpu)) pids = {} for h in hosts: pids[h] = [] for _core in range(num_procs): h.cmd('while true; do a=1; done &') pids[h].append(h.cmd('echo $!').strip()) outputs = {} time = {} # get the initial cpu time for each host for host in hosts: outputs[host] = [] with open('/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r') as f: time[host] = float(f.read()) for _ in range(duration): sleep(1) for host in hosts: with open('/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' % host, 'r') as f: readTime = float(f.read()) outputs[host].append(((readTime - time[host]) / 1000000000) / cores * 100) time[host] = readTime for h, pids in pids.items(): for pid in pids: h.cmd('kill -9 %s' % pid) cpu_fractions = [] for _host, outputs in outputs.items(): for pct in outputs: cpu_fractions.append(pct) output('*** Results: %s\n' % cpu_fractions) return cpu_fractions
def removeSAPNAT(self, SAPSwitch): SAPip = SAPSwitch.ip SAPNet = str(ipaddress.IPv4Network(unicode(SAPip), strict=False)) # due to a bug with python-iptables, removing and finding rules does not succeed when the mininet CLI is running # so we use the iptables tool rule0_ = "iptables -t nat -D POSTROUTING ! -o {0} -s {1} -j MASQUERADE".format(SAPSwitch.deployed_name, SAPNet) p = Popen(shlex.split(rule0_)) p.communicate() rule1_ = "iptables -D FORWARD -o {0} -j ACCEPT".format(SAPSwitch.deployed_name) p = Popen(shlex.split(rule1_)) p.communicate() rule2_ = "iptables -D FORWARD -i {0} -j ACCEPT".format(SAPSwitch.deployed_name) p = Popen(shlex.split(rule2_)) p.communicate() info("remove SAP NAT rules for: {0} - {1}\n".format(SAPSwitch.name, SAPNet))
def config(self, **params): """Configure the NAT and iptables""" super(NAT, self).config(**params) if not self.localIntf: self.localIntf = self.defaultIntf() if self.flush: self.cmd('sysctl net.ipv4.ip_forward=0') self.cmd('iptables -F') self.cmd('iptables -t nat -F') # Create default entries for unmatched traffic self.cmd('iptables -P INPUT ACCEPT') self.cmd('iptables -P OUTPUT ACCEPT') self.cmd('iptables -P FORWARD DROP') # Install NAT rules self.cmd('iptables -I FORWARD', '-i', self.localIntf, '-d', self.subnet, '-j DROP') self.cmd('iptables -A FORWARD', '-i', self.localIntf, '-s', self.subnet, '-j ACCEPT') self.cmd('iptables -A FORWARD', '-o', self.localIntf, '-d', self.subnet, '-j ACCEPT') self.cmd('iptables -t nat -A POSTROUTING', '-s', self.subnet, "'!'", '-d', self.subnet, '-j MASQUERADE') # Instruct the kernel to perform forwarding self.cmd('sysctl net.ipv4.ip_forward=1') # Prevent network-manager from messing with our interface # by specifying manual configuration in /etc/network/interfaces intf = self.localIntf cfile = '/etc/network/interfaces' line = '\niface %s inet manual\n' % intf config = open(cfile).read() if (line) not in config: info('*** Adding "' + line.strip() + '" to ' + cfile + '\n') with open(cfile, 'a') as f: f.write(line) # Probably need to restart network-manager to be safe - # hopefully this won't disconnect you self.cmd('service network-manager restart')
def precheck( self ): """Pre-check to make sure connection works and that we can call sudo without a password""" result = 0 info( '*** Checking servers\n' ) for server in self.servers: ip = self.serverIP[ server ] if not server or server == 'localhost': continue info( server, '' ) dest = '%s@%s' % ( self.user, ip ) cmd = [ 'sudo', '-E', '-u', self.user ] cmd += self.sshcmd + [ '-n', dest, 'sudo true' ] debug( ' '.join( cmd ), '\n' ) _out, _err, code = errRun( cmd ) if code != 0: error( '\nstartConnection: server connection check failed ' 'to %s using command:\n%s\n' % ( server, ' '.join( cmd ) ) ) result |= code if result: error( '*** Server precheck failed.\n' '*** Make sure that the above ssh command works' ' correctly.\n' '*** You may also need to run mn -c on all nodes, and/or\n' '*** use sudo -E.\n' ) sys.exit( 1 ) info( '\n' )
def waitConnected(self, timeout=None, delay=.5): """wait for each switch to connect to a controller, up to 5 seconds timeout: time to wait, or None to wait indefinitely delay: seconds to sleep per iteration returns: True if all switches are connected""" info('*** Waiting for switches to connect\n') time = 0 remaining = list(self.switches) while True: for switch in tuple(remaining): if switch.connected(): info('%s ' % switch) remaining.remove(switch) if not remaining: info('\n') return True if time > timeout and timeout is not None: break sleep(delay) time += delay warn('Timed out after %d seconds\n' % time) for switch in remaining: if not switch.connected(): warn('Warning: %s is not connected to a controller\n' % switch.name) else: remaining.remove(switch) return not remaining
def limit(bw=10, cpu=.1): """Example/test of link and CPU bandwidth limits bw: interface bandwidth limit in Mbps cpu: cpu limit as fraction of overall CPU time""" intf = custom(TCIntf, bw=bw) myTopo = TreeTopo(depth=1, fanout=2) for sched in 'rt', 'cfs': info('*** Testing with', sched, 'bandwidth limiting\n') if sched == 'rt': release = quietRun('uname -r').strip('\r\n') output = quietRun('grep CONFIG_RT_GROUP_SCHED /boot/config-%s' % release) if output == '# CONFIG_RT_GROUP_SCHED is not set\n': info('*** RT Scheduler is not enabled in your kernel. ' 'Skipping this test\n') continue host = custom(CPULimitedHost, sched=sched, cpu=cpu) net = Mininet(topo=myTopo, intf=intf, host=host) net.start() testLinkLimit(net, bw=bw) net.runCpuLimitTest(cpu=cpu) net.stop()
def bwtest(cpuLimits, period_us=100000, seconds=5): """Example/test of link and CPU bandwidth limits cpu: cpu limit as fraction of overall CPU time""" topo = TreeTopo(depth=1, fanout=2) results = {} for sched in 'rt', 'cfs': print '*** Testing with', sched, 'bandwidth limiting' for cpu in cpuLimits: host = custom(CPULimitedHost, sched=sched, period_us=period_us, cpu=cpu) try: net = Mininet(topo=topo, host=host) # pylint: disable=bare-except except: info('*** Skipping host %s\n' % sched) break net.start() net.pingAll() hosts = [net.getNodeByName(h) for h in topo.hosts()] client, server = hosts[0], hosts[-1] server.cmd('iperf -s -p 5001 &') waitListening(client, server, 5001) result = client.cmd('iperf -yc -t %s -c %s' % (seconds, server.IP())).split(',') bps = float(result[-1]) server.cmdPrint('kill %iperf') net.stop() updated = results.get(sched, []) updated += [(cpu, bps)] results[sched] = updated return results
def __init__(self, mininet, stdin=sys.stdin, script=None): """Start and run interactive or batch mode CLI mininet: Mininet network object stdin: standard input for CLI script: script to run in batch mode""" self.mn = mininet # Local variable bindings for py command self.locals = {'net': mininet} # Attempt to handle input self.stdin = stdin self.inPoller = poll() self.inPoller.register(stdin) self.inputFile = script Cmd.__init__(self) # Containernet allows '.' in host identifiers to build human readable hierarchical name spaces: self.identchars = string.ascii_letters + string.digits + '_' + '.' info('*** Starting CLI:\n') if self.inputFile: self.do_source(self.inputFile) return self.initReadline() self.run()