Revision 496b5f9e

View differences:

mininet/cli.py
1
#!/usr/bin/python
2

  
3
"""
4
A simple command-line interface for Mininet.
5

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

  
9
mininet> h27 ifconfig
10

  
11
runs 'ifconfig' on host h27.
12

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

  
17
The CLI automatically substitutes IP addresses for node names,
18
so commands like
19

  
20
mininet> h0 ping -c1 h31
21

  
22
should work correctly and allow host h0 to ping host h31.
23
Note the '-c1' argument as per the Bugs/limitations section below!
24

  
25
Several useful commands are provided, including the ability to
26
list all nodes ('nodes'), to print out the network topology
27
('net') and to check connectivity ('ping_all', 'ping_pair')
28
and bandwidth ('iperf'.)
29

  
30
Bugs/limitations:
31

  
32
- Interactive commands are not supported at the moment;
33
  notably, if you type 'ping h1', you can control-C it, but
34
  it breaks the CLI and your network. ;-(
35
  For now, we recommend limiting CLI use to non-interactive
36
  commands which terminate in a reasonable amount of time.
37

  
38
- We don't (yet) support command line history editing. This is
39
  coming soon.
40

  
41
"""
42

  
43
from subprocess import call
44
import sys
45

  
46
from mininet.log import lg
47

  
48
class CLI( object ):
49
    "Simple command-line interface to talk to nodes."
50

  
51
    cmds = [ '?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \
52
            'ping_pair', 'iperf', 'iperf_udp', 'intfs', 'dump' ]
53

  
54
    def __init__( self, mininet ):
55
        self.mn = mininet
56
        self.nodemap = {} # map names to Node objects
57
        for node in self.mn.nodes.values():
58
            self.nodemap[ node.name ] = node
59
        for cname, cnode in self.mn.controllers.iteritems():
60
            self.nodemap[ cname ] = cnode
61
        self.nodelist = self.nodemap.values()
62
        self.run()
63

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

  
68
    # Commands
69
    def help( self, args ):
70
        "Semi-useful help for CLI."
71
        helpStr = ( 'Available commands are:' + str( self.cmds ) + '\n'
72
                   'You may also send a command to a node using:\n'
73
                   '  <node> command {args}\n'
74
                   'For example:\n'
75
                   '  mininet> h0 ifconfig\n'
76
                   '\n'
77
                   'The interpreter automatically substitutes IP '
78
                   'addresses\n'
79
                   'for node names, so commands like\n'
80
                   '  mininet> h0 ping -c1 h1\n'
81
                   'should work.\n'
82
                   '\n\n'
83
                   'Interactive commands are not really supported yet,\n'
84
                   'so please limit commands to ones that do not\n'
85
                   'require user interaction and will terminate\n'
86
                   'after a reasonable amount of time.\n' )
87
        print( helpStr )
88

  
89
    def nodes( self, args ):
90
        "List all nodes."
91
        nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
92
        lg.info( 'available nodes are: \n%s\n' % nodes )
93

  
94
    def net( self, args ):
95
        "List network connections."
96
        for switchDpid in self.mn.topo.switches():
97
            switch = self.mn.nodes[ switchDpid ]
98
            lg.info( '%s <->', switch.name )
99
            for intf in switch.intfs:
100
                node = switch.connection[ intf ]
101
                lg.info( ' %s' % node.name )
102
            lg.info( '\n' )
103

  
104
    def sh( self, args ):
105
        "Run an external shell command"
106
        call( [ 'sh', '-c' ] + args )
107

  
108
    def pingAll( self, args ):
109
        "Ping between all hosts."
110
        self.mn.pingAll()
111

  
112
    def pingPair( self, args ):
113
        "Ping between first two hosts, useful for testing."
114
        self.mn.pingPair()
115

  
116
    def iperf( self, args ):
117
        "Simple iperf TCP test between two hosts."
118
        self.mn.iperf()
119

  
120
    def iperfUdp( self, args ):
121
        "Simple iperf UDP test between two hosts."
122
        udpBw = args[ 0 ] if len( args ) else '10M'
123
        self.mn.iperfUdp( udpBw )
