text: #!/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
  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 realpath
  from functools import partial
  
  
  # Utility functions for unmounting a tree
  
  MNRUNDIR = realpath( '/var/run/mn' )
  
  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( rootdir=MNRUNDIR ):
      "Unmount all mounts under a directory tree"
      rootdir = realpath( rootdir )
      # Find all mounts below rootdir
      # This is subtle because /foo is not
      # a parent of /foot
      dirslash = rootdir + '/'
      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 = MNRUNDIR
  
      def __init__(self, name, *args, **kwargs ):
          """privateDirs: list of private directories
             remounts: dirs to remount
             unmount: unmount dirs in cleanup? (True)
             Note: if unmount is False, you must call unmountAll()
             manually."""
          self.privateDirs = kwargs.pop( 'privateDirs', [] )
          self.remounts = kwargs.pop( 'remounts', [] )
          self.unmount = kwargs.pop( 'unmount', True )
          Host.__init__( self, name, *args, **kwargs )
          self.rundir = '%s/%s' % ( self.mnRunDir, name )
          self.root, self.private = None, None  # set in createBindMounts
          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 mountDirs( self, dirs ):
          "Mount a list of directories"
          for dir_ in dirs:
              mountpoint = self.root + dir_
              errFail( 'mount -B %s %s' %
                       ( dir_, mountpoint ) )
  
      @classmethod
      def findRemounts( cls, fstypes=None ):
          """Identify mount points in /proc/mounts to remount
             fstypes: file system types to match"""
          if fstypes is None:
              fstypes = [ 'nfs' ]
          dirs = quietRun( 'cat /proc/mounts' ).strip().split( '\n' )
          remounts = []
          for dir_ in dirs:
              line = dir_.split()
              mountpoint, fstype = line[ 1 ], line[ 2 ]
              # Don't re-remount directories!!!
              if mountpoint.find( cls.mnRunDir ) == 0:
                  continue
              if fstype in fstypes:
                  remounts.append( mountpoint )
          return remounts
  
      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.mountDirs( self.remounts )
          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
             unmount: actually unmount bind mounts?"""
          # Wait for process to actually terminate
          self.shell.wait()
          Host.cleanup( self )
          if self.unmount:
              self.unmountBindMounts()
              errFail( 'rmdir ' + self.root )
  
  
  # Convenience aliases
  
  findRemounts = HostWithPrivateDirs.findRemounts
  
  
  # Sample usage
  
  def testHostWithPrivateDirs():
      "Test bind mounts"
      topo = SingleSwitchTopo( 10 )
      remounts = findRemounts( fstypes=[ 'nfs' ] )
      privateDirs = [ '/var/log', '/var/run' ]
      host = partial( HostWithPrivateDirs, remounts=remounts,
                      privateDirs=privateDirs, unmount=False )
      net = Mininet( topo=topo, host=host )
      net.start()
      info( 'Private Directories:', privateDirs, '\n' )
      CLI( net )
      net.stop()
      # We do this all at once to save a bit of time
      info( 'Unmounting host bind mounts...\n' )
      unmountAll()
  
  
  if __name__ == '__main__':
      unmountAll()
      setLogLevel( 'info' )
      testHostWithPrivateDirs()
      info( 'Done.\n')
  
  
  
  
  