From 3dcc1d0eeb4313e5efd5dc37537631cfe287b957 Mon Sep 17 00:00:00 2001 From: roopa Date: Mon, 28 Apr 2014 22:33:33 -0700 Subject: [PATCH] support json input + multiple instance running check Ticket: CM-1438 Reviewed By: Testing Done: Tested sanity and interfaces file in json format --- pkg/iface.py | 39 +++++++++++++++++------- pkg/ifupdownmain.py | 32 ++++++++++++++++---- pkg/networkinterfaces.py | 64 ++++++++++++++++++++++++++++------------ sbin/ifupdown | 41 ++++++++++++++++--------- 4 files changed, 128 insertions(+), 48 deletions(-) diff --git a/pkg/iface.py b/pkg/iface.py index 6f5fd78..731ba84 100644 --- a/pkg/iface.py +++ b/pkg/iface.py @@ -29,8 +29,8 @@ from collections import OrderedDict import logging import json -_tickmark = ' (' + u'\u2713'.encode('utf8') + ')' -_crossmark = ' (' + u'\u2717'.encode('utf8') + ')' +_tickmark = ' (' + u'\u2713' + ')' +_crossmark = ' (' + u'\u2717' + ')' _success_sym = _tickmark _error_sym = _crossmark @@ -128,13 +128,23 @@ class ifaceJsonEncoder(json.JSONEncoder): def default(self, o): retconfig = {} if o.config: - retconfig = dict((k, (v[0] if len(v) == 1 else v)) for k,v in o.config.items()) + retconfig = dict((k, (v[0] if len(v) == 1 else v)) + for k,v in o.config.items()) return OrderedDict({'name' : o.name, 'addr_method' : o.addr_method, 'addr_family' : o.addr_family, 'auto' : o.auto, 'config' : retconfig}) +class ifaceJsonDecoder(): + @classmethod + def json_to_ifaceobj(cls, ifaceattrdict): + ifaceattrdict['config'] = OrderedDict([(k, (v if isinstance(v, list) + else [v])) + for k,v in ifaceattrdict.get('config', + OrderedDict()).items()]) + return iface(attrsdict=ifaceattrdict) + class iface(): """ ifupdown2 interface object class @@ -167,11 +177,8 @@ class iface(): version = '0.1' - def __init__(self): - self.name = None - self.addr_family = None - self.addr_method = None - self.config = OrderedDict() + def __init__(self, attrsdict={}): + self._set_attrs_from_dict(attrsdict) self._config_status = {} self.state = ifaceState.NEW self.status = ifaceStatus.UNKNOWN @@ -180,12 +187,18 @@ class iface(): self.refcnt = 0 self.lowerifaces = None self.upperifaces = None - self.auto = False self.classes = [] self.env = None self.raw_config = [] self.linkstate = None + def _set_attrs_from_dict(self, attrdict): + self.auto = attrdict.get('auto', False) + self.name = attrdict.get('name') + self.addr_family = attrdict.get('addr_family') + self.addr_method = attrdict.get('addr_method') + self.config = attrdict.get('config', OrderedDict()) + def inc_refcnt(self): self.refcnt += 1 @@ -331,7 +344,7 @@ class iface(): self.priv_flags = 0 self.raw_config = [] self.flags |= self._PICKLED - + def dump_raw(self, logger): indent = ' ' if self.auto: @@ -380,6 +393,9 @@ class iface(): else: outbuf += ' %s' %_success_sym if self.status == ifaceStatus.NOTFOUND: + if with_status: + outbuf = (outbuf.encode('utf8') + if isinstance(outbuf, unicode) else outbuf) print outbuf + '\n' return outbuf += '\n' @@ -395,4 +411,7 @@ class iface(): else: outbuf += indent + '%s %s\n' %(cname, cv) idx += 1 + if with_status: + outbuf = (outbuf.encode('utf8') + if isinstance(outbuf, unicode) else outbuf) print outbuf diff --git a/pkg/ifupdownmain.py b/pkg/ifupdownmain.py index fe327f5..8cb3ae2 100644 --- a/pkg/ifupdownmain.py +++ b/pkg/ifupdownmain.py @@ -111,7 +111,9 @@ class ifupdownMain(ifupdownBase): force=False, dryrun=False, nowait=False, perfmode=False, withdepends=False, njobs=1, cache=False, addons_enable=True, statemanager_enable=True, - interfacesfile='/etc/network/interfaces'): + interfacesfile='/etc/network/interfaces', + interfacesfileiobuf=None, + interfacesfileformat='native'): self.logger = logging.getLogger('ifupdown') self.FORCE = force self.DRYRUN = dryrun @@ -121,6 +123,8 @@ class ifupdownMain(ifupdownBase): self.STATEMANAGER_ENABLE = statemanager_enable self.CACHE = cache self.interfacesfile = interfacesfile + self.interfacesfileiobuf = interfacesfileiobuf + self.interfacesfileformat = interfacesfileformat self.config = config self.logger.debug(self.config) @@ -342,7 +346,7 @@ class ifupdownMain(ifupdownBase): return self.ifaceobjdict[ifaceobj.name].append(ifaceobj) - def _module_syntax_checker(self, attrname, attrval): + def _iface_configattr_syntax_checker(self, attrname, attrval): for m, mdict in self.module_attrs.items(): if not mdict: continue @@ -354,13 +358,31 @@ class ifupdownMain(ifupdownBase): pass return False + def _ifaceobj_syntax_checker(self, ifaceobj): + err = False + for attrname in ifaceobj.config: + found = False + for k, v in self.module_attrs.items(): + if v and v.get('attrs', {}).get(attrname): + found = True + break + if not found: + err = True + self.logger.warn('%s: unsupported attribute \'%s\'' %attrname) + continue + return err + def read_iface_config(self): """ Reads default network interface config /etc/network/interfaces. """ nifaces = networkInterfaces(self.interfacesfile, + self.interfacesfileiobuf, + self.interfacesfileformat, template_engine=self.config.get('template_engine'), template_lookuppath=self.config.get('template_lookuppath')) nifaces.subscribe('iface_found', self._save_iface) - nifaces.subscribe('validate', self._module_syntax_checker) + nifaces.subscribe('validateifaceattr', + self._iface_configattr_syntax_checker) + nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker) nifaces.load() def read_old_iface_config(self): @@ -901,8 +923,8 @@ class ifupdownMain(ifupdownBase): self._get_ifaceobjs_pretty(ifacenames, ifaceobjs) if not ifaceobjs: return if format == 'json': - print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=4, - separators=(',', ': ')) + print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, + indent=4, separators=(',', ': ')) else: map(lambda i: i.dump_pretty(), ifaceobjs) diff --git a/pkg/networkinterfaces.py b/pkg/networkinterfaces.py index 4e56d1d..db420e5 100644 --- a/pkg/networkinterfaces.py +++ b/pkg/networkinterfaces.py @@ -25,13 +25,17 @@ class networkInterfaces(): 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6']} def __init__(self, interfacesfile='/etc/network/interfaces', - template_engine=None, template_lookuppath=None): + interfacesfileiobuf=None, interfacesfileformat='native', + template_engine=None, template_lookuppath=None): self.logger = logging.getLogger('ifupdown.' + self.__class__.__name__) self.callbacks = {'iface_found' : None, - 'validate' : None} + 'validateifaceattr' : None, + 'validateifaceobj' : None} self.allow_classes = {} self.interfacesfile = interfacesfile + self.interfacesfileiobuf = interfacesfileiobuf + self.interfacesfileformat = interfacesfileformat self._filestack = [self.interfacesfile] self._template_engine = templateEngine(template_engine, template_lookuppath) @@ -50,7 +54,7 @@ class networkInterfaces(): else: self.logger.error('%s: line%d: %s' %(filename, lineno, msg)) - def _validate_addr_family(self, ifaceobj, lineno): + def _validate_addr_family(self, ifaceobj, lineno=-1): if ifaceobj.addr_family: if not self._addrfams.get(ifaceobj.addr_family): self._parse_error(self._currentfile, lineno, @@ -124,7 +128,7 @@ class networkInterfaces(): attrval, lineno): newattrname = attrname.replace("_", "-") try: - if not self.callbacks.get('validate')(newattrname, attrval): + if not self.callbacks.get('validateifaceattr')(newattrname, attrval): self._parse_error(self._currentfile, lineno, 'iface %s: unsupported keyword (%s)' %(ifacename, attrname)) @@ -239,7 +243,7 @@ class networkInterfaces(): classes.append(class_name) return classes - def process_filedata(self, filedata): + def process_interfaces(self, filedata): line_idx = 0 lines_consumed = 0 raw_config = filedata.split('\n') @@ -260,15 +264,7 @@ class networkInterfaces(): line_idx += 1 return 0 - def read_file(self, filename=None): - interfacesfile = filename - if not interfacesfile: - interfacesfile=self.interfacesfile - self._filestack.append(interfacesfile) - self.logger.info('reading interfaces file %s' %interfacesfile) - f = open(interfacesfile) - filedata = f.read() - f.close() + def read_filedata(self, filedata): self._currentfile_has_template = False # process line continuations filedata = ' '.join(d.strip() for d in filedata.split('\\')) @@ -285,12 +281,42 @@ class networkInterfaces(): 'Continue without template rendering ...') rendered_filedata = None pass - self.logger.info('parsing interfaces file %s ...' %interfacesfile) if rendered_filedata: - self.process_filedata(rendered_filedata) + self.process_interfaces(rendered_filedata) else: - self.process_filedata(filedata) + self.process_interfaces(filedata) + + def read_file(self, filename, fileiobuf=None): + if fileiobuf: + self.read_filedata(fileiobuf) + return + self._filestack.append(filename) + self.logger.info('processing interfaces file %s' %filename) + f = open(filename) + filedata = f.read() + f.close() + self.read_filedata(filedata) self._filestack.pop() - def load(self, filename=None): - return self.read_file(filename) + def read_file_json(self, filename, fileiobuf=None): + if fileiobuf: + ifacedicts = json.loads(fileiobuf, encoding="utf-8") + #object_hook=ifaceJsonDecoder.json_object_hook) + elif filename: + self.logger.info('processing interfaces file %s' %filename) + fp = open(filename) + ifacedicts = json.load(fp) + #object_hook=ifaceJsonDecoder.json_object_hook) + for ifacedict in ifacedicts: + ifaceobj = ifaceJsonDecoder.json_to_ifaceobj(ifacedict) + if ifaceobj: + self._validate_addr_family(ifaceobj) + self.callbacks.get('validateifaceobj')(ifaceobj) + self.callbacks.get('iface_found')(ifaceobj) + + def load(self): + if self.interfacesfileformat == 'json': + return self.read_file_json(self.interfacesfile, + self.interfacesfileiobuf) + return self.read_file(self.interfacesfile, + self.interfacesfileiobuf) diff --git a/sbin/ifupdown b/sbin/ifupdown index b7c33e3..fa10db1 100755 --- a/sbin/ifupdown +++ b/sbin/ifupdown @@ -15,6 +15,7 @@ lockfile="/run/network/.lock" configfile="/etc/network/ifupdown2/ifupdown2.conf" configmap_g=None logger = None +interfacesfileiobuf=None ENVPATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" def run_up(args): @@ -36,7 +37,9 @@ def run_up(args): cache=cachearg, addons_enable=not args.noaddons, statemanager_enable=not args.noaddons, - interfacesfile=args.interfacesfile) + interfacesfile=args.interfacesfile, + interfacesfileiobuf=interfacesfileiobuf, + interfacesfileformat=args.interfacesfileformat) if args.noaddons: ifupdown_handle.up(['up'], args.all, args.CLASS, iflist, excludepats=args.excludepats, @@ -63,7 +66,9 @@ def run_down(args): dryrun=args.noact, addons_enable=not args.noaddons, statemanager_enable=not args.noaddons, - interfacesfile=args.interfacesfile) + interfacesfile=args.interfacesfile, + interfacesfileiobuf=interfacesfileiobuf, + interfacesfileformat=args.interfacesfileformat) ifupdown_handle.down(['pre-down', 'down', 'post-down'], args.all, args.CLASS, iflist, @@ -104,7 +109,9 @@ def run_query(args): withdepends=args.withdepends, perfmode=args.perfmode, cache=cachearg, - interfacesfile=args.interfacesfile) + interfacesfile=args.interfacesfile, + interfacesfileiobuf=interfacesfileiobuf, + interfacesfileformat=args.interfacesfileformat) ifupdown_handle.query([qop], args.all, args.CLASS, iflist, excludepats=args.excludepats, @@ -131,6 +138,7 @@ def run_reload(args): def init(args): global logger + global interfacesfileiobuf log_level = logging.WARNING if args.verbose: @@ -149,6 +157,9 @@ def init(args): except: raise + # If interfaces file is stdin, read + if hasattr(args, 'interfacesfile') and args.interfacesfile == '-': + interfacesfileiobuf = sys.stdin.read() def deinit(): {} @@ -189,6 +200,11 @@ def update_argparser(argparser): default='/etc/network/interfaces', help='use interfaces file instead of default ' + '/etc/network/interfaces') + argparser.add_argument('-t', '--interfaces-format', + dest='interfacesfileformat', + default='native', + choices=['native', 'json'], + help='interfaces file format') def update_ifupdown_argparser(argparser): """ common arg parser for ifup and ifdown """ @@ -276,10 +292,10 @@ def update_ifreload_argparser(argparser): help=argparse.SUPPRESS) #argparser.add_argument('-j', '--jobs', dest='jobs', type=int, # default=-1, choices=range(1,12), help=argparse.SUPPRESS) - argparser.add_argument('-i', '--interfaces', dest='interfacesfile', - default='/etc/network/interfaces', - help='use interfaces file instead of default ' + - '/etc/network/interfaces') + #argparser.add_argument('-i', '--interfaces', dest='interfacesfile', + # default='/etc/network/interfaces', + # help='use interfaces file instead of default ' + + # '/etc/network/interfaces') argparser.add_argument('--use-current-config', dest='usecurrentconfig', action='store_true', help='By default ifreload looks at saved state for ' + @@ -317,10 +333,10 @@ handlers = {'up' : run_up, 'reload' : run_reload } def validate_args(op, args): - if op == 'up' and args.syntaxcheck: - if args.iflist or args.all: - print 'ignoring interface options ..' - return True + #if op == 'up' and args.syntaxcheck: + # if args.iflist or args.all: + # print 'ignoring interface options ..' + # return True if op == 'query' and args.syntaxhelp: return True if not args.iflist and not args.all: @@ -387,12 +403,9 @@ if __name__ == "__main__": print 'error: must be root to run this command' exit(1) - """ - #XXX: Cannot use this. A spawned dhclient process can hold the lock if not utils.lockFile(lockfile): print 'Another instance of this program is already running.' exit(0) - """ # required during boot os.putenv('PATH', ENVPATH)