]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - pkg/ifupdown_main.py
5 from statemanager
import *
6 from networkinterfaces
import *
8 from scheduler
import *
9 from collections
import deque
10 from collections
import OrderedDict
17 class ifupdown_main():
27 modules_dir
='/etc/network'
28 builtin_modules_dir
='/usr/share/ifupdownaddons'
30 # iface dictionary in the below format:
31 # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
33 # { 'swp1' : [<ifaceobject1>, <ifaceobject2> ..] }
35 # Each ifaceobject corresponds to a configuration block for
37 ifaceobjdict
= OrderedDict()
40 # iface dictionary representing the curr running state of an iface
41 # in the below format:
42 # {'<ifacename>' : <ifaceobject>}
43 ifaceobjcurrdict
= OrderedDict()
45 # Dictionary representing operation, sub operation and modules
46 # for every sub operation
48 OrderedDict([('pre-up', OrderedDict({})),
49 ('up' , OrderedDict({})),
50 ('post-up' , OrderedDict({}))]),
52 OrderedDict([('pre-down', OrderedDict({})),
53 ('down' , OrderedDict({})),
54 ('post-down' , OrderedDict({}))])}
58 self
.logger
= logging
.getLogger('ifupdown')
60 self
.ifaces
= OrderedDict()
62 self
.pp
= pprint
.PrettyPrinter(indent
=4)
63 self
.load_modules_builtin(self
.builtin_modules_dir
)
64 self
.load_modules(self
.modules_dir
)
67 self
.statemanager
= stateManager()
68 self
.statemanager
.read_saved_state()
70 # XXX Maybe we should continue by ignoring old state
71 self
.logger
.warning('error reading state (%s)' %str
(e
))
74 def get_subops(self
, op
):
75 """ Returns sub-operation list """
76 return self
.operations
.get(op
).keys()
78 def compat_conv_op_to_mode(self
, op
):
79 """ Returns old op name to work with existing scripts """
87 def set_force(self
, force
):
88 """ Set force flag. """
90 self
.logger
.debug('setting force to true')
94 """ return force flag. """
97 def set_dryrun(self
, dryrun
):
99 self
.logger
.debug('setting dryrun to true')
102 def get_dryrun(self
):
105 def set_nowait(self
, nowait
):
107 self
.logger
.debug('setting dryrun to true')
110 def get_nowait(self
):
113 def set_njobs(self
, njobs
):
114 self
.logger
.debug('setting njobs to %d' %njobs
)
120 def ignore_error(self
, errmsg
):
121 if (self
.FORCE
== True or re
.search(r
'exists', errmsg
,
122 re
.IGNORECASE | re
.MULTILINE
) is not None):
126 def get_nodepends(self
):
127 return self
.NODEPENDS
129 def set_nodepends(self
, nodepends
):
130 self
.logger
.debug('setting nodepends to true')
131 self
.NODEPENDS
= nodepends
133 def set_iface_state(self
, ifaceobj
, state
, status
):
134 ifaceobj
.set_state(state
)
135 ifaceobj
.set_status(status
)
136 self
.statemanager
.update_iface_state(ifaceobj
)
138 def get_iface_objs(self
, ifacename
):
139 return self
.ifaceobjdict
.get(ifacename
)
141 def get_iface_obj_first(self
, ifacename
):
142 ifaceobjs
= self
.get_iface_objs(ifacename
)
143 if ifaceobjs
is not None:
147 def get_iface_obj_last(self
, ifacename
):
148 return self
.ifaceobjdict
.get(ifacename
)[-1]
150 def create_ifaceobjcurr(self
, ifacename
):
151 ifaceobj
= self
.get_ifaceobjcurr(ifacename
)
152 if ifaceobj
is not None:
156 ifaceobj
.set_name(ifacename
)
157 self
.ifaceobjcurrdict
[ifacename
] = ifaceobj
161 def get_ifaceobjcurr(self
, ifacename
):
162 return self
.ifaceobjcurrdict
.get(ifacename
)
164 def get_iface_status(self
, ifacename
):
165 ifaceobjs
= self
.get_iface_objs(ifacename
)
167 if i
.get_status() != ifaceStatus
.SUCCESS
:
168 return i
.get_status()
170 return ifaceStatus
.SUCCESS
172 def get_iface_refcnt(self
, ifacename
):
174 ifaceobjs
= self
.get_iface_objs(ifacename
)
176 if i
.get_refcnt() > max:
180 def create_fake_vlan_iface(self
, vlan_ifname
, op
):
181 """ creates and returns a fake vlan iface object.
183 This was added to support creation of simple vlan devices without any
184 user specified configuration.
188 # XXX: Ideally this should be a call-back into the vlan module.
189 vlan_iface_obj
= iface()
190 vlan_iface_obj
.set_name(vlan_ifname
)
191 vlan_iface_obj
.set_dependents(self
.get_dependents(vlan_iface_obj
, op
))
193 return vlan_iface_obj
195 def is_vlan_device(self
, ifacename
):
196 """ Returns true if iface name is a vlan interface.
198 only supports vlan interfaces of the format <ifacename>.<vlanid>
201 if (re
.search(r
'\.', ifacename
, 0) is not None):
205 def preprocess_dependency_list(self
, dlist
, op
):
208 self
.logger
.debug('pre-processing dependency list: %s' %list(dlist
))
210 dilist
= self
.get_iface_objs(d
)
212 if self
.is_vlan_device(d
) == True:
213 vlan_iface_obj
= self
.create_fake_vlan_iface(d
, op
)
215 # Add face vlan device to ifaceobjdict dict
216 vlan_iface_obj
.inc_refcnt()
217 self
.save_iface(vlan_iface_obj
)
219 # Remove the device from the list
228 self
.logger
.debug('After Processing dependency list: %s'
232 def get_dependents(self
, ifaceobj
, op
):
233 """ Gets iface dependents by calling into respective modules """
236 self
.logger
.debug('%s: ' %ifaceobj
.get_name() + 'getting dependency')
238 # Get dependents for interface by querying respective modules
239 subopdict
= self
.operations
.get(op
)
240 for subop
, mdict
in subopdict
.items():
241 for mname
, mdata
in mdict
.items():
242 if mdata
.get('ftype') == 'pmodule':
243 module
= mdata
.get('module')
245 'get_dependent_ifacenames') == False):
247 dlist
= module
.get_dependent_ifacenames(ifaceobj
,
248 self
.ifaceobjdict
.keys())
250 self
.logger
.debug('%s: ' %ifaceobj
.get_name() +
251 'got dependency list: %s' %str
(dlist
))
257 def generate_dependency_info(self
, ifacenames
, dependency_graph
, op
):
258 """ recursive function to generate iface dependency info """
260 self
.logger
.debug('generating dependency info for %s' %str
(ifacenames
))
263 # Go through all modules and find dependent ifaces
265 ifaceobj
= self
.get_iface_obj_first(i
)
269 dlist
= ifaceobj
.get_dependents()
271 dlist
= self
.get_dependents(ifaceobj
, op
)
273 if dlist
is not None:
274 self
.preprocess_dependency_list(dlist
, op
)
275 ifaceobj
.set_dependents(dlist
)
277 if dependency_graph
.get(i
) is None:
278 dependency_graph
[i
] = dlist
280 if dlist
is not None:
281 self
.generate_dependency_info(dlist
,
282 dependency_graph
, op
)
285 def is_valid_state_transition(self
, ifname
, to_be_state
):
286 return self
.statemanager
.is_valid_state_transition(ifname
,
289 def save_iface(self
, ifaceobj
):
290 if self
.ifaceobjdict
.get(ifaceobj
.get_name()) is None:
291 self
.ifaceobjdict
[ifaceobj
.get_name()] = [ifaceobj
]
293 self
.ifaceobjdict
[ifaceobj
.get_name()].append(ifaceobj
)
295 def read_default_iface_config(self
):
296 """ Reads default network interface config /etc/network/interfaces. """
297 nifaces
= networkInterfaces()
298 nifaces
.subscribe('iface_found', self
.save_iface
)
301 def read_iface_config(self
):
302 return self
.read_default_iface_config()
304 def read_old_iface_config(self
):
305 """ Reads the saved iface config instead of default iface config. """
307 # Read it from the statemanager
308 self
.ifaceobjdict
= self
.statemanager
.get_ifaceobjdict()
311 def save_module(self
, mkind
, msubkind
, mname
, mftype
, module
):
312 """ saves a module into internal module dict for later use.
314 mtype - pre-up.d, post-up.d and so on
315 mftype - pmodule (python module), bashscript (bash script)
319 mmetadata
= self
.operations
[mkind
][msubkind
].get(mname
)
320 if mmetadata
is None or mmetadata
.get('ftype') != 'pmodule':
322 mmetadata
['ftype'] = mftype
323 mmetadata
['module'] = module
324 self
.operations
[mkind
][msubkind
][mname
] = mmetadata
326 self
.logger
.debug('saved module %s' %mkind
+
327 ' %s' %mname
+ ' %s' %mftype
)
329 self
.logger
.warn('ignoring module %s' %mkind
+ ' %s' %msubkind
+
330 ' %s' %mname
+ ' of type %s' %mftype
)
333 def load_modules_builtin(self
, modules_dir
):
334 """ load python modules from modules_dir
336 Default modules_dir is /usr/share/ifupdownmodules
340 self
.logger
.info('loading builtin modules from %s' %modules_dir
)
342 if not modules_dir
in sys
.path
:
343 sys
.path
.append(modules_dir
)
345 module_list
= os
.listdir(modules_dir
)
346 for module
in module_list
:
347 if re
.search('.*\.pyc', module
, 0) != None:
350 mname
, mext
= os
.path
.splitext(module
)
351 if mext
is not None and mext
== '.py':
352 self
.logger
.info('loading ' + modules_dir
+ '/' + module
)
354 m
= __import__(mname
)
355 mclass
= getattr(m
, mname
)
359 minstance
= mclass(force
=self
.get_force(),
360 dryrun
=self
.get_dryrun(),
361 nowait
=self
.get_nowait())
362 ops
= minstance
.get_ops()
364 if re
.search('up', op
) is not None:
365 self
.save_module('up', op
, mname
, 'pmodule',
368 self
.save_module('down', op
, mname
, 'pmodule',
374 def load_modules(self
, modules_dir
):
375 """ loading user modules from /etc/network/.
377 Note that previously loaded python modules override modules found
378 under /etc/network if any
382 self
.logger
.info('loading user modules from %s' %modules_dir
)
383 for op
, subops
in self
.operations
.items():
384 for subop
in subops
.keys():
385 msubdir
= modules_dir
+ '/if-%s.d' %subop
386 self
.logger
.info('loading modules under %s ...' %msubdir
)
388 module_list
= os
.listdir(msubdir
)
389 for module
in module_list
:
390 if re
.search('.*\.pyc', module
, 0) != None:
393 mname
, mext
= os
.path
.splitext(module
)
394 if mext
is not None and mext
== '.py':
395 self
.logger
.debug('loading ' + msubdir
+ '/' + module
)
397 m
= imp
.load_source(module
,
398 msubdir
+ '/' + module
)
399 mclass
= getattr(m
, mname
)
403 self
.save_module(op
, subop
, mname
, 'pmodule',
406 self
.save_module(op
, subop
, mname
, 'script',
407 msubdir
+ '/' + module
)
411 #self.logger.debug('modules ...')
412 #self.pp.pprint(self.operations)
414 # For query, we add a special entry, basically use all 'up' modules
415 self
.operations
['query'] = self
.operations
.get('up')
418 def conv_iface_namelist_to_objlist(self
, intf_list
):
419 for intf
in intf_list
:
420 iface_obj
= self
.get_iface(intf
)
421 if iface_obj
== None:
422 raise ifupdownInvalidValue('no iface %s', intf
)
424 iface_objs
.append(iface_obj
)
429 def run_without_dependents(self
, op
, ifacenames
):
430 ifaceSched
= ifaceScheduler()
432 self
.logger
.debug('run_without_dependents for op %s' %op
+
433 ' for %s' %str
(ifacenames
))
435 if ifacenames
== None:
436 raise ifupdownInvalidValue('no interfaces found')
438 return ifaceSched
.run_iface_list(self
, ifacenames
, op
)
441 def run_with_dependents(self
, op
, ifacenames
):
442 dependency_graph
= {}
444 self
.logger
.debug('run_with_dependents for op %s'
445 %op
+ ' for %s' %str
(ifacenames
))
447 ifaceSched
= ifaceScheduler()
449 if ifacenames
is None:
450 ifacenames
= self
.ifaceobjdict
.keys()
452 # generate dependency graph of interfaces
453 self
.generate_dependency_info(ifacenames
, dependency_graph
, op
)
455 if self
.logger
.isEnabledFor(logging
.DEBUG
) == True:
456 self
.logger
.debug('dependency graph:')
457 self
.pp
.pprint(dependency_graph
)
460 ret
= ifaceSched
.run_iface_dependency_graph_parallel(self
,
461 dependency_graph
, op
)
463 ret
= ifaceSched
.run_iface_dependency_graph(self
, dependency_graph
,
469 def validate_ifaces(self
, ifacenames
):
470 """ validates interface list for config existance.
472 returns -1 if one or more interface not found. else, returns 0
478 ifaceobjs
= self
.get_iface_objs(i
)
479 if ifaceobjs
is None:
482 if len(err_iface
) != 0:
483 self
.logger
.error('cound not find ifaces: %s' %err_iface
)
489 def iface_whitelisted(self
, auto
, allow_classes
, ifacename
):
490 """ Checks if interface is whitelisted depending on set of parameters.
493 interfaces are checked against the allow_classes and auto lists.
497 self
.logger
.debug('checking if iface %s' %ifacename
+
500 ifaceobjs
= self
.get_iface_objs(ifacename
)
501 if ifaceobjs
is None:
502 self
.logger
.debug('iface %s' %ifacename
+ ' not found')
505 # We check classes first
506 if allow_classes
is not None and len(allow_classes
) > 0:
508 if (len(i
.get_classes()) > 0):
509 common
= Set(allow_classes
).intersection(
510 Set(i
.get_classes()))
517 if i
.get_auto() == True:
523 def generate_running_env(self
, ifaceobj
, op
):
524 """ Generates a dictionary with env variables required for an interface.
526 Used to support script execution for interfaces.
531 iface_env
= ifaceobj
.get_env()
532 if iface_env
is not None:
535 cenv
.update(iface_env
)
539 cenv
['MODE'] = self
.compat_conv_op_to_mode(op
)
544 def run(self
, op
, auto
=False, allow_classes
=None,
545 ifacenames
=None, query_state
=None):
546 """ main ifupdown run method """
549 self
.logger
.debug('setting flag ALL')
552 # Only read new iface config for 'up'
553 # operations. For 'downs' we only rely on
555 if op
== 'up' or op
== 'query':
557 self
.read_iface_config()
561 # for down we need to look at old state
562 self
.logger
.debug('down op, looking at old state ..')
564 if len(self
.statemanager
.get_ifaceobjdict()) > 0:
565 self
.read_old_iface_config()
566 elif self
.FORCE
== True:
567 # If no old state available
568 self
.logger
.info('old state not available. Force option ' +
569 'set. Loading new iface config file')
571 self
.read_iface_config()
573 raise Exception('error reading iface config (%s)' %str
(e
))
575 raise Exception('old state not available...aborting.' +
576 ' try running with --force option')
579 if ifacenames
is not None:
580 # If iface list is given, always check if iface is present
581 if self
.validate_ifaces(ifacenames
) != 0:
582 raise Exception('all or some ifaces not found')
584 # if iface list not given by user, assume all from config file
585 if ifacenames
is None: ifacenames
= self
.ifaceobjdict
.keys()
587 # filter interfaces based on auto and allow classes
588 filtered_ifacenames
= [i
for i
in ifacenames
589 if self
.iface_whitelisted(auto
, allow_classes
, i
) == True]
591 if len(filtered_ifacenames
) == 0:
592 raise Exception('no ifaces found matching ' +
596 if query_state
== None:
597 return self
.print_ifaceobjs_pretty(filtered_ifacenames
)
598 elif query_state
== 'presumed':
599 return self
.print_ifaceobjs_saved_state_pretty(
601 elif query_state
== 'presumeddetailed':
602 return self
.print_ifaceobjs_saved_state_detailed_pretty(
605 if op
== 'query' or self
.NODEPENDS
== True:
606 self
.run_without_dependents(op
, filtered_ifacenames
)
608 self
.run_with_dependents(op
, filtered_ifacenames
)
611 if query_state
== 'curr':
612 return self
.print_ifaceobjscurr_pretty(filtered_ifacenames
)
615 # Update persistant iface states
618 self
.statemanager
.flush_state(self
.ifaceobjdict
)
620 self
.statemanager
.flush_state()
622 if self
.logger
.isEnabledFor(logging
.DEBUG
):
623 t
= sys
.exc_info()[2]
624 traceback
.print_tb(t
)
625 self
.logger
.warning('error saving state (%s)' %str
(e
))
628 def up(self
, auto
=False, allow
=None, ifacenames
=None):
629 return self
.run('up', auto
, allow
, ifacenames
)
631 def down(self
, auto
=False, allow
=None, ifacenames
=None):
632 return self
.run('down', auto
, allow
, ifacenames
);
634 def query(self
, auto
=False, allow
=None, ifacenames
=None,
636 return self
.run('query', auto
, allow
, ifacenames
,
637 query_state
=query_state
);
640 """ all state dump """
642 print 'ifupdown object dump'
643 print self
.pp
.pprint(self
.modules
)
644 print self
.pp
.pprint(self
.ifaces
)
645 self
.state_manager
.dump()
647 def print_state(self
, ifacenames
=None):
648 self
.statemanager
.dump(ifacenames
)
650 def print_ifaceobjs_pretty(self
, ifacenames
):
652 ifaceobjs
= self
.get_iface_objs(i
)
654 io
.dump_raw(self
.logger
)
656 def print_ifaceobjscurr_pretty(self
, ifacenames
):
658 ifaceobj
= self
.get_ifaceobjcurr(i
)
659 ifaceobj
.dump_pretty(self
.logger
)
661 def print_ifaceobjs_saved_state_pretty(self
, ifacenames
):
662 self
.statemanager
.print_state_pretty(ifacenames
, self
.logger
)
664 def print_ifaceobjs_saved_state_detailed_pretty(self
, ifacenames
):
665 self
.statemanager
.print_state_detailed_pretty(ifacenames
, self
.logger
)