diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 9bdbb519..abaccbb4 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -19,17 +19,18 @@ Cody Burkard Additional Mininet Contributors Tomasz Buchert -Gustavo Pantuza Coelho Pinto Fernando Cappi +Gustavo Pantuza Coelho Pinto Ryan Cox Shaun Crampton David Erickson -Glen Gibb Andrew Ferguson Eder Leao Fernandes Gregory Gee +Glen Gibb Jon Hall Roan Huang +Nicolas Hurman Vitaly Ivanov Babis Kaidos Rich Lane diff --git a/mininet/moduledeps.py b/mininet/moduledeps.py index 860c21c9..64f0a5d0 100644 --- a/mininet/moduledeps.py +++ b/mininet/moduledeps.py @@ -21,6 +21,7 @@ def modprobe( mod ): OF_KMOD = 'ofdatapath' OVS_KMOD = 'openvswitch_mod' # Renamed 'openvswitch' in OVS 1.7+/Linux 3.5+ TUN = 'tun' +OVERLAY = 'overlay' def moduleDeps( subtract=None, add=None ): """Handle module dependencies. diff --git a/mininet/node.py b/mininet/node.py index ea7851b8..c5a8e906 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -13,7 +13,7 @@ monitor(). Examples of how to run experiments using this functionality are provided in the examples/ directory. By default, hosts share the root file system, but they may also specify private - directories. + directories or overlayed directories. CPULimitedHost: a virtual host whose CPU bandwidth is limited by RT or CFS bandwidth limiting. @@ -63,7 +63,7 @@ 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, TUN +from mininet.moduledeps import moduleDeps, pathCheck, TUN, OVERLAY from mininet.link import Link, Intf, TCIntf, OVSIntf from re import findall from distutils.version import StrictVersion @@ -78,6 +78,7 @@ def __init__( self, name, inNamespace=True, **params ): """name: name of node inNamespace: in network namespace? privateDirs: list of private directory strings or tuples + overlayDirs: list of overlay directory strings or tuples params: Node parameters (see config() for details)""" # Make sure class actually works @@ -85,6 +86,7 @@ def __init__( self, name, inNamespace=True, **params ): self.name = params.get( 'name', name ) self.privateDirs = params.get( 'privateDirs', [] ) + self.overlayDirs = params.get( 'overlayDirs', [] ) self.inNamespace = params.get( 'inNamespace', inNamespace ) # Stash configuration parameters for future reference @@ -104,6 +106,7 @@ def __init__( self, name, inNamespace=True, **params ): # Start command interpreter shell self.startShell() + self.mountOverlayDirs() self.mountPrivateDirs() # File descriptor to node mapping support @@ -130,11 +133,12 @@ def startShell( self, mnopts=None ): # (p)rint pid, and run in (n)amespace opts = '-cd' if mnopts is None else mnopts if self.inNamespace: + opts = '-H %s %s' % (self.name, opts) opts += 'n' # bash -i: force interactive # -s: pass $* to shell, and make process easy to find in ps # prompt is set to sentinel chr( 127 ) - cmd = [ 'mnexec', opts, 'env', 'PS1=' + chr( 127 ), + cmd = [ 'mnexec' ] + opts.split() + [ 'env', 'PS1=' + chr( 127 ), 'bash', '--norc', '-is', 'mininet:' + self.name ] # Spawn a shell subprocess in a pseudo-tty, to disable buffering # in the subprocess and insulate it from signals (e.g. SIGINT) @@ -166,6 +170,48 @@ def startShell( self, mnopts=None ): # +m: disable job control notification self.cmd( 'unset HISTFILE; stty -echo; set +m' ) + def mountOverlayDirs( self ): + "mount overlay directories" + # Avoid expanding a string into a list of chars + assert not isinstance( self.overlayDirs, basestring ) + if self.overlayDirs: + moduleDeps( add=OVERLAY ) + + for directory in self.overlayDirs: + if isinstance( directory, tuple ): + # mount given overlay directory + overlayDir = directory[ 1 ] % self.__dict__ + mountPoint = directory[ 0 ] + else: + # mount temporary filesystem on directory + tmpDir = '/tmp/mininet/%s%s' % (self, directory) + overlayDir = tmpDir + '/overlay' + + mountPoint = directory + self.cmd( 'mkdir -p %s' % tmpDir ) + self.cmd( 'mount -n -t tmpfs tmpfs %s' % tmpDir ) + self.cmd( 'mkdir -p %s' % overlayDir ) + + workDir = overlayDir + '.work' + self.cmd( 'mkdir -p %s %s %s' % (mountPoint, workDir, overlayDir) ) + self.cmd( 'mount -t overlay overlay -o lowerdir=%s,upperdir=%s,workdir=%s %s' % + ( mountPoint, overlayDir, workDir, mountPoint ) ) + + def unmountOverlayDirs( self ): + "mount overlay directories" + for directory in self.overlayDirs: + if isinstance( directory, tuple ): + mountPoint = directory[ 0 ] + overlayDir = directory[ 1 ] % self.__dict__ + workDir = overlayDir + '.work' + self.cmd( 'umount ', mountPoint ) + self.cmd( 'rmdir %s/work %s' % (workDir, workDir) ) + else: + mountPoint = directory + self.cmd( 'umount ', mountPoint ) + self.cmd( 'umount ', '/tmp/mininet/%s/%s' % (self, directory) ) + self.cmd( 'rmdir /tmp/mininet/%s%s /tmp/mininet/%s /tmp/mininet' % (self, directory, self) ) + def mountPrivateDirs( self ): "mount private directories" # Avoid expanding a string into a list of chars @@ -245,6 +291,7 @@ def write( self, data ): def terminate( self ): "Send kill signal to Node and clean up after it." self.unmountPrivateDirs() + self.unmountOverlayDirs() if self.shell: if self.shell.poll() is None: os.killpg( self.shell.pid, signal.SIGHUP ) diff --git a/mininet/term.py b/mininet/term.py index 04d9871c..159a9fbb 100644 --- a/mininet/term.py +++ b/mininet/term.py @@ -7,6 +7,7 @@ """ from os import environ +import subprocess from mininet.log import error from mininet.util import quietRun, errRun @@ -28,6 +29,12 @@ def tunnelX11( node, display=None): quietRun( 'xhost +si:localuser:root' ) return display, None else: + # Add credentials with new hostname + creds = subprocess.check_output( 'xauth list $DISPLAY', shell=True ).split('/', 1) + if len( creds ) == 2: + newCred = node.name + '/' + creds[ 1 ] + node.cmd( 'xauth add ' + newCred ) + # Create a tunnel for the TCP connection port = 6000 + int( float( screen ) ) connection = r'TCP\:%s\:%s' % ( host, port ) @@ -54,7 +61,7 @@ def makeTerm( node, title='Node', term='xterm', display=None, cmd='bash'): display, tunnel = tunnelX11( node, display ) if display is None: return [] - term = node.popen( cmds[ term ] + + term = node.popen( [ '-H', node.name ] + cmds[ term ] + [ display, '-e', 'env TERM=ansi %s' % cmd ] ) return [ tunnel, term ] if tunnel else [ term ] diff --git a/mnexec.c b/mnexec.c index d3f173d7..4458f624 100644 --- a/mnexec.c +++ b/mnexec.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #if !defined(VERSION) #define VERSION "(devel)" @@ -102,8 +104,22 @@ int main(int argc, char *argv[]) char *cwd = get_current_dir_name(); static struct sched_param sp; - while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1) + while ((c = getopt(argc, argv, "+H:cdnpa:g:r:vh")) != -1) switch(c) { + case 'H': + /* rename if we have a hostname */ + if (*optarg) { + if (unshare(CLONE_NEWUTS) == -1) { + perror("unshare"); + return 1; + } + + if (sethostname(optarg, MIN(strlen(optarg), HOST_NAME_MAX)) == -1) { + perror("sethostname"); + return 1; + } + } + break; case 'c': /* close file descriptors except stdin/out/error */ for (fd = getdtablesize(); fd > 2; fd--)