First commit
diff --git a/mininet/node.py b/mininet/node.py
new file mode 100644
index 0000000..4c54d75
--- /dev/null
+++ b/mininet/node.py
@@ -0,0 +1,1235 @@
+"""
+Node objects for Mininet.
+
+Nodes provide a simple abstraction for interacting with hosts, switches
+and controllers. Local nodes are simply one or more processes on the local
+machine.
+
+Node: superclass for all (primarily local) network nodes.
+
+Host: a virtual host. By default, a host is simply a shell; commands
+    may be sent using Cmd (which waits for output), or using sendCmd(),
+    which returns immediately, allowing subsequent monitoring using
+    monitor(). Examples of how to run experiments using this
+    functionality are provided in the examples/ directory.
+
+CPULimitedHost: a virtual host whose CPU bandwidth is limited by
+    RT or CFS bandwidth limiting.
+
+Switch: superclass for switch nodes.
+
+UserSwitch: a switch using the user-space switch from the OpenFlow
+    reference implementation.
+
+KernelSwitch: a switch using the kernel switch from the OpenFlow reference
+    implementation.
+
+OVSSwitch: a switch using the OpenVSwitch OpenFlow-compatible switch
+    implementation (openvswitch.org).
+
+Controller: superclass for OpenFlow controllers. The default controller
+    is controller(8) from the reference implementation.
+
+NOXController: a controller node using NOX (noxrepo.org).
+
+RemoteController: a remote controller node, which may use any
+    arbitrary OpenFlow-compatible controller, and which is not
+    created or managed by mininet.
+
+Future enhancements:
+
+- Possibly make Node, Switch and Controller more abstract so that
+  they can be used for both local and remote nodes
+
+- Create proxy objects for remote nodes (Mininet: Cluster Edition)
+"""
+
+import os
+import re
+import signal
+import select
+from subprocess import Popen, PIPE, STDOUT
+
+from mininet.log import info, error, warn, debug
+from mininet.util import ( quietRun, errRun, errFail, moveIntf, isShellBuiltin,
+                           numCores, retry, mountCgroups )
+from mininet.moduledeps import moduleDeps, pathCheck, OVS_KMOD, OF_KMOD, TUN
+from mininet.link import Link, Intf, TCIntf
+
+class Node( object ):
+    """A virtual network node is simply a shell in a network namespace.
+       We communicate with it using pipes."""
+
+    portBase = 0  # Nodes always start with eth0/port0, even in OF 1.0
+
+    def __init__( self, name, inNamespace=True, **params ):
+        """name: name of node
+           inNamespace: in network namespace?
+           params: Node parameters (see config() for details)"""
+
+        # Make sure class actually works
+        self.checkSetup()
+
+        self.name = name
+        self.inNamespace = inNamespace
+
+        # Stash configuration parameters for future reference
+        self.params = params
+
+        self.intfs = {}  # dict of port numbers to interfaces
+        self.ports = {}  # dict of interfaces to port numbers
+                         # replace with Port objects, eventually ?
+        self.nameToIntf = {}  # dict of interface names to Intfs
+
+        # Make pylint happy
+        ( self.shell, self.execed, self.pid, self.stdin, self.stdout,
+            self.lastPid, self.lastCmd, self.pollOut ) = (
+                None, None, None, None, None, None, None, None )
+        self.waiting = False
+        self.readbuf = ''
+
+        # Start command interpreter shell
+        self.startShell()
+
+    # File descriptor to node mapping support
+    # Class variables and methods
+
+    inToNode = {}  # mapping of input fds to nodes
+    outToNode = {}  # mapping of output fds to nodes
+
+    @classmethod
+    def fdToNode( cls, fd ):
+        """Return node corresponding to given file descriptor.
+           fd: file descriptor
+           returns: node"""
+        node = cls.outToNode.get( fd )
+        return node or cls.inToNode.get( fd )
+
+    # Command support via shell process in namespace
+
+    def startShell( self ):
+        "Start a shell process for running commands"
+        if self.shell:
+            error( "%s: shell is already running" )
+            return
+        # mnexec: (c)lose descriptors, (d)etach from tty,
+        # (p)rint pid, and run in (n)amespace
+        opts = '-cdp'
+        if self.inNamespace:
+            opts += 'n'
+        # bash -m: enable job control
+        cmd = [ 'mnexec', opts, 'bash', '-m' ]
+        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
+                            close_fds=True )
+        self.stdin = self.shell.stdin
+        self.stdout = self.shell.stdout
+        self.pid = self.shell.pid
+        self.pollOut = select.poll()
+        self.pollOut.register( self.stdout )
+        # Maintain mapping between file descriptors and nodes
+        # This is useful for monitoring multiple nodes
+        # using select.poll()
+        self.outToNode[ self.stdout.fileno() ] = self
+        self.inToNode[ self.stdin.fileno() ] = self
+        self.execed = False
+        self.lastCmd = None
+        self.lastPid = None
+        self.readbuf = ''
+        self.waiting = False
+
+    def cleanup( self ):
+        "Help python collect its garbage."
+        if not self.inNamespace:
+            for intfName in self.intfNames():
+                if self.name in intfName:
+                    quietRun( 'ip link del ' + intfName )
+        self.shell = None
+
+    # Subshell I/O, commands and control
+
+    def read( self, maxbytes=1024 ):
+        """Buffered read from node, non-blocking.
+           maxbytes: maximum number of bytes to return"""
+        count = len( self.readbuf )
+        if count < maxbytes:
+            data = os.read( self.stdout.fileno(), maxbytes - count )
+            self.readbuf += data
+        if maxbytes >= len( self.readbuf ):
+            result = self.readbuf
+            self.readbuf = ''
+        else:
+            result = self.readbuf[ :maxbytes ]
+            self.readbuf = self.readbuf[ maxbytes: ]
+        return result
+
+    def readline( self ):
+        """Buffered readline from node, non-blocking.
+           returns: line (minus newline) or None"""
+        self.readbuf += self.read( 1024 )
+        if '\n' not in self.readbuf:
+            return None
+        pos = self.readbuf.find( '\n' )
+        line = self.readbuf[ 0: pos ]
+        self.readbuf = self.readbuf[ pos + 1: ]
+        return line
+
+    def write( self, data ):
+        """Write data to node.
+           data: string"""
+        os.write( self.stdin.fileno(), data )
+
+    def terminate( self ):
+        "Send kill signal to Node and clean up after it."
+        os.kill( self.pid, signal.SIGKILL )
+        self.cleanup()
+
+    def stop( self ):
+        "Stop node."
+        self.terminate()
+
+    def waitReadable( self, timeoutms=None ):
+        """Wait until node's output is readable.
+           timeoutms: timeout in ms or None to wait indefinitely."""
+        if len( self.readbuf ) == 0:
+            self.pollOut.poll( timeoutms )
+
+    def sendCmd( self, *args, **kwargs ):
+        """Send a command, followed by a command to echo a sentinel,
+           and return without waiting for the command to complete.
+           args: command and arguments, or string
+           printPid: print command's PID?"""
+        assert not self.waiting
+        printPid = kwargs.get( 'printPid', True )
+        # Allow sendCmd( [ list ] )
+        if len( args ) == 1 and type( args[ 0 ] ) is list:
+            cmd = args[ 0 ]
+        # Allow sendCmd( cmd, arg1, arg2... )
+        elif len( args ) > 0:
+            cmd = args
+        # Convert to string
+        if not isinstance( cmd, str ):
+            cmd = ' '.join( [ str( c ) for c in cmd ] )
+        if not re.search( r'\w', cmd ):
+            # Replace empty commands with something harmless
+            cmd = 'echo -n'
+        self.lastCmd = cmd
+        printPid = printPid and not isShellBuiltin( cmd )
+        if len( cmd ) > 0 and cmd[ -1 ] == '&':
+            # print ^A{pid}\n{sentinel}
+            cmd += ' printf "\\001%d\n\\177" $! \n'
+        else:
+            # print sentinel
+            cmd += '; printf "\\177"'
+            if printPid and not isShellBuiltin( cmd ):
+                cmd = 'mnexec -p ' + cmd
+        self.write( cmd + '\n' )
+        self.lastPid = None
+        self.waiting = True
+
+    def sendInt( self, sig=signal.SIGINT ):
+        "Interrupt running command."
+        if self.lastPid:
+            try:
+                os.kill( self.lastPid, sig )
+            except OSError:
+                pass
+
+    def monitor( self, timeoutms=None ):
+        """Monitor and return the output of a command.
+           Set self.waiting to False if command has completed.
+           timeoutms: timeout in ms or None to wait indefinitely."""
+        self.waitReadable( timeoutms )
+        data = self.read( 1024 )
+        # Look for PID
+        marker = chr( 1 ) + r'\d+\n'
+        if chr( 1 ) in data:
+            markers = re.findall( marker, data )
+            if markers:
+                self.lastPid = int( markers[ 0 ][ 1: ] )
+                data = re.sub( marker, '', data )
+        # Look for sentinel/EOF
+        if len( data ) > 0 and data[ -1 ] == chr( 127 ):
+            self.waiting = False
+            data = data[ :-1 ]
+        elif chr( 127 ) in data:
+            self.waiting = False
+            data = data.replace( chr( 127 ), '' )
+        return data
+
+    def waitOutput( self, verbose=False ):
+        """Wait for a command to complete.
+           Completion is signaled by a sentinel character, ASCII(127)
+           appearing in the output stream.  Wait for the sentinel and return
+           the output, including trailing newline.
+           verbose: print output interactively"""
+        log = info if verbose else debug
+        output = ''
+        while self.waiting:
+            data = self.monitor()
+            output += data
+            log( data )
+        return output
+
+    def cmd( self, *args, **kwargs ):
+        """Send a command, wait for output, and return it.
+           cmd: string"""
+        verbose = kwargs.get( 'verbose', False )
+        log = info if verbose else debug
+        log( '*** %s : %s\n' % ( self.name, args ) )
+        self.sendCmd( *args, **kwargs )
+        return self.waitOutput( verbose )
+
+    def cmdPrint( self, *args):
+        """Call cmd and printing its output
+           cmd: string"""
+        return self.cmd( *args, **{ 'verbose': True } )
+
+    def popen( self, *args, **kwargs ):
+        """Return a Popen() object in our namespace
+           args: Popen() args, single list, or string
+           kwargs: Popen() keyword args"""
+        defaults = { 'stdout': PIPE, 'stderr': PIPE,
+                     'mncmd':
+                     [ 'mnexec', '-a', str( self.pid ) ] }
+        defaults.update( kwargs )
+        if len( args ) == 1:
+            if type( args[ 0 ] ) is list:
+                # popen([cmd, arg1, arg2...])
+                cmd = args[ 0 ]
+            elif type( args[ 0 ] ) is str:
+                # popen("cmd arg1 arg2...")
+                cmd = args[ 0 ].split()
+            else:
+                raise Exception( 'popen() requires a string or list' )
+        elif len( args ) > 0:
+            # popen( cmd, arg1, arg2... )
+            cmd = list( args )
+        # Attach to our namespace  using mnexec -a
+        mncmd = defaults[ 'mncmd' ]
+        del defaults[ 'mncmd' ]
+        cmd = mncmd + cmd
+        # Shell requires a string, not a list!
+        if defaults.get( 'shell', False ):
+            cmd = ' '.join( cmd )
+        return Popen( cmd, **defaults )
+
+    def pexec( self, *args, **kwargs ):
+        """Execute a command using popen
+           returns: out, err, exitcode"""
+        popen = self.popen( *args, **kwargs)
+        out, err = popen.communicate()
+        exitcode = popen.wait()
+        return out, err, exitcode
+
+    # Interface management, configuration, and routing
+
+    # BL notes: This might be a bit redundant or over-complicated.
+    # However, it does allow a bit of specialization, including
+    # changing the canonical interface names. It's also tricky since
+    # the real interfaces are created as veth pairs, so we can't
+    # make a single interface at a time.
+
+    def newPort( self ):
+        "Return the next port number to allocate."
+        if len( self.ports ) > 0:
+            return max( self.ports.values() ) + 1
+        return self.portBase
+
+    def addIntf( self, intf, port=None ):
+        """Add an interface.
+           intf: interface
+           port: port number (optional, typically OpenFlow port number)"""
+        if port is None:
+            port = self.newPort()
+        self.intfs[ port ] = intf
+        self.ports[ intf ] = port
+        self.nameToIntf[ intf.name ] = intf
+        debug( '\n' )
+        debug( 'added intf %s:%d to node %s\n' % ( intf, port, self.name ) )
+        if self.inNamespace:
+            debug( 'moving', intf, 'into namespace for', self.name, '\n' )
+            moveIntf( intf.name, self )
+
+    def defaultIntf( self ):
+        "Return interface for lowest port"
+        ports = self.intfs.keys()
+        if ports:
+            return self.intfs[ min( ports ) ]
+        else:
+            warn( '*** defaultIntf: warning:', self.name,
+                  'has no interfaces\n' )
+
+    def intf( self, intf='' ):
+        """Return our interface object with given string name,
+           default intf if name is falsy (None, empty string, etc).
+           or the input intf arg.
+
+        Having this fcn return its arg for Intf objects makes it
+        easier to construct functions with flexible input args for
+        interfaces (those that accept both string names and Intf objects).
+        """
+        if not intf:
+            return self.defaultIntf()
+        elif type( intf) is str:
+            return self.nameToIntf[ intf ]
+        else:
+            return intf
+
+    def connectionsTo( self, node):
+        "Return [ intf1, intf2... ] for all intfs that connect self to node."
+        # We could optimize this if it is important
+        connections = []
+        for intf in self.intfList():
+            link = intf.link
+            if link:
+                node1, node2 = link.intf1.node, link.intf2.node
+                if node1 == self and node2 == node:
+                    connections += [ ( intf, link.intf2 ) ]
+                elif node1 == node and node2 == self:
+                    connections += [ ( intf, link.intf1 ) ]
+        return connections
+
+    def deleteIntfs( self ):
+        "Delete all of our interfaces."
+        # In theory the interfaces should go away after we shut down.
+        # However, this takes time, so we're better off removing them
+        # explicitly so that we won't get errors if we run before they
+        # have been removed by the kernel. Unfortunately this is very slow,
+        # at least with Linux kernels before 2.6.33
+        for intf in self.intfs.values():
+            intf.delete()
+            info( '.' )
+
+    # Routing support
+
+    def setARP( self, ip, mac ):
+        """Add an ARP entry.
+           ip: IP address as string
+           mac: MAC address as string"""
+        result = self.cmd( 'arp', '-s', ip, mac )
+        return result
+
+    def setHostRoute( self, ip, intf ):
+        """Add route to host.
+           ip: IP address as dotted decimal
+           intf: string, interface name"""
+        return self.cmd( 'route add -host', ip, 'dev', intf )
+
+    def setDefaultRoute( self, intf=None ):
+        """Set the default route to go through intf.
+           intf: string, interface name"""
+        if not intf:
+            intf = self.defaultIntf()
+        self.cmd( 'ip route flush root 0/0' )
+        return self.cmd( 'route add default %s' % intf )
+
+    # Convenience and configuration methods
+
+    def setMAC( self, mac, intf=None ):
+        """Set the MAC address for an interface.
+           intf: intf or intf name
+           mac: MAC address as string"""
+        return self.intf( intf ).setMAC( mac )
+
+    def setIP( self, ip, prefixLen=8, intf=None ):
+        """Set the IP address for an interface.
+           intf: intf or intf name
+           ip: IP address as a string
+           prefixLen: prefix length, e.g. 8 for /8 or 16M addrs"""
+        # This should probably be rethought
+        if '/' not in ip:
+            ip = '%s/%s' % ( ip, prefixLen )
+        return self.intf( intf ).setIP( ip )
+
+    def IP( self, intf=None ):
+        "Return IP address of a node or specific interface."
+        return self.intf( intf ).IP()
+
+    def MAC( self, intf=None ):
+        "Return MAC address of a node or specific interface."
+        return self.intf( intf ).MAC()
+
+    def intfIsUp( self, intf=None ):
+        "Check if an interface is up."
+        return self.intf( intf ).isUp()
+
+    # The reason why we configure things in this way is so
+    # That the parameters can be listed and documented in
+    # the config method.
+    # Dealing with subclasses and superclasses is slightly
+    # annoying, but at least the information is there!
+
+    def setParam( self, results, method, **param ):
+        """Internal method: configure a *single* parameter
+           results: dict of results to update
+           method: config method name
+           param: arg=value (ignore if value=None)
+           value may also be list or dict"""
+        name, value = param.items()[ 0 ]
+        f = getattr( self, method, None )
+        if not f or value is None:
+            return
+        if type( value ) is list:
+            result = f( *value )
+        elif type( value ) is dict:
+            result = f( **value )
+        else:
+            result = f( value )
+        results[ name ] = result
+        return result
+
+    def config( self, mac=None, ip=None,
+                defaultRoute=None, lo='up', **_params ):
+        """Configure Node according to (optional) parameters:
+           mac: MAC address for default interface
+           ip: IP address for default interface
+           ifconfig: arbitrary interface configuration
+           Subclasses should override this method and call
+           the parent class's config(**params)"""
+        # If we were overriding this method, we would call
+        # the superclass config method here as follows:
+        # r = Parent.config( **_params )
+        r = {}
+        self.setParam( r, 'setMAC', mac=mac )
+        self.setParam( r, 'setIP', ip=ip )
+        self.setParam( r, 'defaultRoute', defaultRoute=defaultRoute )
+        # This should be examined
+        self.cmd( 'ifconfig lo ' + lo )
+        return r
+
+    def configDefault( self, **moreParams ):
+        "Configure with default parameters"
+        self.params.update( moreParams )
+        self.config( **self.params )
+
+    # This is here for backward compatibility
+    def linkTo( self, node, link=Link ):
+        """(Deprecated) Link to another node
+           replace with Link( node1, node2)"""
+        return link( self, node )
+
+    # Other methods
+
+    def intfList( self ):
+        "List of our interfaces sorted by port number"
+        return [ self.intfs[ p ] for p in sorted( self.intfs.iterkeys() ) ]
+
+    def intfNames( self ):
+        "The names of our interfaces sorted by port number"
+        return [ str( i ) for i in self.intfList() ]
+
+    def __repr__( self ):
+        "More informative string representation"
+        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
+                              for i in self.intfList() ] ) )
+        return '<%s %s: %s pid=%s> ' % (
+            self.__class__.__name__, self.name, intfs, self.pid )
+
+    def __str__( self ):
+        "Abbreviated string representation"
+        return self.name
+
+    # Automatic class setup support
+
+    isSetup = False
+
+    @classmethod
+    def checkSetup( cls ):
+        "Make sure our class and superclasses are set up"
+        while cls and not getattr( cls, 'isSetup', True ):
+            cls.setup()
+            cls.isSetup = True
+            # Make pylint happy
+            cls = getattr( type( cls ), '__base__', None )
+
+    @classmethod
+    def setup( cls ):
+        "Make sure our class dependencies are available"
+        pathCheck( 'mnexec', 'ifconfig', moduleName='Mininet')
+
+
+class Host( Node ):
+    "A host is simply a Node"
+    pass
+
+
+        
+class CPULimitedHost( Host ):
+
+    "CPU limited host"
+
+    def __init__( self, name, sched='cfs', **kwargs ):
+        Host.__init__( self, name, **kwargs )
+        # Initialize class if necessary
+        if not CPULimitedHost.inited:
+            CPULimitedHost.init()
+        # Create a cgroup and move shell into it
+        self.cgroup = 'cpu,cpuacct,cpuset:/' + self.name
+        errFail( 'cgcreate -g ' + self.cgroup )
+        # We don't add ourselves to a cpuset because you must
+        # specify the cpu and memory placement first
+        errFail( 'cgclassify -g cpu,cpuacct:/%s %s' % ( self.name, self.pid ) )
+        # BL: Setting the correct period/quota is tricky, particularly
+        # for RT. RT allows very small quotas, but the overhead
+        # seems to be high. CFS has a mininimum quota of 1 ms, but
+        # still does better with larger period values.
+        self.period_us = kwargs.get( 'period_us', 100000 )
+        self.sched = sched
+        self.rtprio = 20
+
+    def cgroupSet( self, param, value, resource='cpu' ):
+        "Set a cgroup parameter and return its value"
+        cmd = 'cgset -r %s.%s=%s /%s' % (
+            resource, param, value, self.name )
+        quietRun( cmd )
+        nvalue = int( self.cgroupGet( param, resource ) )
+        if nvalue != value:
+            error( '*** error: cgroupSet: %s set to %s instead of %s\n'
+                   % ( param, nvalue, value ) )
+        return nvalue
+
+    def cgroupGet( self, param, resource='cpu' ):
+        "Return value of cgroup parameter"
+        cmd = 'cgget -r %s.%s /%s' % (
+            resource, param, self.name )
+        return int( quietRun( cmd ).split()[ -1 ] )
+
+    def cgroupDel( self ):
+        "Clean up our cgroup"
+        # info( '*** deleting cgroup', self.cgroup, '\n' )
+        _out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup )
+        return exitcode != 0
+
+    def popen( self, *args, **kwargs ):
+        """Return a Popen() object in node's namespace
+           args: Popen() args, single list, or string
+           kwargs: Popen() keyword args"""
+        # Tell mnexec to execute command in our cgroup
+        mncmd = [ 'mnexec', '-a', str( self.pid ),
+                  '-g', self.name ]
+        if self.sched == 'rt':
+            mncmd += [ '-r', str( self.rtprio ) ]
+        return Host.popen( self, *args, mncmd=mncmd, **kwargs )
+
+    def cleanup( self ):
+        "Clean up our cgroup"
+        retry( retries=3, delaySecs=1, fn=self.cgroupDel )
+
+    def chrt( self ):
+        "Set RT scheduling priority"
+        quietRun( 'chrt -p %s %s' % ( self.rtprio, self.pid ) )
+        result = quietRun( 'chrt -p %s' % self.pid )
+        firstline = result.split( '\n' )[ 0 ]
+        lastword = firstline.split( ' ' )[ -1 ]
+        if lastword != 'SCHED_RR':
+            error( '*** error: could not assign SCHED_RR to %s\n' % self.name )
+        return lastword
+
+    def rtInfo( self, f ):
+        "Internal method: return parameters for RT bandwidth"
+        pstr, qstr = 'rt_period_us', 'rt_runtime_us'
+        # RT uses wall clock time for period and quota
+        quota = int( self.period_us * f * numCores() )
+        return pstr, qstr, self.period_us, quota
+
+    def cfsInfo( self, f):
+        "Internal method: return parameters for CFS bandwidth"
+        pstr, qstr = 'cfs_period_us', 'cfs_quota_us'
+        # CFS uses wall clock time for period and CPU time for quota.
+        quota = int( self.period_us * f * numCores() )
+        period = self.period_us
+        if f > 0 and quota < 1000:
+            debug( '(cfsInfo: increasing default period) ' )
+            quota = 1000
+            period = int( quota / f / numCores() )
+        return pstr, qstr, period, quota
+
+    # BL comment:
+    # This may not be the right API,
+    # since it doesn't specify CPU bandwidth in "absolute"
+    # units the way link bandwidth is specified.
+    # We should use MIPS or SPECINT or something instead.
+    # Alternatively, we should change from system fraction
+    # to CPU seconds per second, essentially assuming that
+    # all CPUs are the same.
+
+    def setCPUFrac( self, f=-1, sched=None):
+        """Set overall CPU fraction for this host
+           f: CPU bandwidth limit (fraction)
+           sched: 'rt' or 'cfs'
+           Note 'cfs' requires CONFIG_CFS_BANDWIDTH"""
+        if not f:
+            return
+        if not sched:
+            sched = self.sched
+        if sched == 'rt':
+            pstr, qstr, period, quota = self.rtInfo( f )
+        elif sched == 'cfs':
+            pstr, qstr, period, quota = self.cfsInfo( f )
+        else:
+            return
+        if quota < 0:
+            # Reset to unlimited
+            quota = -1
+        # Set cgroup's period and quota
+        self.cgroupSet( pstr, period )
+        self.cgroupSet( qstr, quota )
+        if sched == 'rt':
+            # Set RT priority if necessary
+            self.chrt()
+        info( '(%s %d/%dus) ' % ( sched, quota, period ) )
+
+    def setCPUs( self, cores, mems=0 ):
+        "Specify (real) cores that our cgroup can run on"
+        if type( cores ) is list:
+            cores = ','.join( [ str( c ) for c in cores ] )
+        self.cgroupSet( resource='cpuset', param='cpus',
+                        value=cores )
+        # Memory placement is probably not relevant, but we
+        # must specify it anyway
+        self.cgroupSet( resource='cpuset', param='mems',
+                        value=mems)
+        # We have to do this here after we've specified
+        # cpus and mems
+        errFail( 'cgclassify -g cpuset:/%s %s' % (
+                 self.name, self.pid ) )
+
+    def config( self, cpu=None, cores=None, **params ):
+        """cpu: desired overall system CPU fraction
+           cores: (real) core(s) this host can run on
+           params: parameters for Node.config()"""
+        r = Node.config( self, **params )
+        # Was considering cpu={'cpu': cpu , 'sched': sched}, but
+        # that seems redundant
+
+        self.setParam( r, 'setCPUFrac', cpu=cpu )
+        self.setParam( r, 'setCPUs', cores=cores )
+
+        return r
+
+    inited = False
+
+    @classmethod
+    def init( cls ):
+        "Initialization for CPULimitedHost class"
+        mountCgroups()
+        cls.inited = True
+
+class CCNHost( Host ):
+    "CCNHost is a Host that always runs the ccnd daemon"
+
+    def __init__( self, name, **kwargs ):
+
+        
+        Host.__init__( self, name, **kwargs )
+        if not CCNHost.inited:
+            CCNHost.init()
+            
+        self.cmd("export CCN_LOCAL_SOCKNAME=/tmp/.sock.ccnx.{0}".format(self.name))
+        self.cmd("ccndstart")
+        self.peerList = {}
+
+    def config( self, fib=None, app=None, **params ):
+             
+        r = Node.config( self, **params )
+        
+        self.setParam( r, 'app', fib=fib )
+        self.setParam( r, 'fib', app=app)
+        
+        return r
+    
+    def configCCN(self):
+        
+        self.buildPeerIP()
+        self.setFIB()
+    
+    def buildPeerIP(self):
+        for iface in self.intfList():
+            link = iface.link
+            if link:
+                node1, node2 = link.intf1.node, link.intf2.node
+                if node1 == self:
+                    self.peerList[node2.name] = link.intf2.node.IP(link.intf2)
+                else:
+                    self.peerList[node1.name] = link.intf1.node.IP(link.intf1)
+        
+        
+    def setFIB(self):
+
+        for name in self.params['fib']:
+            if not name:
+                pass
+            else:
+                self.insert_fib(name[0],self.peerList[name[1]])
+            
+        
+    def insert_fib(self, uri, host):
+        self.cmd('ccndc add {0} tcp {1}'.format(uri,host))
+        
+    def terminate( self ):
+        "Stop node."
+        self.cmd('ccndstop')
+        Host.terminate(self)
+        
+    inited = False
+
+    
+    @classmethod
+    def init( cls ):
+        "Initialization for CCNHost class"
+        cls.inited = True
+        
+class CPULimitedCCNHost( CPULimitedHost ):
+    '''CPULimitedCCNHost is a Host that always runs the ccnd daemon and extends CPULimitedHost.
+       It should be used when one wants to limit the resources of CCN routers and hosts '''
+    
+
+    def __init__( self, name, sched='cfs', **kwargs ):
+        
+        CPULimitedHost.__init__( self, name, sched, **kwargs )
+        if not CCNHost.inited:
+            CCNHost.init()
+            
+        self.cmd("export CCN_LOCAL_SOCKNAME=/tmp/.sock.ccnx.{0}".format(self.name))
+        self.cmd("ccndstart")
+        self.peerList = {}
+
+    def config( self, fib=None, app=None, cpu=None, cores=None, **params):   
+
+        r = CPULimitedHost.config(self,cpu,cores, **params)
+        
+        print 'nozess'
+        self.setParam( r, 'app', fib=fib )
+        self.setParam( r, 'fib', app=app)
+        
+        return r
+    
+    def configCCN(self):
+        
+        self.buildPeerIP()
+        self.setFIB()
+    
+    def buildPeerIP(self):
+        for iface in self.intfList():
+            link = iface.link
+            if link:
+                node1, node2 = link.intf1.node, link.intf2.node
+                if node1 == self:
+                    self.peerList[node2.name] = link.intf2.node.IP(link.intf2)
+                else:
+                    self.peerList[node1.name] = link.intf1.node.IP(link.intf1)
+        
+        
+    def setFIB(self):
+
+        for name in self.params['fib']:
+            if not name:
+                pass
+            else:
+                self.insert_fib(name[0],self.peerList[name[1]])
+            
+        
+    def insert_fib(self, uri, host):
+        self.cmd('ccndc add {0} tcp {1}'.format(uri,host))
+        
+    def terminate( self ):
+        "Stop node."
+        self.cmd('ccndstop')
+        Host.terminate(self)
+        
+    inited = False
+
+    
+    @classmethod
+    def init( cls ):
+        "Initialization for CCNHost class"
+        cls.inited = True
+
+# Some important things to note:
+#
+# The "IP" address which setIP() assigns to the switch is not
+# an "IP address for the switch" in the sense of IP routing.
+# Rather, it is the IP address for the control interface,
+# on the control network, and it is only relevant to the
+# controller. If you are running in the root namespace
+# (which is the only way to run OVS at the moment), the
+# control interface is the loopback interface, and you
+# normally never want to change its IP address!
+#
+# In general, you NEVER want to attempt to use Linux's
+# network stack (i.e. ifconfig) to "assign" an IP address or
+# MAC address to a switch data port. Instead, you "assign"
+# the IP and MAC addresses in the controller by specifying
+# packets that you want to receive or send. The "MAC" address
+# reported by ifconfig for a switch data port is essentially
+# meaningless. It is important to understand this if you
+# want to create a functional router using OpenFlow.
+
+class Switch( Node ):
+    """A Switch is a Node that is running (or has execed?)
+       an OpenFlow switch."""
+
+    portBase = 1  # Switches start with port 1 in OpenFlow
+    dpidLen = 16  # digits in dpid passed to switch
+
+    def __init__( self, name, dpid=None, opts='', listenPort=None, **params):
+        """dpid: dpid for switch (or None to derive from name, e.g. s1 -> 1)
+           opts: additional switch options
+           listenPort: port to listen on for dpctl connections"""
+        Node.__init__( self, name, **params )
+        self.dpid = dpid if dpid else self.defaultDpid()
+        self.opts = opts
+        self.listenPort = listenPort
+        if not self.inNamespace:
+            self.controlIntf = Intf( 'lo', self, port=0 )
+
+    def defaultDpid( self ):
+        "Derive dpid from switch name, s1 -> 1"
+        try:
+            dpid = int( re.findall( '\d+', self.name )[ 0 ] )
+            dpid = hex( dpid )[ 2: ]
+            dpid = '0' * ( self.dpidLen - len( dpid ) ) + dpid
+            return dpid
+        except IndexError:
+            raise Exception( 'Unable to derive default datapath ID - '
+                             'please either specify a dpid or use a '
+                             'canonical switch name such as s23.' )
+
+    def defaultIntf( self ):
+        "Return control interface"
+        if self.controlIntf:
+            return self.controlIntf
+        else:
+            return Node.defaultIntf( self )
+
+    def sendCmd( self, *cmd, **kwargs ):
+        """Send command to Node.
+           cmd: string"""
+        kwargs.setdefault( 'printPid', False )
+        if not self.execed:
+            return Node.sendCmd( self, *cmd, **kwargs )
+        else:
+            error( '*** Error: %s has execed and cannot accept commands' %
+                   self.name )
+
+    def __repr__( self ):
+        "More informative string representation"
+        intfs = ( ','.join( [ '%s:%s' % ( i.name, i.IP() )
+                              for i in self.intfList() ] ) )
+        return '<%s %s: %s pid=%s> ' % (
+            self.__class__.__name__, self.name, intfs, self.pid )
+
+class UserSwitch( Switch ):
+    "User-space switch."
+
+    dpidLen = 12
+
+    def __init__( self, name, **kwargs ):
+        """Init.
+           name: name for the switch"""
+        Switch.__init__( self, name, **kwargs )
+        pathCheck( 'ofdatapath', 'ofprotocol',
+                   moduleName='the OpenFlow reference user switch' +
+                              '(openflow.org)' )
+        if self.listenPort:
+            self.opts += ' --listen=ptcp:%i ' % self.listenPort
+
+    @classmethod
+    def setup( cls ):
+        "Ensure any dependencies are loaded; if not, try to load them."
+        if not os.path.exists( '/dev/net/tun' ):
+            moduleDeps( add=TUN )
+
+    def dpctl( self, *args ):
+        "Run dpctl command"
+        if not self.listenPort:
+            return "can't run dpctl without passive listening port"
+        return self.cmd( 'dpctl ' + ' '.join( args ) +
+                         ' tcp:127.0.0.1:%i' % self.listenPort )
+
+    def start( self, controllers ):
+        """Start OpenFlow reference user datapath.
+           Log to /tmp/sN-{ofd,ofp}.log.
+           controllers: list of controller objects"""
+        # Add controllers
+        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
+                            for c in controllers ] )
+        ofdlog = '/tmp/' + self.name + '-ofd.log'
+        ofplog = '/tmp/' + self.name + '-ofp.log'
+        self.cmd( 'ifconfig lo up' )
+        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
+        self.cmd( 'ofdatapath -i ' + ','.join( intfs ) +
+                  ' punix:/tmp/' + self.name + ' -d ' + self.dpid +
+                  ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
+        self.cmd( 'ofprotocol unix:/tmp/' + self.name +
+                  ' ' + clist +
+                  ' --fail=closed ' + self.opts +
+                  ' 1> ' + ofplog + ' 2>' + ofplog + ' &' )
+
+    def stop( self ):
+        "Stop OpenFlow reference user datapath."
+        self.cmd( 'kill %ofdatapath' )
+        self.cmd( 'kill %ofprotocol' )
+        self.deleteIntfs()
+
+
+class OVSLegacyKernelSwitch( Switch ):
+    """Open VSwitch legacy kernel-space switch using ovs-openflowd.
+       Currently only works in the root namespace."""
+
+    def __init__( self, name, dp=None, **kwargs ):
+        """Init.
+           name: name for switch
+           dp: netlink id (0, 1, 2, ...)
+           defaultMAC: default MAC as unsigned int; random value if None"""
+        Switch.__init__( self, name, **kwargs )
+        self.dp = dp if dp else self.name
+        self.intf = self.dp
+        if self.inNamespace:
+            error( "OVSKernelSwitch currently only works"
+                   " in the root namespace.\n" )
+            exit( 1 )
+
+    @classmethod
+    def setup( cls ):
+        "Ensure any dependencies are loaded; if not, try to load them."
+        pathCheck( 'ovs-dpctl', 'ovs-openflowd',
+                   moduleName='Open vSwitch (openvswitch.org)')
+        moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
+
+    def start( self, controllers ):
+        "Start up kernel datapath."
+        ofplog = '/tmp/' + self.name + '-ofp.log'
+        quietRun( 'ifconfig lo up' )
+        # Delete local datapath if it exists;
+        # then create a new one monitoring the given interfaces
+        self.cmd( 'ovs-dpctl del-dp ' + self.dp )
+        self.cmd( 'ovs-dpctl add-dp ' + self.dp )
+        intfs = [ str( i ) for i in self.intfList() if not i.IP() ]
+        self.cmd( 'ovs-dpctl', 'add-if', self.dp, ' '.join( intfs ) )
+        # Run protocol daemon
+        clist = ','.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
+                            for c in controllers ] )
+        self.cmd( 'ovs-openflowd ' + self.dp +
+                  ' ' + clist +
+                  ' --fail=secure ' + self.opts +
+                  ' --datapath-id=' + self.dpid +
+                  ' 1>' + ofplog + ' 2>' + ofplog + '&' )
+        self.execed = False
+
+    def stop( self ):
+        "Terminate kernel datapath."
+        quietRun( 'ovs-dpctl del-dp ' + self.dp )
+        self.cmd( 'kill %ovs-openflowd' )
+        self.deleteIntfs()
+
+
+class OVSSwitch( Switch ):
+    "Open vSwitch switch. Depends on ovs-vsctl."
+
+    def __init__( self, name, failMode='secure', **params ):
+        """Init.
+           name: name for switch
+           failMode: controller loss behavior (secure|open)"""
+        Switch.__init__( self, name, **params )
+        self.failMode = failMode
+
+    @classmethod
+    def setup( cls ):
+        "Make sure Open vSwitch is installed and working"
+        pathCheck( 'ovs-vsctl',
+                   moduleName='Open vSwitch (openvswitch.org)')
+        # This should no longer be needed, and it breaks
+        # with OVS 1.7 which has renamed the kernel module:
+        #  moduleDeps( subtract=OF_KMOD, add=OVS_KMOD )
+        out, err, exitcode = errRun( 'ovs-vsctl -t 1 show' )
+        if exitcode:
+            error( out + err +
+                   'ovs-vsctl exited with code %d\n' % exitcode +
+                   '*** Error connecting to ovs-db with ovs-vsctl\n'
+                   'Make sure that Open vSwitch is installed, '
+                   'that ovsdb-server is running, and that\n'
+                   '"ovs-vsctl show" works correctly.\n'
+                   'You may wish to try '
+                   '"service openvswitch-switch start".\n' )
+            exit( 1 )
+
+    def dpctl( self, *args ):
+        "Run ovs-dpctl command"
+        return self.cmd( 'ovs-dpctl', args[ 0 ], self, *args[ 1: ] )
+
+    @staticmethod
+    def TCReapply( intf ):
+        """Unfortunately OVS and Mininet are fighting
+           over tc queuing disciplines. As a quick hack/
+           workaround, we clear OVS's and reapply our own."""
+        if type( intf ) is TCIntf:
+            intf.config( **intf.params )
+
+    def attach( self, intf ):
+        "Connect a data port"
+        self.cmd( 'ovs-vsctl add-port', self, intf )
+        self.cmd( 'ifconfig', intf, 'up' )
+        self.TCReapply( intf )
+
+    def detach( self, intf ):
+        "Disconnect a data port"
+        self.cmd( 'ovs-vsctl del-port', self, intf )
+
+    def start( self, controllers ):
+        "Start up a new OVS OpenFlow switch using ovs-vsctl"
+        if self.inNamespace:
+            raise Exception(
+                'OVS kernel switch does not work in a namespace' )
+        # We should probably call config instead, but this
+        # requires some rethinking...
+        self.cmd( 'ifconfig lo up' )
+        # Annoyingly, --if-exists option seems not to work
+        self.cmd( 'ovs-vsctl del-br', self )
+        self.cmd( 'ovs-vsctl add-br', self )
+        self.cmd( 'ovs-vsctl -- set Bridge', self,
+                  'other_config:datapath-id=' + self.dpid )
+        self.cmd( 'ovs-vsctl set-fail-mode', self, self.failMode )
+        for intf in self.intfList():
+            if not intf.IP():
+                self.attach( intf )
+        # Add controllers
+        clist = ' '.join( [ 'tcp:%s:%d' % ( c.IP(), c.port )
+                            for c in controllers ] )
+        if self.listenPort:
+            clist += ' ptcp:%s' % self.listenPort
+        self.cmd( 'ovs-vsctl set-controller', self, clist )
+
+    def stop( self ):
+        "Terminate OVS switch."
+        self.cmd( 'ovs-vsctl del-br', self )
+        self.deleteIntfs()
+
+OVSKernelSwitch = OVSSwitch
+
+
+class Controller( Node ):
+    """A Controller is a Node that is running (or has execed?) an
+       OpenFlow controller."""
+
+    def __init__( self, name, inNamespace=False, command='controller',
+                  cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
+                  port=6633, **params ):
+        self.command = command
+        self.cargs = cargs
+        self.cdir = cdir
+        self.ip = ip
+        self.port = port
+        Node.__init__( self, name, inNamespace=inNamespace,
+                       ip=ip, **params  )
+        self.cmd( 'ifconfig lo up' )  # Shouldn't be necessary
+        self.checkListening()
+
+    def checkListening( self ):
+        "Make sure no controllers are running on our port"
+        # Verify that Telnet is installed first:
+        out, _err, returnCode = errRun( "which telnet" )
+        if 'telnet' not in out or returnCode != 0:
+            raise Exception( "Error running telnet to check for listening "
+                             "controllers; please check that it is "
+                             "installed." )
+        listening = self.cmd( "echo A | telnet -e A %s %d" %
+                              ( self.ip, self.port ) )
+        if 'Unable' not in listening:
+            servers = self.cmd( 'netstat -atp' ).split( '\n' )
+            pstr = ':%d ' % self.port
+            clist = servers[ 0:1 ] + [ s for s in servers if pstr in s ]
+            raise Exception( "Please shut down the controller which is"
+                             " running on port %d:\n" % self.port +
+                             '\n'.join( clist ) )
+
+    def start( self ):
+        """Start <controller> <args> on controller.
+           Log to /tmp/cN.log"""
+        pathCheck( self.command )
+        cout = '/tmp/' + self.name + '.log'
+        if self.cdir is not None:
+            self.cmd( 'cd ' + self.cdir )
+        self.cmd( self.command + ' ' + self.cargs % self.port +
+                  ' 1>' + cout + ' 2>' + cout + '&' )
+        self.execed = False
+
+    def stop( self ):
+        "Stop controller."
+        self.cmd( 'kill %' + self.command )
+        self.terminate()
+
+    def IP( self, intf=None ):
+        "Return IP address of the Controller"
+        if self.intfs:
+            ip = Node.IP( self, intf )
+        else:
+            ip = self.ip
+        return ip
+
+    def __repr__( self ):
+        "More informative string representation"
+        return '<%s %s: %s:%s pid=%s> ' % (
+            self.__class__.__name__, self.name,
+            self.IP(), self.port, self.pid )
+
+
+class OVSController( Controller ):
+    "Open vSwitch controller"
+    def __init__( self, name, command='ovs-controller', **kwargs ):
+        Controller.__init__( self, name, command=command, **kwargs )
+
+
+class NOX( Controller ):
+    "Controller to run a NOX application."
+
+    def __init__( self, name, *noxArgs, **kwargs ):
+        """Init.
+           name: name to give controller
+           noxArgs: arguments (strings) to pass to NOX"""
+        if not noxArgs:
+            warn( 'warning: no NOX modules specified; '
+                  'running packetdump only\n' )
+            noxArgs = [ 'packetdump' ]
+        elif type( noxArgs ) not in ( list, tuple ):
+            noxArgs = [ noxArgs ]
+
+        if 'NOX_CORE_DIR' not in os.environ:
+            exit( 'exiting; please set missing NOX_CORE_DIR env var' )
+        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
+
+        Controller.__init__( self, name,
+                             command=noxCoreDir + '/nox_core',
+                             cargs='--libdir=/usr/local/lib -v -i ptcp:%s ' +
+                             ' '.join( noxArgs ),
+                             cdir=noxCoreDir,
+                             **kwargs )
+
+
+class RemoteController( Controller ):
+    "Controller running outside of Mininet's control."
+
+    def __init__( self, name, ip='127.0.0.1',
+                  port=6633, **kwargs):
+        """Init.
+           name: name to give controller
+           ip: the IP address where the remote controller is
+           listening
+           port: the port where the remote controller is listening"""
+        Controller.__init__( self, name, ip=ip, port=port, **kwargs )
+
+    def start( self ):
+        "Overridden to do nothing."
+        return
+
+    def stop( self ):
+        "Overridden to do nothing."
+        return
+
+    def checkListening( self ):
+        "Warn if remote controller is not accessible"
+        listening = self.cmd( "echo A | telnet -e A %s %d" %
+                              ( self.ip, self.port ) )
+        if 'Unable' in listening:
+            warn( "Unable to contact the remote controller"
+                  " at %s:%d\n" % ( self.ip, self.port ) )