Skip to content

Commit 723d068

Browse files
author
Brandon Heller
committed
Add static code checking for style and errors
This required a change to logging, which now uses a singleton pattern. For all future checkins, 'make codecheck' should pass.
1 parent ca58c89 commit 723d068

11 files changed

Lines changed: 208 additions & 140 deletions

File tree

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
all: codecheck test
2+
13
clean:
24
rm -rf build dist build *.egg-info *.pyc
35

6+
codecheck: mininet/*.py mininet/test/*.py
7+
pyflakes mininet/*.py mininet/test/*.py bin/*.py
8+
pylint --rcfile=.pylint mininet/*.py mininet/test/*.py bin/*.py
9+
pep8 --ignore=E251 mininet/*.py mininet/test/*.py bin/*.py
10+
411
test: mininet/*.py mininet/test/*.py
5-
mininet/test/test_nets.py
12+
mininet/test/test_nets.py

bin/mn_clean.py

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,47 @@
1212
"""
1313

1414
from subprocess import Popen, PIPE
15-
import re
1615

17-
from mininet.util import quietRun
1816
from mininet.xterm import cleanUpScreens
1917

20-
def sh( cmd ):
21-
"Print a command and send it to the shell"
22-
print cmd
23-
return Popen( [ '/bin/sh', '-c', cmd ],
24-
stdout=PIPE ).communicate()[ 0 ]
2518

26-
19+
def sh(cmd):
20+
"Print a command and send it to the shell"
21+
print cmd
22+
return Popen(['/bin/sh', '-c', cmd], stdout=PIPE).communicate()[0]
23+
24+
2725
def cleanup():
28-
"""Clean up junk which might be left over from old runs;
29-
do fast stuff before slow dp and link removal!"""
30-
31-
print "*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
32-
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
33-
zombies += 'udpbwtest'
34-
# Note: real zombie processes can't actually be killed, since they
35-
# are already (un)dead. Then again,
36-
# you can't connect to them either, so they're mostly harmless.
37-
sh( 'killall -9 ' + zombies + ' 2> /dev/null' )
38-
39-
print "*** Removing junk from /tmp"
40-
sh( 'rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log' )
41-
42-
print "*** Removing old screen sessions"
43-
cleanUpScreens()
44-
45-
print "*** Removing excess kernel datapaths"
46-
dps = sh( "ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'" ).split( '\n')
47-
for dp in dps:
48-
if dp != '': sh( 'dpctl deldp ' + dp )
49-
50-
print "*** Removing all links of the pattern foo-ethX"
51-
links = sh( "ip link show | egrep -o '(\w+-eth\w+)'" ).split( '\n' )
52-
for link in links:
53-
if link != '': sh( "ip link del " + link )
54-
55-
print "*** Cleanup complete."
26+
"""Clean up junk which might be left over from old runs;
27+
do fast stuff before slow dp and link removal!"""
28+
29+
print "*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes"
30+
zombies = 'controller ofprotocol ofdatapath ping nox_core lt-nox_core '
31+
zombies += 'udpbwtest'
32+
# Note: real zombie processes can't actually be killed, since they
33+
# are already (un)dead. Then again,
34+
# you can't connect to them either, so they're mostly harmless.
35+
sh('killall -9 ' + zombies + ' 2> /dev/null')
36+
37+
print "*** Removing junk from /tmp"
38+
sh('rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log')
39+
40+
print "*** Removing old screen sessions"
41+
cleanUpScreens()
42+
43+
print "*** Removing excess kernel datapaths"
44+
dps = sh("ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'").split('\n')
45+
for dp in dps:
46+
if dp != '':
47+
sh('dpctl deldp ' + dp)
48+
49+
print "*** Removing all links of the pattern foo-ethX"
50+
links = sh("ip link show | egrep -o '(\w+-eth\w+)'").split('\n')
51+
for link in links:
52+
if link != '':
53+
sh("ip link del " + link)
54+
55+
print "*** Cleanup complete."
5656

5757
if __name__ == "__main__":
58-
cleanup()
58+
cleanup()

bin/mn_run.py

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,51 +13,52 @@
1313
except ImportError:
1414
USE_RIPCORD = False
1515

16-
from mininet.logging_mod import lg, set_loglevel, LEVELS
16+
from mininet.logging_mod import lg, LEVELS
1717
from mininet.net import Mininet, init
1818
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
1919
from mininet.node import RemoteController, UserSwitch
2020
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
2121

2222
# built in topologies, created only when run
2323
TOPO_DEF = 'minimal'
24-
TOPOS = {'minimal' : (lambda: SingleSwitchTopo(k = 2)),
25-
'reversed' : (lambda: SingleSwitchReversedTopo(k = 2)),
26-
'single4' : (lambda: SingleSwitchTopo(k = 4)),
27-
'single100' : (lambda: SingleSwitchTopo(k = 100)),
28-
'linear2' : (lambda: LinearTopo(k = 2)),
29-
'linear100' : (lambda: LinearTopo(k = 100))}
24+
TOPOS = {'minimal': (lambda: SingleSwitchTopo(k = 2)),
25+
'reversed': (lambda: SingleSwitchReversedTopo(k = 2)),
26+
'single4': (lambda: SingleSwitchTopo(k = 4)),
27+
'single100': (lambda: SingleSwitchTopo(k = 100)),
28+
'linear2': (lambda: LinearTopo(k = 2)),
29+
'linear100': (lambda: LinearTopo(k = 100))}
3030
if USE_RIPCORD:
3131
TOPOS_RIPCORD = {
32-
'tree16' : (lambda: TreeTopo(depth = 3, fanout = 4)),
33-
'tree64' : (lambda: TreeTopo(depth = 4, fanout = 4)),
34-
'tree1024' : (lambda: TreeTopo(depth = 3, fanout = 32)),
35-
'fattree4' : (lambda: FatTreeTopo(k = 4)),
36-
'fattree6' : (lambda: FatTreeTopo(k = 6)),
37-
'vl2' : (lambda: VL2Topo(da = 4, di = 4))}
32+
'tree16': (lambda: TreeTopo(depth = 3, fanout = 4)),
33+
'tree64': (lambda: TreeTopo(depth = 4, fanout = 4)),
34+
'tree1024': (lambda: TreeTopo(depth = 3, fanout = 32)),
35+
'fattree4': (lambda: FatTreeTopo(k = 4)),
36+
'fattree6': (lambda: FatTreeTopo(k = 6)),
37+
'vl2': (lambda: VL2Topo(da = 4, di = 4))}
3838
TOPOS.update(TOPOS_RIPCORD)
3939

4040
SWITCH_DEF = 'kernel'
41-
SWITCHES = {'kernel' : KernelSwitch,
42-
'user' : UserSwitch}
41+
SWITCHES = {'kernel': KernelSwitch,
42+
'user': UserSwitch}
4343

4444
HOST_DEF = 'process'
45-
HOSTS = {'process' : Host}
45+
HOSTS = {'process': Host}
4646

4747
CONTROLLER_DEF = 'ref'
4848
# a and b are the name and inNamespace params.
49-
CONTROLLERS = {'ref' : Controller,
50-
'nox_dump' : lambda a, b: NOX(a, b, 'packetdump'),
51-
'nox_pysw' : lambda a, b: NOX(a, b, 'pyswitch'),
52-
'remote' : lambda a, b: None,
53-
'none' : lambda a, b: None}
49+
CONTROLLERS = {'ref': Controller,
50+
'nox_dump': lambda a, b: NOX(a, b, 'packetdump'),
51+
'nox_pysw': lambda a, b: NOX(a, b, 'pyswitch'),
52+
'remote': lambda a, b: None,
53+
'none': lambda a, b: None}
5454

5555
# optional tests to run
5656
TESTS = ['cli', 'build', 'ping_all', 'ping_pair', 'iperf', 'all', 'iperf_udp']
5757

58+
5859
def add_dict_option(opts, choices_dict, default, name, help_str = None):
5960
'''Convenience function to add choices dicts to OptionParser.
60-
61+
6162
@param opts OptionParser instance
6263
@param choices_dict dictionary of valid choices, must include default
6364
@param default default choice key
@@ -82,14 +83,14 @@ class MininetRunner(object):
8283
def __init__(self):
8384
'''Init.'''
8485
self.options = None
85-
86+
8687
self.parse_args()
8788
self.setup()
8889
self.begin()
8990

9091
def parse_args(self):
9192
'''Parse command-line args and return options object.
92-
93+
9394
@return opts parse options dict
9495
'''
9596
opts = OptionParser()
@@ -124,7 +125,7 @@ def setup(self):
124125
'''Setup and validate environment.'''
125126

126127
# set logging verbosity
127-
set_loglevel(self.options.verbosity)
128+
lg.set_loglevel(self.options.verbosity)
128129

129130
# validate environment setup
130131
init()

mininet/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
'''Docstring to silence pylint; ignores --ignore option for __init__.py'''

mininet/logging_mod.py

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'''Logging functions for Mininet.'''
22

33
import logging
4+
from logging import Logger
45
import types
56

67
LEVELS = {'debug': logging.DEBUG,
@@ -16,7 +17,6 @@
1617
LOG_MSG_FORMAT = '%(message)s'
1718

1819

19-
2020
# Modified from python2.5/__init__.py
2121
class StreamHandlerNoNewline(logging.StreamHandler):
2222
'''StreamHandler that doesn't print newlines by default.
@@ -53,47 +53,79 @@ def emit(self, record):
5353
self.handleError(record)
5454

5555

56-
def set_loglevel(level_name = None):
57-
'''Setup loglevel.
56+
class Singleton(type):
57+
'''Singleton pattern from Wikipedia
58+
59+
See http://en.wikipedia.org/wiki/Singleton_pattern#Python
60+
61+
Intended to be used as a __metaclass_ param, as shown for the class below.
62+
63+
Changed cls first args to mcs to satsify pylint.
64+
'''
65+
66+
def __init__(mcs, name, bases, dict_):
67+
super(Singleton, mcs).__init__(name, bases, dict_)
68+
mcs.instance = None
69+
70+
def __call__(mcs, *args, **kw):
71+
if mcs.instance is None:
72+
mcs.instance = super(Singleton, mcs).__call__(*args, **kw)
73+
return mcs.instance
74+
75+
76+
class MininetLogger(Logger, object):
77+
'''Mininet-specific logger
78+
79+
Enable each mininet .py file to with one import:
80+
81+
from mininet.logging_mod import lg
82+
83+
...get a default logger that doesn't require one newline per logging call.
5884
59-
@param level_name level name from LEVELS
85+
Inherit from object to ensure that we have at least one new-style base
86+
class, and can then use the __metaclass__ directive, to prevent this error:
87+
88+
TypeError: Error when calling the metaclass bases
89+
a new-style class can't have only classic bases
90+
91+
If Python2.5/logging/__init__.py defined Filterer as a new-style class,
92+
via Filterer(object): rather than Filterer, we wouldn't need this.
93+
94+
Use singleton pattern to ensure only one logger is ever created.
6095
'''
61-
level = LOG_LEVEL_DEFAULT
62-
if level_name != None:
63-
if level_name not in LEVELS:
64-
raise Exception('unknown loglevel seen in set_loglevel')
65-
else:
66-
level = LEVELS.get(level_name, level)
67-
68-
lg.setLevel(level)
69-
if len(lg.handlers) != 1:
70-
raise Exception('lg.handlers length not zero in logging_mod')
71-
lg.handlers[0].setLevel(level)
72-
73-
74-
def _setup_logging():
75-
'''Setup logging for Mininet.'''
76-
global lg
77-
78-
# create logger if first time
79-
if 'lg' not in globals():
80-
lg = logging.getLogger('mininet')
96+
__metaclass__ = Singleton
97+
98+
def __init__(self):
99+
100+
Logger.__init__(self, "mininet")
101+
81102
# create console handler
82103
ch = StreamHandlerNoNewline()
83104
# create formatter
84105
formatter = logging.Formatter(LOG_MSG_FORMAT)
85106
# add formatter to ch
86107
ch.setFormatter(formatter)
87108
# add ch to lg
88-
lg.addHandler(ch)
89-
else:
90-
raise Exception('setup_logging called twice')
109+
self.addHandler(ch)
110+
111+
self.set_loglevel()
112+
113+
def set_loglevel(self, levelname = None):
114+
'''Setup loglevel.
115+
116+
Convenience function to support lowercase names.
117+
118+
@param level_name level name from LEVELS
119+
'''
120+
level = LOG_LEVEL_DEFAULT
121+
if levelname != None:
122+
if levelname not in LEVELS:
123+
raise Exception('unknown loglevel seen in set_loglevel')
124+
else:
125+
level = LEVELS.get(levelname, level)
91126

92-
set_loglevel()
127+
self.setLevel(level)
128+
self.handlers[0].setLevel(level)
93129

94130

95-
# There has to be some better way to ensure we only ever have one logging
96-
# variable. If this check isn't in, the order in which imports occur can
97-
# affect whether a program runs, because the variable lg may get rebound.
98-
if 'lg' not in globals():
99-
_setup_logging()
131+
lg = MininetLogger()

0 commit comments

Comments
 (0)