]> git.proxmox.com Git - mirror_ifupdown2.git/blob - pkg/ifupdown_main.py
python-ifupdown initial checkin
[mirror_ifupdown2.git] / pkg / ifupdown_main.py
1 #!/usr/bin/python
2
3 import os
4 import re
5 from statemanager import *
6 from networkinterfaces import *
7 from iface import *
8 from scheduler import *
9 from collections import deque
10 from collections import OrderedDict
11 import imp
12 import pprint
13 import logging
14 from graph import *
15 import sys, traceback
16
17 class ifupdown_main():
18
19 # Flags
20 FORCE = False
21 NOWAIT = False
22 DRYRUN = False
23 NODEPENDS = False
24 ALL = False
25 STATE_CHECK = True
26
27 modules_dir='/etc/network'
28 builtin_modules_dir='/usr/share/ifupdownaddons'
29
30 # iface dictionary in the below format:
31 # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
32 # eg:
33 # { 'swp1' : [<ifaceobject1>, <ifaceobject2> ..] }
34 #
35 # Each ifaceobject corresponds to a configuration block for
36 # that interface
37 ifaceobjdict = OrderedDict()
38
39
40 # iface dictionary representing the curr running state of an iface
41 # in the below format:
42 # {'<ifacename>' : <ifaceobject>}
43 ifaceobjcurrdict = OrderedDict()
44
45 # Dictionary representing operation, sub operation and modules
46 # for every sub operation
47 operations = { 'up' :
48 OrderedDict([('pre-up', OrderedDict({})),
49 ('up' , OrderedDict({})),
50 ('post-up' , OrderedDict({}))]),
51 'down' :
52 OrderedDict([('pre-down', OrderedDict({})),
53 ('down' , OrderedDict({})),
54 ('post-down' , OrderedDict({}))])}
55
56
57 def __init__(self):
58 self.logger = logging.getLogger('ifupdown')
59
60 self.ifaces = OrderedDict()
61 self.njobs = 1
62 self.pp = pprint.PrettyPrinter(indent=4)
63 self.load_modules_builtin(self.builtin_modules_dir)
64 self.load_modules(self.modules_dir)
65
66 try:
67 self.statemanager = stateManager()
68 self.statemanager.read_saved_state()
69 except Exception, e:
70 # XXX Maybe we should continue by ignoring old state
71 self.logger.warning('error reading state (%s)' %str(e))
72 raise
73
74 def get_subops(self, op):
75 """ Returns sub-operation list """
76 return self.operations.get(op).keys()
77
78 def compat_conv_op_to_mode(self, op):
79 """ Returns old op name to work with existing scripts """
80 if op == 'up':
81 return 'start'
82 elif op == 'down':
83 return 'stop'
84 else:
85 return op
86
87 def set_force(self, force):
88 """ Set force flag. """
89 if force == True:
90 self.logger.debug('setting force to true')
91 self.FORCE = force
92
93 def get_force(self):
94 """ return force flag. """
95 return self.FORCE
96
97 def set_dryrun(self, dryrun):
98 if dryrun == True:
99 self.logger.debug('setting dryrun to true')
100 self.DRYRUN = dryrun
101
102 def get_dryrun(self):
103 return self.DRYRUN
104
105 def set_nowait(self, nowait):
106 if nowait == True:
107 self.logger.debug('setting dryrun to true')
108 self.NOWAIT = nowait
109
110 def get_nowait(self):
111 return self.NOWAIT
112
113 def set_njobs(self, njobs):
114 self.logger.debug('setting njobs to %d' %njobs)
115 self.njobs = njobs
116
117 def get_njobs(self):
118 return self.njobs
119
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):
123 return True
124 return False
125
126 def get_nodepends(self):
127 return self.NODEPENDS
128
129 def set_nodepends(self, nodepends):
130 self.logger.debug('setting nodepends to true')
131 self.NODEPENDS = nodepends
132
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)
137
138 def get_iface_objs(self, ifacename):
139 return self.ifaceobjdict.get(ifacename)
140
141 def get_iface_obj_first(self, ifacename):
142 ifaceobjs = self.get_iface_objs(ifacename)
143 if ifaceobjs is not None:
144 return ifaceobjs[0]
145 return None
146
147 def get_iface_obj_last(self, ifacename):
148 return self.ifaceobjdict.get(ifacename)[-1]
149
150 def create_ifaceobjcurr(self, ifacename):
151 ifaceobj = self.get_ifaceobjcurr(ifacename)
152 if ifaceobj is not None:
153 return ifaceobj
154
155 ifaceobj = iface()
156 ifaceobj.set_name(ifacename)
157 self.ifaceobjcurrdict[ifacename] = ifaceobj
158
159 return ifaceobj
160
161 def get_ifaceobjcurr(self, ifacename):
162 return self.ifaceobjcurrdict.get(ifacename)
163
164 def get_iface_status(self, ifacename):
165 ifaceobjs = self.get_iface_objs(ifacename)
166 for i in ifaceobjs:
167 if i.get_status() != ifaceStatus.SUCCESS:
168 return i.get_status()
169
170 return ifaceStatus.SUCCESS
171
172 def get_iface_refcnt(self, ifacename):
173 max = 0
174 ifaceobjs = self.get_iface_objs(ifacename)
175 for i in ifaceobjs:
176 if i.get_refcnt() > max:
177 max = i.get_refcnt()
178 return max
179
180 def create_fake_vlan_iface(self, vlan_ifname, op):
181 """ creates and returns a fake vlan iface object.
182
183 This was added to support creation of simple vlan devices without any
184 user specified configuration.
185
186 """
187
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))
192
193 return vlan_iface_obj
194
195 def is_vlan_device(self, ifacename):
196 """ Returns true if iface name is a vlan interface.
197
198 only supports vlan interfaces of the format <ifacename>.<vlanid>
199
200 """
201 if (re.search(r'\.', ifacename, 0) is not None):
202 return True
203 return False
204
205 def preprocess_dependency_list(self, dlist, op):
206 del_list = []
207
208 self.logger.debug('pre-processing dependency list: %s' %list(dlist))
209 for d in dlist:
210 dilist = self.get_iface_objs(d)
211 if dilist == None:
212 if self.is_vlan_device(d) == True:
213 vlan_iface_obj = self.create_fake_vlan_iface(d, op)
214
215 # Add face vlan device to ifaceobjdict dict
216 vlan_iface_obj.inc_refcnt()
217 self.save_iface(vlan_iface_obj)
218 else:
219 # Remove the device from the list
220 del_list.append(d)
221 else:
222 for di in dilist:
223 di.inc_refcnt()
224
225 for d in del_list:
226 dlist.remove(d)
227
228 self.logger.debug('After Processing dependency list: %s'
229 %list(dlist))
230
231
232 def get_dependents(self, ifaceobj, op):
233 """ Gets iface dependents by calling into respective modules """
234 dlist = None
235
236 self.logger.debug('%s: ' %ifaceobj.get_name() + 'getting dependency')
237
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')
244 if (hasattr(module,
245 'get_dependent_ifacenames') == False):
246 continue
247 dlist = module.get_dependent_ifacenames(ifaceobj,
248 self.ifaceobjdict.keys())
249 if dlist:
250 self.logger.debug('%s: ' %ifaceobj.get_name() +
251 'got dependency list: %s' %str(dlist))
252 break
253
254 return dlist
255
256
257 def generate_dependency_info(self, ifacenames, dependency_graph, op):
258 """ recursive function to generate iface dependency info """
259
260 self.logger.debug('generating dependency info for %s' %str(ifacenames))
261
262 for i in ifacenames:
263 # Go through all modules and find dependent ifaces
264 dlist = None
265 ifaceobj = self.get_iface_obj_first(i)
266 if ifaceobj is None:
267 continue
268
269 dlist = ifaceobj.get_dependents()
270 if dlist is None:
271 dlist = self.get_dependents(ifaceobj, op)
272
273 if dlist is not None:
274 self.preprocess_dependency_list(dlist, op)
275 ifaceobj.set_dependents(dlist)
276
277 if dependency_graph.get(i) is None:
278 dependency_graph[i] = dlist
279
280 if dlist is not None:
281 self.generate_dependency_info(dlist,
282 dependency_graph, op)
283
284
285 def is_valid_state_transition(self, ifname, to_be_state):
286 return self.statemanager.is_valid_state_transition(ifname,
287 to_be_state)
288
289 def save_iface(self, ifaceobj):
290 if self.ifaceobjdict.get(ifaceobj.get_name()) is None:
291 self.ifaceobjdict[ifaceobj.get_name()] = [ifaceobj]
292 else:
293 self.ifaceobjdict[ifaceobj.get_name()].append(ifaceobj)
294
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)
299 nifaces.load()
300
301 def read_iface_config(self):
302 return self.read_default_iface_config()
303
304 def read_old_iface_config(self):
305 """ Reads the saved iface config instead of default iface config. """
306
307 # Read it from the statemanager
308 self.ifaceobjdict = self.statemanager.get_ifaceobjdict()
309
310
311 def save_module(self, mkind, msubkind, mname, mftype, module):
312 """ saves a module into internal module dict for later use.
313
314 mtype - pre-up.d, post-up.d and so on
315 mftype - pmodule (python module), bashscript (bash script)
316
317 """
318
319 mmetadata = self.operations[mkind][msubkind].get(mname)
320 if mmetadata is None or mmetadata.get('ftype') != 'pmodule':
321 mmetadata = {}
322 mmetadata['ftype'] = mftype
323 mmetadata['module'] = module
324 self.operations[mkind][msubkind][mname] = mmetadata
325
326 self.logger.debug('saved module %s' %mkind +
327 ' %s' %mname + ' %s' %mftype)
328 else:
329 self.logger.warn('ignoring module %s' %mkind + ' %s' %msubkind +
330 ' %s' %mname + ' of type %s' %mftype)
331
332
333 def load_modules_builtin(self, modules_dir):
334 """ load python modules from modules_dir
335
336 Default modules_dir is /usr/share/ifupdownmodules
337
338 """
339
340 self.logger.info('loading builtin modules from %s' %modules_dir)
341
342 if not modules_dir in sys.path:
343 sys.path.append(modules_dir)
344 try:
345 module_list = os.listdir(modules_dir)
346 for module in module_list:
347 if re.search('.*\.pyc', module, 0) != None:
348 continue
349
350 mname, mext = os.path.splitext(module)
351 if mext is not None and mext == '.py':
352 self.logger.info('loading ' + modules_dir + '/' + module)
353 try:
354 m = __import__(mname)
355 mclass = getattr(m, mname)
356 except:
357 raise
358
359 minstance = mclass(force=self.get_force(),
360 dryrun=self.get_dryrun(),
361 nowait=self.get_nowait())
362 ops = minstance.get_ops()
363 for op in ops:
364 if re.search('up', op) is not None:
365 self.save_module('up', op, mname, 'pmodule',
366 minstance)
367 else:
368 self.save_module('down', op, mname, 'pmodule',
369 minstance)
370 except:
371 raise
372
373
374 def load_modules(self, modules_dir):
375 """ loading user modules from /etc/network/.
376
377 Note that previously loaded python modules override modules found
378 under /etc/network if any
379
380 """
381
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)
387 try:
388 module_list = os.listdir(msubdir)
389 for module in module_list:
390 if re.search('.*\.pyc', module, 0) != None:
391 continue
392
393 mname, mext = os.path.splitext(module)
394 if mext is not None and mext == '.py':
395 self.logger.debug('loading ' + msubdir + '/' + module)
396 try:
397 m = imp.load_source(module,
398 msubdir + '/' + module)
399 mclass = getattr(m, mname)
400 except:
401 raise
402 minstance = mclass()
403 self.save_module(op, subop, mname, 'pmodule',
404 minstance)
405 else:
406 self.save_module(op, subop, mname, 'script',
407 msubdir + '/' + module)
408 except:
409 raise
410
411 #self.logger.debug('modules ...')
412 #self.pp.pprint(self.operations)
413
414 # For query, we add a special entry, basically use all 'up' modules
415 self.operations['query'] = self.operations.get('up')
416
417
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)
423
424 iface_objs.append(iface_obj)
425
426 return iface_objs
427
428
429 def run_without_dependents(self, op, ifacenames):
430 ifaceSched = ifaceScheduler()
431
432 self.logger.debug('run_without_dependents for op %s' %op +
433 ' for %s' %str(ifacenames))
434
435 if ifacenames == None:
436 raise ifupdownInvalidValue('no interfaces found')
437
438 return ifaceSched.run_iface_list(self, ifacenames, op)
439
440
441 def run_with_dependents(self, op, ifacenames):
442 dependency_graph = {}
443 ret = 0
444 self.logger.debug('run_with_dependents for op %s'
445 %op + ' for %s' %str(ifacenames))
446
447 ifaceSched = ifaceScheduler()
448
449 if ifacenames is None:
450 ifacenames = self.ifaceobjdict.keys()
451
452 # generate dependency graph of interfaces
453 self.generate_dependency_info(ifacenames, dependency_graph, op)
454
455 if self.logger.isEnabledFor(logging.DEBUG) == True:
456 self.logger.debug('dependency graph:')
457 self.pp.pprint(dependency_graph)
458
459 if self.njobs > 1:
460 ret = ifaceSched.run_iface_dependency_graph_parallel(self,
461 dependency_graph, op)
462 else:
463 ret = ifaceSched.run_iface_dependency_graph(self, dependency_graph,
464 op)
465
466 return ret
467
468
469 def validate_ifaces(self, ifacenames):
470 """ validates interface list for config existance.
471
472 returns -1 if one or more interface not found. else, returns 0
473
474 """
475
476 err_iface = ''
477 for i in ifacenames:
478 ifaceobjs = self.get_iface_objs(i)
479 if ifaceobjs is None:
480 err_iface += ' ' + i
481
482 if len(err_iface) != 0:
483 self.logger.error('cound not find ifaces: %s' %err_iface)
484 return -1
485
486 return 0
487
488
489 def iface_whitelisted(self, auto, allow_classes, ifacename):
490 """ Checks if interface is whitelisted depending on set of parameters.
491
492
493 interfaces are checked against the allow_classes and auto lists.
494
495 """
496
497 self.logger.debug('checking if iface %s' %ifacename +
498 ' is whitelisted')
499
500 ifaceobjs = self.get_iface_objs(ifacename)
501 if ifaceobjs is None:
502 self.logger.debug('iface %s' %ifacename + ' not found')
503 return False
504
505 # We check classes first
506 if allow_classes is not None and len(allow_classes) > 0:
507 for i in ifaceobjs:
508 if (len(i.get_classes()) > 0):
509 common = Set(allow_classes).intersection(
510 Set(i.get_classes()))
511 if len(common) > 0:
512 return True
513 return False
514
515 if auto == True:
516 for i in ifaceobjs:
517 if i.get_auto() == True:
518 return True
519 return False
520
521 return True
522
523 def generate_running_env(self, ifaceobj, op):
524 """ Generates a dictionary with env variables required for an interface.
525
526 Used to support script execution for interfaces.
527
528 """
529
530 cenv = None
531 iface_env = ifaceobj.get_env()
532 if iface_env is not None:
533 cenv = os.environ
534 if cenv is not None:
535 cenv.update(iface_env)
536 else:
537 cenv = iface_env
538
539 cenv['MODE'] = self.compat_conv_op_to_mode(op)
540
541 return cenv
542
543
544 def run(self, op, auto=False, allow_classes=None,
545 ifacenames=None, query_state=None):
546 """ main ifupdown run method """
547
548 if auto == True:
549 self.logger.debug('setting flag ALL')
550 self.ALL = True
551
552 # Only read new iface config for 'up'
553 # operations. For 'downs' we only rely on
554 # old state
555 if op == 'up' or op == 'query':
556 try:
557 self.read_iface_config()
558 except Exception, e:
559 raise
560 else:
561 # for down we need to look at old state
562 self.logger.debug('down op, looking at old state ..')
563
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')
570 try:
571 self.read_iface_config()
572 except Exception, e:
573 raise Exception('error reading iface config (%s)' %str(e))
574 else:
575 raise Exception('old state not available...aborting.' +
576 ' try running with --force option')
577
578
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')
583
584 # if iface list not given by user, assume all from config file
585 if ifacenames is None: ifacenames = self.ifaceobjdict.keys()
586
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]
590
591 if len(filtered_ifacenames) == 0:
592 raise Exception('no ifaces found matching ' +
593 'given allow lists')
594
595 if op == 'query':
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(
600 filtered_ifacenames)
601 elif query_state == 'presumeddetailed':
602 return self.print_ifaceobjs_saved_state_detailed_pretty(
603 filtered_ifacenames)
604
605 if op == 'query' or self.NODEPENDS == True:
606 self.run_without_dependents(op, filtered_ifacenames)
607 else:
608 self.run_with_dependents(op, filtered_ifacenames)
609
610 if op == 'query':
611 if query_state == 'curr':
612 return self.print_ifaceobjscurr_pretty(filtered_ifacenames)
613 return
614
615 # Update persistant iface states
616 try:
617 if self.ALL == True:
618 self.statemanager.flush_state(self.ifaceobjdict)
619 else:
620 self.statemanager.flush_state()
621 except Exception, e:
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))
626
627
628 def up(self, auto=False, allow=None, ifacenames=None):
629 return self.run('up', auto, allow, ifacenames)
630
631 def down(self, auto=False, allow=None, ifacenames=None):
632 return self.run('down', auto, allow, ifacenames);
633
634 def query(self, auto=False, allow=None, ifacenames=None,
635 query_state=False):
636 return self.run('query', auto, allow, ifacenames,
637 query_state=query_state);
638
639 def dump(self):
640 """ all state dump """
641
642 print 'ifupdown object dump'
643 print self.pp.pprint(self.modules)
644 print self.pp.pprint(self.ifaces)
645 self.state_manager.dump()
646
647 def print_state(self, ifacenames=None):
648 self.statemanager.dump(ifacenames)
649
650 def print_ifaceobjs_pretty(self, ifacenames):
651 for i in ifacenames:
652 ifaceobjs = self.get_iface_objs(i)
653 for io in ifaceobjs:
654 io.dump_raw(self.logger)
655
656 def print_ifaceobjscurr_pretty(self, ifacenames):
657 for i in ifacenames:
658 ifaceobj = self.get_ifaceobjcurr(i)
659 ifaceobj.dump_pretty(self.logger)
660
661 def print_ifaceobjs_saved_state_pretty(self, ifacenames):
662 self.statemanager.print_state_pretty(ifacenames, self.logger)
663
664 def print_ifaceobjs_saved_state_detailed_pretty(self, ifacenames):
665 self.statemanager.print_state_detailed_pretty(ifacenames, self.logger)