124

  
125
    def intfs( self, args ):
126
        "List interfaces."
127
        for node in self.mn.nodes.values():
128
            lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) )
129

  
130
    def dump( self, args ):
131
        "Dump node info."
132
        for node in self.mn.nodes.values():
133
            lg.info( '%s\n' % node )
134

  
135
    # Re-enable pylint "Unused argument: 'arg's'" messages.
136
    # pylint: enable-msg=W0613
137

  
138
    def run( self ):
139
        "Read and execute commands."
140
        lg.warn( '*** Starting CLI:\n' )
141
        while True:
142
            lg.warn( 'mininet> ' )
143
            inputLine = sys.stdin.readline()
144
            if inputLine == '':
145
                break
146
            if inputLine[ -1 ] == '\n':
147
                inputLine = inputLine[ :-1 ]
148
            cmd = inputLine.split( ' ' )
149
            first = cmd[ 0 ]
150
            rest = cmd[ 1: ]
151
            if first in self.cmds and hasattr( self, first ):
152
                getattr( self, first )( rest )
153
            elif first in self.nodemap and rest != []:
154
                node = self.nodemap[ first ]
155
                # Substitute IP addresses for node names in command
156
                rest = [ self.nodemap[ arg ].IP()
157
                    if arg in self.nodemap else arg
158
                    for arg in rest ]
159
                rest = ' '.join( rest )
160
                # Interactive commands don't work yet, and
161
                # there are still issues with control-c
162
                lg.warn( '*** %s: running %s\n' % ( node.name, rest ) )
163
                node.sendCmd( rest )
164
                while True:
165
                    try:
166
                        done, data = node.monitor()
167
                        lg.info( '%s\n' % data )
168
                        if done:
169
                            break
170
                    except KeyboardInterrupt:
171
                        node.sendInt()
172
            elif first == '':
173
                pass
174
            elif first in [ 'exit', 'quit' ]:
175
                break
176
            elif first == '?':
177
                self.help( rest )
178
            else:
179
                lg.error( 'CLI: unknown node or command: < %s >\n' % first )
180
            #lg.info( '*** CLI: command complete\n' )
181
        return 'exited by user command'
mininet/net.py
52 52
import os
53 53
import re
54 54
import signal
55
from subprocess import call
56
import sys
57 55
from time import sleep
58 56

  
57
from mininet.cli import CLI
59 58
from mininet.log import lg
60 59
from mininet.node import KernelSwitch, OVSKernelSwitch
61 60
from mininet.util import quietRun, fixLimits
......
490 489
    def interact( self ):
491 490
        "Start network and run our simple CLI."
492 491
        self.start()
493
        result = MininetCLI( self )
492
        result = CLI( self )
494 493
        self.stop()
495 494
        return result
496

  
497

  
498
class MininetCLI( object ):
499
    "Simple command-line interface to talk to nodes."
500
    cmds = [ '?', 'help', 'nodes', 'net', 'sh', 'ping_all', 'exit', \
501
            'ping_pair', 'iperf', 'iperf_udp', 'intfs', 'dump' ]
502

  
503
    def __init__( self, mininet ):
504
        self.mn = mininet
505
        self.nodemap = {} # map names to Node objects
506
        for node in self.mn.nodes.values():
507
            self.nodemap[ node.name ] = node
508
        for cname, cnode in self.mn.controllers.iteritems():
509
            self.nodemap[ cname ] = cnode
510
        self.nodelist = self.nodemap.values()
511
        self.run()
512

  
513
    # Disable pylint "Unused argument: 'arg's'" messages.
514
    # Each CLI function needs the same interface.
515
    # pylint: disable-msg=W0613
516

  
517
    # Commands
518
    def help( self, args ):
519
        "Semi-useful help for CLI."
