Statistics
| Branch: | Tag: | Revision:

mininet / examples / bind.py @ 9bfc7c77

History | View | Annotate | Download (6.28 KB)

1
#!/usr/bin/python
2

    
3
"""
4
bind.py: Bind mount prototype
5

6
This creates hosts with private directories as desired.
7
"""
8

    
9
from mininet.net import Mininet
10
from mininet.node import Host
11
from mininet.cli import CLI
12
from mininet.util import errFail, quietRun, errRun
13
from mininet.topo import SingleSwitchTopo
14
from mininet.log import setLogLevel, info, debug
15

    
16
from os.path import realpath
17
from functools import partial
18

    
19

    
20
# Utility functions for unmounting a tree
21

    
22
MNRUNDIR = realpath( '/var/run/mn' )
23

    
24
def mountPoints():
25
    "Return list of mounted file systems"
26
    mtab, _err, _ret = errFail( 'cat /proc/mounts' )
27
    lines = mtab.split( '\n' )
28
    mounts = []
29
    for line in lines:
30
        if not line:
31
            continue
32
        fields = line.split( ' ')
33
        mount = fields[ 1 ]
34
        mounts.append( mount )
35
    return mounts
36

    
37
def unmountAll( rootdir=MNRUNDIR ):
38
    "Unmount all mounts under a directory tree"
39
    rootdir = realpath( rootdir )
40
    # Find all mounts below rootdir
41
    # This is subtle because /foo is not
42
    # a parent of /foot
43
    dirslash = rootdir + '/'
44
    mounts = [ m for m in mountPoints()
45
              if m == dir or m.find( dirslash ) == 0 ]
46
    # Unmount them from bottom to top
47
    mounts.sort( reverse=True )
48
    for mount in mounts:
49
        debug( 'Unmounting', mount, '\n' )
50
        _out, err, code = errRun( 'umount', mount )
51
        if code != 0:
52
            info( '*** Warning: failed to umount', mount, '\n' )
53
            info( err )
54

    
55

    
56
class HostWithPrivateDirs( Host ):
57
    "Host with private directories"
58

    
59
    mnRunDir = MNRUNDIR
60

    
61
    def __init__(self, name, *args, **kwargs ):
62
        """privateDirs: list of private directories
63
           remounts: dirs to remount
64
           unmount: unmount dirs in cleanup? (True)
65
           Note: if unmount is False, you must call unmountAll()
66
           manually."""
67
        self.privateDirs = kwargs.pop( 'privateDirs', [] )
68
        self.remounts = kwargs.pop( 'remounts', [] )
69
        self.unmount = kwargs.pop( 'unmount', True )
70
        Host.__init__( self, name, *args, **kwargs )
71
        self.rundir = '%s/%s' % ( self.mnRunDir, name )
72
        self.root, self.private = None, None  # set in createBindMounts
73
        if self.privateDirs:
74
            self.privateDirs = [ realpath( d ) for d in self.privateDirs ]
75
            self.createBindMounts()
76
        # These should run in the namespace before we chroot,
77
        # in order to put the right entries in /etc/mtab
78
        # Eventually this will allow a local pid space
79
        # Now we chroot and cd to wherever we were before.
80
        pwd = self.cmd( 'pwd' ).strip()
81
        self.sendCmd( 'exec chroot', self.root, 'bash -ms mininet:'
82
                       + self.name )
83
        self.waiting = False
84
        self.cmd( 'cd', pwd )
85
        # In order for many utilities to work,
86
        # we need to remount /proc and /sys
87
        self.cmd( 'mount /proc' )
88
        self.cmd( 'mount /sys' )
89

    
90
    def mountPrivateDirs( self ):
91
        "Create and bind mount private dirs"
92
        for dir_ in self.privateDirs:
93
            privateDir = self.private + dir_
94
            errFail( 'mkdir -p ' + privateDir )
95
            mountPoint = self.root + dir_
