Statistics
| Branch: | Tag: | Revision:

mininet / mininet / cli.py @ b2ef87ae

History | View | Annotate | Download (6.97 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, as well as
63
    # "method could be a function" warning, since each CLI function
64
    # must have the same interface
65
    # pylint: disable-msg=W0613,R0201
66

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

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

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

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

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

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

    
121
    # pylint: enable-msg=W0703
122

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
222
    # pylint: enable-msg=W0613,R0201