diff --git a/examples/bind.py b/examples/bind.py new file mode 100755 index 0000000000000000000000000000000000000000..142eec40a9ad0c82567058787e7366e2b772b73b --- /dev/null +++ b/examples/bind.py @@ -0,0 +1,164 @@ +#!/usr/bin/python + +""" +bind.py: Bind mount prototype + +This creates hosts with private directories as desired. +""" + +from mininet.net import Mininet +from mininet.node import Host, Switch, Controller +from mininet.cli import CLI +from mininet.util import errFail, quietRun, errRun +from mininet.topo import SingleSwitchTopo +from mininet.log import setLogLevel, info, debug + +from os.path import join, realpath +from functools import partial + +# Utility functions for unmounting a tree + +def mountPoints(): + "Return list of mounted file systems" + mtab, _err, _ret = errFail( 'cat /proc/mounts' ) + lines = mtab.split( '\n' ) + mounts = [] + for line in lines: + if not line: + continue + fields = line.split( ' ') + mount = fields[ 1 ] + mounts.append( mount ) + return mounts + +def unmountAll( dir='/var/run/mn' ): + "Unmount all mounts under a directory tree" + dir = realpath( dir ) + # Find all mounts below dir + # This is subtle because /foo is not + # a parent of /foot + dirslash = dir + '/' + mounts = [ m for m in mountPoints() + if m == dir or m.find( dirslash ) == 0 ] + # Unmount them from bottom to top + mounts.sort( reverse=True ) + for mount in mounts: + debug( 'Unmounting', mount, '\n' ) + out, err, code = errRun( 'umount', mount ) + if code != 0: + info( '*** Warning: failed to umount', mount, '\n' ) + info( err ) + + +class HostWithPrivateDirs( Host ): + "Host with private directories" + + mnRunDir = realpath( '/var/run/mn' ) + + def __init__(self, name, *args, **kwargs ): + "privateDirs: list of private directories" + self.privateDirs = kwargs.pop( 'privateDirs', [] ) + Host.__init__( self, name, *args, **kwargs ) + self.rundir = '%s/%s' % ( self.mnRunDir, name ) + if self.privateDirs: + self.privateDirs = [ realpath( d ) for d in self.privateDirs ] + self.createBindMounts() + # These should run in the namespace before we chroot, + # in order to put the right entries in /etc/mtab + # Eventually this will allow a local pid space + # Now we chroot and cd to wherever we were before. + pwd = self.cmd( 'pwd' ).strip() + self.sendCmd( 'exec chroot', self.root, 'bash -ms mininet:' + + self.name ) + self.waiting = False + self.cmd( 'cd', pwd ) + # In order for many utilities to work, + # we need to remount /proc and /sys + self.cmd( 'mount /proc' ) + self.cmd( 'mount /sys' ) + + def mountPrivateDirs( self ): + "Create and bind mount private dirs" + for dir in self.privateDirs: + privateDir = self.private + dir + errFail( 'mkdir -p ' + privateDir ) + mountPoint = self.root + dir + errFail( 'mount -B %s %s' % + ( privateDir, mountPoint) ) + + def remountDirs( self, fstypes=[ 'nfs' ] ): + "Remount mounted file systems" + dirs = self.cmd( 'cat /proc/mounts' ).strip().split( '\n' ) + for dir in dirs: + line = dir.split() + mountpoint, fstype = line[ 1 ], line[ 2 ] + # Don't re-remount directories!!! + if mountpoint.find( self.mnRunDir ) == 0: + continue + if fstype in fstypes: + print "remounting:", mountpoint + errFail( 'mount -B %s %s' % ( + mountpoint, self.root + mountpoint ) ) + + def createBindMounts( self ): + """Create a chroot directory structure, + with self.privateDirs as private dirs""" + errFail( 'mkdir -p '+ self.rundir ) + unmountAll( self.rundir ) + # Create /root and /private directories + self.root = self.rundir + '/root' + self.private = self.rundir + '/private' + errFail( 'mkdir -p ' + self.root ) + errFail( 'mkdir -p ' + self.private ) + # Recursively mount / in private doort + # note we'll remount /sys and /proc later + errFail( 'mount -B / ' + self.root ) + self.remountDirs() + self.mountPrivateDirs() + + def unmountBindMounts( self ): + "Unmount all of our bind mounts" + unmountAll( self.rundir ) + + def popen( self, *args, **kwargs ): + "Popen with chroot support" + chroot = kwargs.pop( 'chroot', True ) + mncmd = kwargs.get( 'mncmd', + [ 'mnexec', '-a', str( self.pid ) ] ) + if chroot: + mncmd = [ 'chroot', self.root ] + mncmd + kwargs[ 'mncmd' ] = mncmd + return Host.popen( self, *args, **kwargs ) + + def cleanup( self ): + "Clean up, then unmount bind mounts" + # Wait for process to actually terminate + self.shell.wait() + Host.cleanup( self ) + self.unmountBindMounts() + errFail( 'rmdir ' + self.root ) + +# Sample usage + +def testHostWithPrivateDirs(): + "Test bind mounts" + topo = SingleSwitchTopo( 2 ) + privateDirs = [ '/var/log', '/var/run' ] + host = partial( HostWithPrivateDirs, privateDirs=privateDirs ) + net = Mininet( topo=topo, host=host ) + net.start() + print 'Private Directories:', privateDirs + CLI( net ) + net.stop() + + +if __name__ == '__main__': + unmountAll() + setLogLevel( 'info' ) + testHostWithPrivateDirs() + unmountAll() + + + + +