96
            errFail( 'mount -B %s %s' %
97
                           ( privateDir, mountPoint) )
98

    
99
    def mountDirs( self, dirs ):
100
        "Mount a list of directories"
101
        for dir_ in dirs:
102
            mountpoint = self.root + dir_
103
            errFail( 'mount -B %s %s' %
104
                     ( dir_, mountpoint ) )
105

    
106
    @classmethod
107
    def findRemounts( cls, fstypes=None ):
108
        """Identify mount points in /proc/mounts to remount
109
           fstypes: file system types to match"""
110
        if fstypes is None:
111
            fstypes = [ 'nfs' ]
112
        dirs = quietRun( 'cat /proc/mounts' ).strip().split( '\n' )
113
        remounts = []
114
        for dir_ in dirs:
115
            line = dir_.split()
116
            mountpoint, fstype = line[ 1 ], line[ 2 ]
117
            # Don't re-remount directories!!!
118
            if mountpoint.find( cls.mnRunDir ) == 0:
119
                continue
120
            if fstype in fstypes:
121
                remounts.append( mountpoint )
122
        return remounts
123

    
124
    def createBindMounts( self ):
125
        """Create a chroot directory structure,
126
           with self.privateDirs as private dirs"""
127
        errFail( 'mkdir -p '+ self.rundir )
128
        unmountAll( self.rundir )
129
        # Create /root and /private directories
130
        self.root = self.rundir + '/root'
131
        self.private = self.rundir + '/private'
132
        errFail( 'mkdir -p ' + self.root )
133
        errFail( 'mkdir -p ' + self.private )
134
        # Recursively mount / in private doort
135
        # note we'll remount /sys and /proc later
136
        errFail( 'mount -B / ' + self.root )
137
        self.mountDirs( self.remounts )
138
        self.mountPrivateDirs()
139

    
140
    def unmountBindMounts( self ):
141
        "Unmount all of our bind mounts"
142
        unmountAll( self.rundir )
143

    
144
    def popen( self, *args, **kwargs ):
145
        "Popen with chroot support"
146
        chroot = kwargs.pop( 'chroot', True )
147
        mncmd = kwargs.get( 'mncmd',
148
                           [ 'mnexec', '-a', str( self.pid ) ] )
149
        if chroot:
150
            mncmd = [ 'chroot', self.root ] + mncmd
151
            kwargs[ 'mncmd' ] = mncmd
152
        return Host.popen( self, *args, **kwargs )
153

    
154
    def cleanup( self ):
155
        """Clean up, then unmount bind mounts
156
           unmount: actually unmount bind mounts?"""
157
        # Wait for process to actually terminate
158
        self.shell.wait()
159
        Host.cleanup( self )
160
        if self.unmount:
161
            self.unmountBindMounts()
162
            errFail( 'rmdir ' + self.root )
163

    
164

    
165
# Convenience aliases
166

    
167
findRemounts = HostWithPrivateDirs.findRemounts
168

    
169

    
170
# Sample usage
171

    
172
def testHostWithPrivateDirs():
173
    "Test bind mounts"
174
    topo = SingleSwitchTopo( 10 )
175
    remounts = findRemounts( fstypes=[ 'nfs' ] )
176
    privateDirs = [ '/var/log', '/var/run' ]
177
    host = partial( HostWithPrivateDirs, remounts=remounts,
178
                    privateDirs=privateDirs, unmount=False )
179
    net = Mininet( topo=topo, host=host )
180
    net.start()
181
    info( 'Private Directories:', privateDirs, '\n' )
182
    CLI( net )
183
    net.stop()
184
    # We do this all at once to save a bit of time
185
    info( 'Unmounting host bind mounts...\n' )
186
    unmountAll()
187

    
188

    
189
if __name__ == '__main__':
190
    unmountAll()
191
    setLogLevel( 'info' )
192
    testHostWithPrivateDirs()
193
    info( 'Done.\n')
194

    
195

    
196

    
197