1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00

Some fixes + enhancements

Ticket: CM-1438
Reviewed By:
Testing Done: Tested installing new ifupdown on the box

- fixed a few things in ifquery
- added new perfmode to skip some of the checks (useful during boot when there is
  no previous state)
- updated doc dir with example
- Added README, TODO and KNOWN_ISSUES file
This commit is contained in:
roopa
2013-11-10 22:35:40 -08:00
parent a6f80f0e0b
commit eab25b7c62
10 changed files with 215 additions and 72 deletions

8
KNOWN_ISSUES Normal file
View File

@@ -0,0 +1,8 @@
- Some state issues if multiple interfaces for the same interface config blocks are present for the same interface
- `ifquery -r` can give wrong result if dhclient is running + static addresses are configured
- `ifquery -r` status is success for success case and also for cases where there
is no support for query yet
- setup.py has ifupdown listed in data section instead of scripts: This is because default location for scripts is /usr/bin/. And ifupdown default location is /sbin. With newer versions we can specify --install-scripts directory. This needs to be fixed then.
-

17
README
View File

@@ -37,3 +37,20 @@ It expects a few things from the pluggable modules:
- get_dependent_ifacenames() : must return a list of interfaces the
interface is dependent on. This is used to build the dependency list
for sorting and executing interfaces in parallel.
install instructions
====================
- remove existing ifupdown package
dpkg -r ifupdown
- download python-ifupdown2*.deb's and install
- or apt-get from testing repo
apt-get install python-ifupdown2
apt-get install python-ifupdown2-addons
- or install from deb
dpkg -i python-ifupdown2-<ver>.deb
dpkg -i python-ifupdown2-addons-<ver>.deb

13
TODO
View File

@@ -1,7 +1,13 @@
TODO:
====
- support for /etc/networking.defaults
- Implement the ifupdown interface mapping feature
- implement legacy ifupdown mapping feature
- support for the following ifupdown options:
-o OPTION=VALUE set OPTION to VALUE as though it were in
/etc/network/interfaces
--no-mappings don't run any mappings
--no-scripts don't run any hook scripts
- Fix parallel implementation
- Test all original ifupdown options for compatibility
- Test scale
@@ -17,3 +23,8 @@ TODO:
VERBOSITY indicates whether --verbose was used; set to 1 if so, 0 if not.
PATH the command search path: /usr/local/sbin:/usr/local/bin:
/usr/sbin:/usr/bin:/sbin:/bin
- option to pretty print dependency graph. Also an option to output in dot format
- make inet, inet6 address handling smarter: query for existing addresses and
decide

54
docs/examples/interfaces Normal file
View File

@@ -0,0 +1,54 @@
# The loopback network interface
auto lo
iface lo
address 10.0.18.3/24
address6 2002::2/128
# The primary network interface
auto eth0
iface eth0 inet dhcp
auto bond3
iface bond3 inet static
address 100.0.0.4/24
bond-slaves swp2 swp3
bond-mode 802.3ad
bond-miimon 100
bond-use-carrier 1
bond-lacp-rate 1
bond-min-links 1
bond-xmit_hash_policy layer3+4
auto br1000
iface br1000 inet static
address 14.0.0.1/24
mstpctl_ports glob swp1-4.1000
mstpctl_stp on
mstpctl_treeprio 32768
mstpctl_ageing 200
auto swp5
iface swp5 inet static
address 15.0.0.2/24
auto swp6
iface swp6
address 11.0.3.2/24
post-up ip route add 11.0.0.0/8 via 11.0.3.1
post-up ip route add 12.0.0.0/8 via 11.0.3.1
post-up ip route add 13.0.0.0/8 via 11.0.3.1
post-up ip route add 60.0.0.0/8 via 11.0.3.1
pre-down ip route del 11.0.0.0/8 via 11.0.3.1
pre-down ip route del 12.0.0.0/8 via 11.0.3.1
pre-down ip route del 13.0.0.0/8 via 11.0.3.1
pre-down ip route del 60.0.0.0/8 via 11.0.3.1
auto br1001
iface br1001 inet static
address 14.0.0.2/24
bridge_ports glob swp7-8.1000
bridge_stp on
bridge_ageing 200
bridge_pathcosts swp5.1000=100
bridge_portprios swp5.1000=10

View File

