Statistics
| Branch: | Tag: | Revision:

mininet / mininet / cli.py @ c70aab0a

History | View | Annotate | Download (6.95 KB)

1
"""
2
A simple command-line interface for Mininet.
3

4
The Mininet CLI provides a simple control console which
5
makes it easy to talk to nodes. For example, the command
6

7
mininet> h27 ifconfig
8

9
runs 'ifconfig' on host h27.
10

11
Having a single console rather than, for example, an xterm for each
12
node is particularly convenient for networks of any reasonable
13
size.
14

15
The CLI automatically substitutes IP addresses for node names,
16
so commands like
17

18
mininet> h2 ping h3
19

20
should work correctly and allow host h2 to ping host h3
21

22
Several useful commands are provided, including the ability to
23
list all nodes ('nodes'), to print out the network topology
24
('net') and to check connectivity ('pingall', 'pingpair')
25
and bandwidth ('iperf'.)
26

27
Bugs/limitations:
28

29
- Interactive commands are not supported at the moment
30

31
"""
32

    
33
from subprocess import call
34
from cmd import Cmd
35

    
36
from mininet.log import info, output, error
37

    
38
class CLI( Cmd ):
39
    "Simple command-line interface to talk to nodes."
40

    
41
    prompt = 'mininet> '
42

    
43
    def __init__( self, mininet ):
44
        self.mn = mininet
45
        self.nodelist = self.mn.controllers + self.mn.switches + self.mn.hosts
46
        self.nodemap = {} # map names to Node objects
47
        for node in self.nodelist:
48
            self.nodemap[ node.name ] = node
49
        Cmd.__init__( self )
50
        info( '*** Starting CLI:\n' )
51
        while True:
52
            try:
53
                self.cmdloop()
54
                break
55
            except KeyboardInterrupt:
56
                info( 'Interrupt\n' )
57

    
58
    def emptyline( self ):
59
        "Don't repeat last command when you hit return."
60
        pass
61

    
62
    # Disable pylint "Unused argument: 'arg's'" messages.
63
    # Each CLI function needs the same interface.
64
    # pylint: disable-msg=W0613
65

    
66
    def do_help( self, args ):
67
        "Describe available CLI commands."
68
        Cmd.do_help( self, args )
69
        helpStr = ( 'You may also send a command to a node using:\n'
70
                   '  <node> command {args}\n'
71
                   'For example:\n'
72
                   '  mininet> h0 ifconfig\n'
73
                   '\n'
74
                   'The interpreter automatically substitutes IP '
75
                   'addresses\n'
76
                   'for node names when a node is the first arg, so commands'
77
                   ' like\n'
78
                   ' mininet> h2 ping h3\n'
79
                   'should work.\n'
80
                   '\n'
81
                   'Interactive commands are not supported yet.\n' )
82
        if args is "":
83
            self.stdout.write( helpStr )
84

    
85
    def do_nodes( self, args ):
86
        "List all nodes."
87
        nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
88
        output( 'available nodes are: \n%s\n' % nodes )
89

    
90
    def do_net( self, args ):
91
        "List network connections."
92
        for switch in self.mn.switches:
93
            output( switch.name, '<->' )
94
            for intf in switch.intfs.values():
95
                name = switch.connection[ intf ][ 1 ]
96
                output( ' %s' % name )
97
            output( '\n' )
98

    
99
    def do_sh( self, args ):
100
        "Run an external shell command"
101
        call( args, shell=True )
102

    
103
    # do_py() needs to catch any exception during eval()
104
    # pylint: disable-msg=W0703
105

    
106
    def do_py( self, args ):
107
        """Evaluate a Python expression.
108
           Node names may be used, e.g.: h1.cmd('ls')"""
109
        try:
110
            result = eval( args, globals(), self.nodemap )
111
            if not result:
112
                return
113
            elif isinstance( result, str ):
114
                info( result + '\n' )
115
            else:
116
                info( repr( result ) + '\n' )