520
        helpStr = ( 'Available commands are:' + str( self.cmds ) + '\n'
521
                   'You may also send a command to a node using:\n'
522
                   '  <node> command {args}\n'
523
                   'For example:\n'
524
                   '  mininet> h0 ifconfig\n'
525
                   '\n'
526
                   'The interpreter automatically substitutes IP '
527
                   'addresses\n'
528
                   'for node names, so commands like\n'
529
                   '  mininet> h0 ping -c1 h1\n'
530
                   'should work.\n'
531
                   '\n\n'
532
                   'Interactive commands are not really supported yet,\n'
533
                   'so please limit commands to ones that do not\n'
534
                   'require user interaction and will terminate\n'
535
                   'after a reasonable amount of time.\n' )
536
        print( helpStr )
537

  
538
    def nodes( self, args ):
539
        "List all nodes."
540
        nodes = ' '.join( [ node.name for node in sorted( self.nodelist ) ] )
541
        lg.info( 'available nodes are: \n%s\n' % nodes )
542

  
543
    def net( self, args ):
544
        "List network connections."
545
        for switchDpid in self.mn.topo.switches():
546
            switch = self.mn.nodes[ switchDpid ]
547
            lg.info( '%s <->', switch.name )
548
            for intf in switch.intfs:
549
                node = switch.connection[ intf ]
550
                lg.info( ' %s' % node.name )
551
            lg.info( '\n' )
552

  
553
    def sh( self, args ):
554
        "Run an external shell command"
555
        call( [ 'sh', '-c' ] + args )
556

  
557
    def pingAll( self, args ):
558
        "Ping between all hosts."
559
        self.mn.pingAll()
560

  
561
    def pingPair( self, args ):
562
        "Ping between first two hosts, useful for testing."
563
        self.mn.pingPair()
564

  
565
    def iperf( self, args ):
566
        "Simple iperf TCP test between two hosts."
567
        self.mn.iperf()
568

  
569
    def iperfUdp( self, args ):
570
        "Simple iperf UDP test between two hosts."
571
        udpBw = args[ 0 ] if len( args ) else '10M'
572
        self.mn.iperfUdp( udpBw )
573

  
574
    def intfs( self, args ):
575
        "List interfaces."
576
        for node in self.mn.nodes.values():
577
            lg.info( '%s: %s\n' % ( node.name, ' '.join( node.intfs ) ) )
578

  
579
    def dump( self, args ):
580
        "Dump node info."
581
        for node in self.mn.nodes.values():
582
            lg.info( '%s\n' % node )
583

  
584
    # Re-enable pylint "Unused argument: 'arg's'" messages.
585
    # pylint: enable-msg=W0613
586

  
587
    def run( self ):
588
        "Read and execute commands."
589
        lg.warn( '*** Starting CLI:\n' )
590
        while True:
591
            lg.warn( 'mininet> ' )
592
            inputLine = sys.stdin.readline()
593
            if inputLine == '':
594
                break
595
            if inputLine[ -1 ] == '\n':
596
                inputLine = inputLine[ :-1 ]
597
            cmd = inputLine.split( ' ' )
598
            first = cmd[ 0 ]
599
            rest = cmd[ 1: ]
600
            if first in self.cmds and hasattr( self, first ):
601
                getattr( self, first )( rest )
602
            elif first in self.nodemap and rest != []:
603
                node = self.nodemap[ first ]
604
                # Substitute IP addresses for node names in command
605
                rest = [ self.nodemap[ arg ].IP()
606
                    if arg in self.nodemap else arg
607
                    for arg in rest ]
608
                rest = ' '.join( rest )
609
                # Interactive commands don't work yet, and
610
                # there are still issues with control-c
611
                lg.warn( '*** %s: running %s\n' % ( node.name, rest ) )
612
                node.sendCmd( rest )
613
                while True:
614
                    try:
615
                        done, data = node.monitor()
616
                        lg.info( '%s\n' % data )
617
                        if done:
618
                            break
619
                    except KeyboardInterrupt:
620
                        node.sendInt()
621
            elif first == '':
622
                pass
623
            elif first in [ 'exit', 'quit' ]:
624
                break
625
            elif first == '?':
626
                self.help( rest )
627
            else:
628
                lg.error( 'CLI: unknown node or command: < %s >\n' % first )
629
            #lg.info( '*** CLI: command complete\n' )
630
        return 'exited by user command'

Also available in: Unified diff