@@ -297,18 +297,29 @@ class iface():
if len(env) > 0:
self.set_env(env)
def update_config(self, attr_name, attr_value, attr_status=None):
def update_config(self, attr_name, attr_value, attr_status=0):
if attr_value is None:
attr_value = ''
if attr_status == None:
if attr_status != 0:
self.set_status(ifaceStatus.ERROR)
else:
if self.get_status() != ifaceStatus.ERROR:
self.set_status(ifaceStatus.SUCCESS)
if self.config.get(attr_name) is not None:
self.config[attr_name].append(attr_value)
else:
self.config[attr_name] = [attr_value]
""" XXX: If status needs to be encoded in the query string
if attr_status == 0:
self.set_status(attr
attr_status_str = ''
elif attr_status == 0:
attr_status_str = ' (success)'
elif attr_status != 0:
attr_status_str = ' (error)'
self.config[attr_name] = attr_value + attr_status_str
self.config[attr_name] = attr_value + attr_status_str """
def dump_raw(self, logger):
indent = ' '
@@ -316,12 +327,6 @@ class iface():
for i in range(1, len(self.raw_lines)):
print (indent + self.raw_lines[i])
def dump_state_pretty(self, logger):
indent = ' '
logger.info('iface %s' %self.get_name())
for attr_name, attr_value in self.get_config_current().items():
print (indent + '%s' %attr_name + ' %s' %attr_value)
def dump(self, logger):
indent = '\t'
logger.info(self.get_name() + ' : {')
@@ -357,8 +362,9 @@ class iface():
config = self.get_config()
if config is not None:
for cname, cvalue in config.items():
outbuf += indent + '%s' %cname + ' %s\n' %cvalue
for cname, cvaluelist in config.items():
for cv in cvaluelist:
outbuf += indent + '%s' %cname + ' %s\n' %cv
#outbuf += ('%s' %indent + '%s' %self.get_state_str() +
# ' %s' %self.get_status_str())

View File

@@ -17,12 +17,9 @@ import sys, traceback
class ifupdown_main():
# Flags
FORCE = False
NOWAIT = False
DRYRUN = False
NODEPENDS = False
ALL = False
STATE_CHECK = True
STATE_CHECK = False
modules_dir='/etc/network'
builtin_modules_dir='/usr/share/ifupdownaddons'
@@ -54,11 +51,18 @@ class ifupdown_main():
('post-down' , OrderedDict({}))])}
def __init__(self):
def __init__(self, force=False, dryrun=False, nowait=False,
perfmode=False, nodepends=False, njobs=1):
self.logger = logging.getLogger('ifupdown')
self.FORCE = force
self.DRYRUN = dryrun
self.NOWAIT = nowait
self.PERFMODE = perfmode
self.NODEPENDS = nodepends
self.ifaces = OrderedDict()
self.njobs = 1
self.njobs = njobs
self.pp = pprint.PrettyPrinter(indent=4)
self.load_modules_builtin(self.builtin_modules_dir)
self.load_modules(self.modules_dir)
@@ -102,6 +106,14 @@ class ifupdown_main():
def get_dryrun(self):
return self.DRYRUN
def set_perfmode(self, perfmode):
if perfmode == True:
self.logger.debug('setting perfmode to true')
self.PERFMODE = perfmode
def get_perfmode(self):
return self.PERFMODE
def set_nowait(self, nowait):
if nowait == True:
self.logger.debug('setting dryrun to true')
@@ -326,7 +338,7 @@ class ifupdown_main():
self.logger.debug('saved module %s' %mkind +
' %s' %mname + ' %s' %mftype)
else:
self.logger.warn('ignoring module %s' %mkind + ' %s' %msubkind +
self.logger.info('ignoring module %s' %mkind + ' %s' %msubkind +
' %s' %mname + ' of type %s' %mftype)
@@ -355,10 +367,11 @@ class ifupdown_main():
mclass = getattr(m, mname)
except:
raise
minstance = mclass(force=self.get_force(),
dryrun=self.get_dryrun(),
nowait=self.get_nowait())
nowait=self.get_nowait(),
perfmode=self.get_perfmode())
ops = minstance.get_ops()
for op in ops:
if re.search('up', op) is not None:
@@ -480,7 +493,7 @@ class ifupdown_main():
err_iface += ' ' + i
if len(err_iface) != 0:
self.logger.error('cound not find ifaces: %s' %err_iface)
self.logger.error('did not find interfaces: %s' %err_iface)
return -1
return 0
@@ -579,7 +592,7 @@ class ifupdown_main():
if ifacenames is not None:
# If iface list is given, always check if iface is present
if self.validate_ifaces(ifacenames) != 0:
raise Exception('all or some ifaces not found')
raise Exception('all or some interfaces not found')
# if iface list not given by user, assume all from config file
if ifacenames is None: ifacenames = self.ifaceobjdict.keys()
@@ -609,7 +622,11 @@ class ifupdown_main():
if op == 'query':
if query_state == 'curr':
return self.print_ifaceobjscurr_pretty(filtered_ifacenames)
# print curr state of all interfaces
ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames)
if ret != 0:
# if any of the object has an error, signal that silently
raise Exception('')
return
# Update persistant iface states
@@ -652,11 +669,24 @@ class ifupdown_main():
ifaceobjs = self.get_iface_objs(i)
for io in ifaceobjs:
io.dump_raw(self.logger)
print '\n'
def print_ifaceobjscurr_pretty(self, ifacenames):
""" Dumps current running state of interfaces.
returns 1 if any of the interface has an error,
else returns 0
"""
ret = 0
for i in ifacenames:
ifaceobj = self.get_ifaceobjcurr(i)
ifaceobj.dump_pretty(self.logger)
if ifaceobj.get_status() == ifaceStatus.ERROR:
ret = 1
return ret
def print_ifaceobjs_saved_state_pretty(self, ifacenames):
self.statemanager.print_state_pretty(ifacenames, self.logger)