117
        except Exception, e:
118
            info( str( e ) + '\n' )
119

    
120
    # pylint: enable-msg=W0703
121

    
122
    def do_pingall( self, args ):
123
        "Ping between all hosts."
124
        self.mn.pingAll()
125

    
126
    def do_pingpair( self, args ):
127
        "Ping between first two hosts, useful for testing."
128
        self.mn.pingPair()
129

    
130
    def do_iperf( self, args ):
131
        "Simple iperf TCP test between two hosts."
132
        self.mn.iperf()
133

    
134
    def do_iperfudp( self, args ):
135
        "Simple iperf UDP test between two hosts."
136
        udpBw = args[ 0 ] if len( args ) else '10M'
137
        self.mn.iperfUdp( udpBw )
138

    
139
    def do_intfs( self, args ):
140
        "List interfaces."
141
        for node in self.nodelist:
142
            output( '%s: %s\n' %
143
                ( node.name, ' '.join( sorted( node.intfs.values() ) ) ) )
144

    
145
    def do_dump( self, args ):
146
        "Dump node info."
147
        for node in self.nodelist:
148
            output( '%s\n' % node )
149

    
150
    def do_link( self, args ):
151
        "Bring link(s) between two nodes up or down."
152
        args = args.split()
153
        if len(args) != 3:
154
            error( 'invalid number of args: link end1 end2 [up down]\n' )
155
        elif args[ 2 ] not in [ 'up', 'down' ]:
156
            error( 'invalid type: link end1 end2 [up down]\n' )
157
        else:
158
            self.mn.configLinkStatus( *args )
159

    
160
    def do_pause( self, args ):
161
        "Temporarily bring a node down."
162
        args = args.split()
163
        if len(args) != 1:
164
            error( 'invalid number of args: pause [node]\n' )
165
        elif args[0] not in self.nodemap:
166
            error( 'invalid node: %s\n' % args[0] )
167
        else:
168
            self.nodemap[ args[ 0 ] ].pause()
169

    
170
    def do_resume( self, args ):
171
        "Temporarily bring a node up."
172
        args = args.split()
173
        if len(args) != 1:
174
            error( 'invalid number of args: resume [node]\n' )
175
        elif args[0] not in self.nodemap:
176
            error( 'invalid node: %s\n' % args[0] )
177
        else:
178
            self.nodemap[ args[ 0 ] ].resume()
179

    
180
    def do_exit( self, args ):
181
        "Exit"
182
        return 'exited by user command'
183

    
184
    def do_quit( self, args ):
185
        "Exit"
186
        return self.do_exit( args )
187

    
188
    def do_EOF( self, args ):
189
        "Exit"
190
        return self.do_exit( args )
191

    
192
    def default( self, line ):
193
        """Called on an input line when the command prefix is not recognized.
194
        Overridden to run shell commands when a node is the first CLI argument.
195
        Past the first CLI argument, node names are automatically replaced with
196
        corresponding IP addrs."""
197

    
198
        first, args, line = self.parseline( line )
199
        if len(args) > 0 and args[ -1 ] == '\n':
200
            args = args[ :-1 ]
201
        rest = args.split( ' ' )
202

    
203
        if first in self.nodemap:
204
            node = self.nodemap[ first ]
205
            # Substitute IP addresses for node names in command
206
            rest = [ self.nodemap[ arg ].IP()
207
                    if arg in self.nodemap else arg
208
                    for arg in rest ]
209
            rest = ' '.join( rest )
210
            # Run cmd on node:
211
            node.sendCmd( rest, printPid=True )
212
            while node.waiting:
213
                try:
214
                    data = node.monitor()
215
                    info( '%s' % data )
216
                except KeyboardInterrupt:
217
                    node.sendInt()
218
        else:
219
            self.stdout.write( '*** Unknown syntax: %s\n' % line )
220

    
221
    # Re-enable pylint "Unused argument: 'arg's'" messages.
222
    # pylint: enable-msg=W0613