Statistics
| Branch: | Tag: | Revision:

mininet / mininet / cli.py @ 28cd95c3

History | View | Annotate | Download (6.3 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 a link up or down."
152
        args = args.split()
153
        if len(args) != 3:
154
            error( 'invalid number of args: link [up down] end1 end2\n' )
155
        elif args[ 0 ] not in [ 'up', 'down' ]:
156
            error( 'invalid type: link [up down] end1 end2\n' )
157
        else:
158
            self.mn.link( *args )
159

    
160
    def do_exit( self, args ):
161
        "Exit"
162
        return 'exited by user command'
163

    
164
    def do_quit( self, args ):
165
        "Exit"
166
        return self.do_exit( args )
167

    
168
    def do_EOF( self, args ):
169
        "Exit"
170
        return self.do_exit( args )
171

    
172
    def default( self, line ):
173
        """Called on an input line when the command prefix is not recognized.
174
        Overridden to run shell commands when a node is the first CLI argument.
175
        Past the first CLI argument, node names are automatically replaced with
176
        corresponding IP addrs."""
177

    
178
        first, args, line = self.parseline( line )
179
        if len(args) > 0 and args[ -1 ] == '\n':
180
            args = args[ :-1 ]
181
        rest = args.split( ' ' )
182

    
183
        if first in self.nodemap:
184
            node = self.nodemap[ first ]
185
            # Substitute IP addresses for node names in command
186
            rest = [ self.nodemap[ arg ].IP()
187
                    if arg in self.nodemap else arg
188
                    for arg in rest ]
189
            rest = ' '.join( rest )
190
            # Run cmd on node:
191
            node.sendCmd( rest, printPid=True )
192
            while True:
193
                try:
194
                    done, data = node.monitor()
195
                    info( '%s' % data )
196
                    if done:
197
                        break
198
                except KeyboardInterrupt:
199
                    node.sendInt()
200
        else:
201
            self.stdout.write( '*** Unknown syntax: %s\n' % line )
202

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