View File

@@ -58,10 +58,13 @@ class networkInterfaces():
def process_source(self, lines, cur_idx, lineno):
# Support regex
self.logger.debug('process_source ..%s' %lines[cur_idx])
sourced_file = lines[cur_idx].split(' ', 2)[1]
if sourced_file is not None:
self.logger.debug('process_source ..%s' %sourced_file)
for f in glob.glob(sourced_file):
self.read_file(self, sourced_file)
self.logger.info('Reading sourced file %s' %f)
self.read_file(f)
else:
self.logger.warn('unable to read source line at %d', lineno)
@@ -95,18 +98,21 @@ class networkInterfaces():
if self.ignore_line(l) == 1:
continue
ifaceobj.raw_lines.append(l)
if self.is_keyword(l.split()[0]) == True:
line_idx -= 1
break
(attr_name, attrs) = l.split(' ', 1)
ifaceobj.raw_lines.append(l)
if iface_config.get(attr_name) == None:
iface_config[attr_name] = [attrs.strip(' ')]
attrs = l.split(' ', 1)
if len(attrs) < 2:
self.logger.warn('invalid syntax at line %d' %(line_idx + 1))
continue
if iface_config.get(attrs[0]) == None:
iface_config[attrs[0]] = [attrs[1].strip(' ')]
else:
iface_config[attr_name].append(attrs.strip(' '))
iface_config[attrs[0]].append(attrs[1].strip(' '))
lines_consumed = line_idx - cur_idx
@@ -175,7 +181,8 @@ class networkInterfaces():
self.logger.debug('reading ifaces_file %s' %ifaces_file)
with open(ifaces_file) as f:
lines = f.readlines()
raw_lines = f.readlines()
lines = [l.strip(' \n') for l in raw_lines]
while (line_idx < len(lines)):
lineno = lineno + 1
@@ -184,8 +191,7 @@ class networkInterfaces():
line_idx += 1
continue
l = lines[line_idx].strip('\n ')
words = l.split()
words = lines[line_idx].split()
# Check if first element is a supported keyword
if self.is_keyword(words[0]):

View File

@@ -223,26 +223,27 @@ class stateManager():
return True
def print_state(self, ifaceobj, prefix, indent):
print ('%s' %indent + '%s' %prefix +
'%s' %firstifaceobj.get_state_str(),
', %s' %firstifaceobj.get_status_str())
print (indent + '%s' %prefix +
'%s' %ifaceobj.get_state_str() +
', %s' %ifaceobj.get_status_str())
def print_state_pretty(self, ifacenames, logger):
for ifacename in ifacenames:
old_ifaceobjs = self.ifaceobjdict.get(ifacename)
if old_ifaceobjs is not None:
firstifaceobj = old_ifaceobjs[0]
self.print_state(self, firstifaceobj,
'%s: ' %firstifaceobj.get_name(), indent)
self.print_state(firstifaceobj,
'%s: ' %firstifaceobj.get_name(), '')
def print_state_detailed_pretty(self, ifacenames, logger):
indent = '\t'
for ifacename in ifacenames:
old_ifaceobjs = self.ifaceobjdict.get(ifacename)
if old_ifaceobjs is not None:
for i in old_ifaceobjs:
i.dump_pretty(logger)
self.print_state(self, firstifaceobj, '', indent)
self.print_state(i, '', indent)
print '\n'
def dump(self, ifacenames=None):
print 'iface state:'

View File

@@ -12,25 +12,20 @@ lockfile="/run/network/.lock"
logger = None
def run(args, op):
logger.debug('args = %s' %str(args))
try:
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdown_main()
if op == 'up' or op == 'down':
if args.force == True:
ifupdown_handle.set_force(args.force)
if args.jobs > 0:
ifupdown_handle.set_njobs(args.jobs)
if args.dryrun == True:
ifupdown_handle.set_dryrun(args.dryrun)
if args.nodepends == True:
ifupdown_handle.set_nodepends(args.nodepends)
ifupdown_handle = ifupdown_main(force=args.force,
dryrun=args.noact,
nodepends=args.nodepends,
perfmode=args.perfmode,
njobs=args.jobs)
elif op == 'query':
ifupdown_handle = ifupdown_main(dryrun=args.noact,
nodepends=args.nodepends,
perfmode=args.perfmode)
iflist = args.iflist
if len(args.iflist) == 0:
@@ -38,9 +33,9 @@ def run(args, op):
logger.debug('calling %s' %op + ' for all interfaces ..')
if op == 'up':
ifupdown_handle.up(args.all, args.allow, iflist)
ifupdown_handle.up(args.all, args.CLASS, iflist)
elif op == 'down':
ifupdown_handle.down(args.all, args.allow, iflist)
ifupdown_handle.down(args.all, args.CLASS, iflist)
elif op == 'query':
if args.curstate == True:
qstate='curr'
@@ -50,12 +45,11 @@ def run(args, op):
qstate='presumeddetailed'
else:
qstate=None
ifupdown_handle.query(args.all, args.allow, iflist,
ifupdown_handle.query(args.all, args.CLASS, iflist,
query_state=qstate)
except:
raise
def init(args):
global logger
@@ -69,23 +63,24 @@ def init(args):
try:
logging.basicConfig(level=log_level,
format='%(message)s')
format='%(levelname)s: %(message)s')
logger = logging.getLogger('ifupdown')
except:
raise
def deinit():
print 'deinit called'
{}
def update_argparser(argparser):
argparser.add_argument('iflist', metavar='IFACE',
nargs='*', help='interfaces list')
argparser.add_argument('-a', '--all', action='store_true',
help='operate on all interfaces')
argparser.add_argument('-n', '--dry-run', dest='dryrun',
action='store_true', help='dry run')
help='process all interfaces marked \"auto\"')
argparser.add_argument('-n', '--no-act', dest='noact',
action='store_true', help='print out what would happen,' +
'but don\'t do it')
argparser.add_argument('-v', '--verbose', dest='verbose',
action='store_true', help='verbose')
argparser.add_argument('-d', '--debug', dest='debug',
@@ -94,10 +89,12 @@ def update_argparser(argparser):
argparser.add_argument('-q', '--quiet', dest='quiet',
action='store_true',
help=argparse.SUPPRESS)
argparser.add_argument('--allow', dest='allow',
help='allow class')
argparser.add_argument('--allow', dest='CLASS',
help='ignore non-\"allow-CLASS\" interfaces')
argparser.add_argument('--nodepends', dest='nodepends',
action='store_true', help='dont follow dependents')
action='store_true', help=argparse.SUPPRESS)
argparser.add_argument('--performance-mode', dest='perfmode',
action='store_true', help=argparse.SUPPRESS)
def update_ifupdown_argparser(argparser):
@@ -115,10 +112,16 @@ def update_ifdown_argparser(argparser):
def update_ifquery_argparser(argparser):
group = argparser.add_mutually_exclusive_group(required=False)
group.add_argument('-s', '--query-state', dest='curstate',
action='store_true', help=argparse.SUPPRESS)
group.add_argument('-r', '--running-state', dest='curstate',
action='store_true',
help='query running state of an interface')
# presumed-state is state maintained by ifupdown
group.add_argument('--presumed-state', dest='presumedstate',
action='store_true', help=argparse.SUPPRESS)
# presumed-state-detailed prints all details about the object stored
# by ifupdown
group.add_argument('--presumed-state-detailed',
dest='presumedstatedetailed',
action='store_true', help=argparse.SUPPRESS)
@@ -161,14 +164,17 @@ def main(argv):
init(args)
run(args, op)
except Exception, e:
if str(e) == '':
exit(1)
if args.debug == True:
raise
else:
logger.error(str(e))
exit(1)
finally:
deinit()
if __name__ == "__main__":
if not os.geteuid() == 0:
@@ -176,6 +182,7 @@ if __name__ == "__main__":
exit(1)
"""
XXX: Cannot use this. A spawned dhclient process can hold the lock
if not utilities.lockFile(lockfile):
print 'Another instance of this program is already running.'
exit(0)

View File

@@ -12,5 +12,8 @@ setup(name='ifupdown2',
data_files=[('share/man/man8/',
['man/ifup.8', 'man/ifdown.8', 'man/ifquery.8']),
('/etc/init.d/',
['init.d/networking'])]
['init.d/networking']),
('/sbin/ifupdown', ['sbin/ifupdown']),
('/usr/share/doc/ifupdown/examples/',
['docs/examples/interfaces'])]
)