]> git.proxmox.com Git - mirror_ifupdown2.git/blame_incremental - ifupdown/ifupdownmain.py
fix kernel lacp_fallback_* sysfs file references to use lacp_bypass_*
[mirror_ifupdown2.git] / ifupdown / ifupdownmain.py
... / ...
CommitLineData
1#!/usr/bin/python
2#
3# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6# ifupdownMain --
7# ifupdown main module
8#
9
10import os
11import re
12import imp
13import pprint
14import logging
15import sys, traceback
16import copy
17import json
18from statemanager import *
19from networkinterfaces import *
20from iface import *
21from scheduler import *
22from collections import deque
23from collections import OrderedDict
24from graph import *
25from sets import Set
26
27"""
28.. module:: ifupdownmain
29:synopsis: main module for ifupdown package
30
31.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
32
33"""
34
35_tickmark = u'\u2713'
36_crossmark = u'\u2717'
37_success_sym = '(%s)' %_tickmark
38_error_sym = '(%s)' %_crossmark
39
40class ifupdownFlags():
41 FORCE = False
42 DRYRUN = False
43 NOWAIT = False
44 PERFMODE = False
45 CACHE = False
46
47 # Flags
48 CACHE_FLAGS = 0x0
49
50class ifupdownMain(ifupdownBase):
51 """ ifupdown2 main class """
52
53 # Flags
54 WITH_DEPENDS = False
55 ALL = False
56 IFACE_CLASS = False
57 COMPAT_EXEC_SCRIPTS = False
58 STATEMANAGER_ENABLE = True
59 STATEMANAGER_UPDATE = True
60 ADDONS_ENABLE = False
61
62 # priv flags to mark iface objects
63 BUILTIN = 0x1
64 NOCONFIG = 0x2
65
66 scripts_dir='/etc/network'
67 addon_modules_dir='/usr/share/ifupdownaddons'
68 addon_modules_configfile='/var/lib/ifupdownaddons/addons.conf'
69
70 # iface dictionary in the below format:
71 # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
72 # eg:
73 # { 'swp1' : [<iface swp1>, <iface swp2> ..] }
74 #
75 # Each ifaceobject corresponds to a configuration block for
76 # that interface
77 # The value in the dictionary is a list because the network
78 # interface configuration file supports more than one iface section
79 # in the interfaces file
80 ifaceobjdict = OrderedDict()
81
82 # iface dictionary representing the curr running state of an iface
83 # in the below format:
84 # {'<ifacename>' : <ifaceobject>}
85 ifaceobjcurrdict = OrderedDict()
86
87 # Dictionary representing operation and modules
88 # for every operation
89 module_ops = OrderedDict([('pre-up', []),
90 ('up' , []),
91 ('post-up' , []),
92 ('query-checkcurr', []),
93 ('query-running', []),
94 ('query-dependency', []),
95 ('query', []),
96 ('query-raw', []),
97 ('pre-down', []),
98 ('down' , []),
99 ('post-down' , [])])
100
101 # For old style /etc/network/ bash scripts
102 script_ops = OrderedDict([('pre-up', []),
103 ('up' , []),
104 ('post-up' , []),
105 ('pre-down', []),
106 ('down' , []),
107 ('post-down' , [])])
108
109 # Handlers for ops that ifupdown2 owns
110 def run_up(self, ifaceobj):
111 # If this object is a link slave, ie its link is controlled
112 # by its link master interface, then dont set the link state.
113 # But do allow user to change state of the link if the interface
114 # is already with its link master (hence the master check).
115 if ((ifaceobj.link_type == ifaceLinkType.LINK_SLAVE) and
116 not os.path.exists('/sys/class/net/%s/master' %ifaceobj.name)):
117 return
118 if self.link_exists(ifaceobj.name):
119 self.link_up(ifaceobj.name)
120
121 def run_down(self, ifaceobj):
122 # If this object is a link slave, ie its link is controlled
123 # by its link master interface, then dont set the link state.
124 # But do allow user to change state of the link if the interface
125 # is already with its link master (hence the master check).
126 if ((ifaceobj.link_type == ifaceLinkType.LINK_SLAVE) and
127 not os.path.exists('/sys/class/net/%s/master' %ifaceobj.name)):
128 return
129 if self.link_exists(ifaceobj.name):
130 self.link_down(ifaceobj.name)
131
132 # ifupdown object interface operation handlers
133 ops_handlers = OrderedDict([('up', run_up),
134 ('down', run_down)])
135
136 def run_sched_ifaceobj_posthook(self, ifaceobj, op):
137 if ((ifaceobj.priv_flags & self.BUILTIN) or
138 (ifaceobj.priv_flags & self.NOCONFIG)):
139 return
140 if self.STATEMANAGER_UPDATE:
141 self.statemanager.ifaceobj_sync(ifaceobj, op)
142
143 # ifupdown object interface scheduler pre and posthooks
144 sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
145
146 def __init__(self, config={},
147 force=False, dryrun=False, nowait=False,
148 perfmode=False, withdepends=False, njobs=1,
149 cache=False, addons_enable=True, statemanager_enable=True,
150 interfacesfile='/etc/network/interfaces',
151 interfacesfileiobuf=None,
152 interfacesfileformat='native'):
153 """This member function initializes the ifupdownmain object.
154
155 Kwargs:
156 config (dict): config dict from /etc/network/ifupdown2/ifupdown2.conf
157 force (bool): force interface configuration
158 dryrun (bool): dryrun interface configuration
159 withdepends (bool): apply interface configuration on all depends
160 interfacesfile (str): interfaces file. default is /etc/network/interfaces
161 interfacesfileformat (str): default is 'native'. Other choices are 'json'
162
163 Raises:
164 AttributeError, KeyError """
165
166 self.logger = logging.getLogger('ifupdown')
167 self.FORCE = force
168 self.DRYRUN = dryrun
169 self.NOWAIT = nowait
170 self.PERFMODE = perfmode
171 self.WITH_DEPENDS = withdepends
172 self.STATEMANAGER_ENABLE = statemanager_enable
173 self.CACHE = cache
174 self.interfacesfile = interfacesfile
175 self.interfacesfileiobuf = interfacesfileiobuf
176 self.interfacesfileformat = interfacesfileformat
177 self.config = config
178 self.logger.debug(self.config)
179
180 self.type = ifaceType.UNKNOWN
181
182 # Can be used to provide hints for caching
183 self.CACHE_FLAGS = 0x0
184 self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
185 self.ADDONS_ENABLE = addons_enable
186
187 # Copy flags into ifupdownFlags
188 # XXX: before we transition fully to ifupdownFlags
189 ifupdownFlags.FORCE = force
190 ifupdownFlags.DRYRUN = dryrun
191 ifupdownFlags.NOWAIT = nowait
192 ifupdownFlags.PERFMODE = perfmode
193 ifupdownFlags.CACHE = cache
194
195 self.ifaces = OrderedDict()
196 self.njobs = njobs
197 self.pp = pprint.PrettyPrinter(indent=4)
198 self.modules = OrderedDict({})
199 self.module_attrs = {}
200
201 self.load_addon_modules(self.addon_modules_dir)
202 if self.COMPAT_EXEC_SCRIPTS:
203 self.load_scripts(self.scripts_dir)
204 self.dependency_graph = OrderedDict({})
205
206 self._cache_no_repeats = {}
207
208 if self.STATEMANAGER_ENABLE:
209 try:
210 self.statemanager = stateManager()
211 self.statemanager.read_saved_state()
212 except Exception, e:
213 # XXX Maybe we should continue by ignoring old state
214 self.logger.warning('error reading state (%s)' %str(e))
215 raise
216 else:
217 self.STATEMANAGER_UPDATE = False
218
219 def get_ifaceobjs(self, ifacename):
220 return self.ifaceobjdict.get(ifacename)
221
222 def get_ifaceobj_first(self, ifacename):
223 ifaceobjs = self.get_ifaceobjs(ifacename)
224 if ifaceobjs:
225 return ifaceobjs[0]
226 return None
227
228 def get_ifacenames(self):
229 return self.ifaceobjdict.keys()
230
231 def get_iface_obj_last(self, ifacename):
232 return self.ifaceobjdict.get(ifacename)[-1]
233
234 def must_follow_upperifaces(self, ifacename):
235 #
236 # XXX: This bleeds the knowledge of iface
237 # types in the infrastructure module.
238 # Cant think of a better fix at the moment.
239 # In future maybe the module can set a flag
240 # to indicate if we should follow upperifaces
241 #
242 ifaceobj = self.get_ifaceobj_first(ifacename)
243 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
244 return False
245 return True
246
247 def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
248 increfcnt=False):
249 """ creates a iface object and adds it to the iface dictionary """
250 ifaceobj = iface()
251 ifaceobj.name = ifacename
252 ifaceobj.priv_flags = priv_flags
253 ifaceobj.auto = True
254 if increfcnt:
255 ifaceobj.inc_refcnt()
256 self.ifaceobjdict[ifacename] = [ifaceobj]
257 return ifaceobj
258
259 def create_n_save_ifaceobjcurr(self, ifaceobj):
260 """ creates a copy of iface object and adds it to the iface
261 dict containing current iface objects
262 """
263 ifaceobjcurr = iface()
264 ifaceobjcurr.name = ifaceobj.name
265 ifaceobjcurr.type = ifaceobj.type
266 ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
267 ifaceobjcurr.priv_flags = ifaceobj.priv_flags
268 ifaceobjcurr.auto = ifaceobj.auto
269 self.ifaceobjcurrdict.setdefault(ifaceobj.name,
270 []).append(ifaceobjcurr)
271 return ifaceobjcurr
272
273 def get_ifaceobjcurr(self, ifacename, idx=0):
274 ifaceobjlist = self.ifaceobjcurrdict.get(ifacename)
275 if not ifaceobjlist:
276 return None
277 if not idx:
278 return ifaceobjlist
279 else:
280 return ifaceobjlist[idx]
281
282 def get_ifaceobjrunning(self, ifacename):
283 return self.ifaceobjrunningdict.get(ifacename)
284
285 def get_iface_refcnt(self, ifacename):
286 """ Return iface ref count """
287 max = 0
288 ifaceobjs = self.get_ifaceobjs(ifacename)
289 if not ifaceobjs:
290 return 0
291 for i in ifaceobjs:
292 if i.refcnt > max:
293 max = i.refcnt
294 return max
295
296 def is_iface_builtin_byname(self, ifacename):
297 """ Returns true if iface name is a builtin interface.
298
299 A builtin interface is an interface which ifupdown understands.
300 The following are currently considered builtin ifaces:
301 - vlan interfaces in the format <ifacename>.<vlanid>
302 """
303 return '.' in ifacename
304
305 def is_ifaceobj_builtin(self, ifaceobj):
306 """ Returns true if iface name is a builtin interface.
307
308 A builtin interface is an interface which ifupdown understands.
309 The following are currently considered builtin ifaces:
310 - vlan interfaces in the format <ifacename>.<vlanid>
311 """
312 return (ifaceobj.priv_flags & self.BUILTIN)
313
314 def is_ifaceobj_noconfig(self, ifaceobj):
315 """ Returns true if iface object did not have a user defined config.
316
317 These interfaces appear only when they are dependents of interfaces
318 which have user defined config
319 """
320 return (ifaceobj.priv_flags & self.NOCONFIG)
321
322 def is_iface_noconfig(self, ifacename):
323 """ Returns true if iface has no config """
324
325 ifaceobj = self.get_ifaceobj_first(ifacename)
326 if not ifaceobj: return True
327 return self.is_ifaceobj_noconfig(ifaceobj)
328
329 def preprocess_dependency_list(self, upperifaceobj, dlist, ops):
330 """ We go through the dependency list and
331 delete or add interfaces from the interfaces dict by
332 applying the following rules:
333 if flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
334 we only consider devices whose configuration was
335 specified in the network interfaces file. We delete
336 any interface whose config was not specified except
337 for vlan devices. vlan devices get special treatment.
338 Even if they are not present they are created and added
339 to the ifacesdict
340 elif flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
341 we create objects for all dependent devices that are not
342 present in the ifacesdict
343 """
344 del_list = []
345
346 for d in dlist:
347 dilist = self.get_ifaceobjs(d)
348 if not dilist:
349 ni = None
350 if self.is_iface_builtin_byname(d):
351 ni = self.create_n_save_ifaceobj(d, self.BUILTIN | self.NOCONFIG,
352 True)
353 elif not self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
354 ni = self.create_n_save_ifaceobj(d, self.NOCONFIG,
355 True)
356 else:
357 del_list.append(d)
358 if ni:
359 ni.add_to_upperifaces(upperifaceobj.name)
360 if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
361 ni.link_type = ifaceLinkType.LINK_SLAVE
362 else:
363 for di in dilist:
364 di.inc_refcnt()
365 di.add_to_upperifaces(upperifaceobj.name)
366 if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
367 di.link_type = ifaceLinkType.LINK_SLAVE
368
369 for d in del_list:
370 dlist.remove(d)
371
372 def query_dependents(self, ifaceobj, ops, ifacenames, type=None):
373 """ Gets iface dependents by calling into respective modules """
374 ret_dlist = []
375
376 # Get dependents for interface by querying respective modules
377 for module in self.modules.values():
378 try:
379 if ops[0] == 'query-running':
380 if (not hasattr(module,
381 'get_dependent_ifacenames_running')):
382 continue
383 dlist = module.get_dependent_ifacenames_running(ifaceobj)
384 else:
385 if (not hasattr(module, 'get_dependent_ifacenames')):
386 continue
387 dlist = module.get_dependent_ifacenames(ifaceobj,
388 ifacenames)
389 except Exception, e:
390 self.logger.warn('%s: error getting dependent interfaces (%s)'
391 %(ifaceobj.name, str(e)))
392 dlist = None
393 pass
394 if dlist: ret_dlist.extend(dlist)
395 return list(set(ret_dlist))
396
397 def populate_dependency_info(self, ops, ifacenames=None):
398 """ recursive function to generate iface dependency info """
399
400 if not ifacenames:
401 ifacenames = self.ifaceobjdict.keys()
402
403 iqueue = deque(ifacenames)
404 while iqueue:
405 i = iqueue.popleft()
406 # Go through all modules and find dependent ifaces
407 dlist = None
408 ifaceobj = self.get_ifaceobj_first(i)
409 if not ifaceobj:
410 continue
411 dlist = ifaceobj.lowerifaces
412 if not dlist:
413 dlist = self.query_dependents(ifaceobj, ops, ifacenames)
414 else:
415 continue
416 if dlist:
417 self.preprocess_dependency_list(ifaceobj,
418 dlist, ops)
419 ifaceobj.lowerifaces = dlist
420 [iqueue.append(d) for d in dlist]
421 if not self.dependency_graph.get(i):
422 self.dependency_graph[i] = dlist
423
424 def _check_config_no_repeats(self, ifaceobj):
425 """ check if object has an attribute that is
426 restricted to a single object in the system.
427 if yes, warn and return """
428 for k,v in self._cache_no_repeats.items():
429 iv = ifaceobj.config.get(k)
430 if iv and iv[0] == v:
431 self.logger.error('ignoring interface %s. ' %ifaceobj.name +
432 'Only one object with attribute ' +
433 '\'%s %s\' allowed.' %(k, v))
434 return True
435 for k, v in self.config.get('no_repeats', {}).items():
436 iv = ifaceobj.config.get(k)
437 if iv and iv[0] == v:
438 self._cache_no_repeats[k] = v
439 return False
440
441 def _save_iface(self, ifaceobj):
442 if self._check_config_no_repeats(ifaceobj):
443 return
444 currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
445 if not currentifaceobjlist:
446 self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
447 return
448 if ifaceobj.compare(currentifaceobjlist[0]):
449 self.logger.warn('duplicate interface %s found' %ifaceobj.name)
450 return
451 if currentifaceobjlist[0].type == ifaceobj.type:
452 currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS
453 ifaceobj.flags |= iface.HAS_SIBLINGS
454 self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
455
456 def _iface_configattr_syntax_checker(self, attrname, attrval):
457 for m, mdict in self.module_attrs.items():
458 if not mdict:
459 continue
460 attrsdict = mdict.get('attrs')
461 try:
462 if attrsdict.get(attrname):
463 return True
464 except AttributeError:
465 pass
466 return False
467
468 def _ifaceobj_syntax_checker(self, ifaceobj):
469 err = False
470 for attrname, attrvalue in ifaceobj.config.items():
471 found = False
472 for k, v in self.module_attrs.items():
473 if v and v.get('attrs', {}).get(attrname):
474 found = True
475 break
476 if not found:
477 err = True
478 self.logger.warn('%s: unsupported attribute \'%s\'' %attrname)
479 continue
480 return err
481
482 def read_iface_config(self):
483 """ Reads default network interface config /etc/network/interfaces. """
484 nifaces = networkInterfaces(self.interfacesfile,
485 self.interfacesfileiobuf,
486 self.interfacesfileformat,
487 template_engine=self.config.get('template_engine'),
488 template_lookuppath=self.config.get('template_lookuppath'))
489 nifaces.subscribe('iface_found', self._save_iface)
490 nifaces.subscribe('validateifaceattr',
491 self._iface_configattr_syntax_checker)
492 nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker)
493 nifaces.load()
494
495 def read_old_iface_config(self):
496 """ Reads the saved iface config instead of default iface config.
497 And saved iface config is already read by the statemanager """
498 self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
499
500 def _load_addon_modules_config(self):
501 """ Load addon modules config file """
502
503 with open(self.addon_modules_configfile, 'r') as f:
504 lines = f.readlines()
505 for l in lines:
506 litems = l.rstrip(' \n\t\r').split(',')
507 operation = litems[0]
508 mname = litems[1]
509 self.module_ops[operation].append(mname)
510
511 def load_addon_modules(self, modules_dir):
512 """ load python modules from modules_dir
513
514 Default modules_dir is /usr/share/ifupdownmodules
515
516 """
517 self.logger.info('loading builtin modules from %s' %modules_dir)
518 self._load_addon_modules_config()
519 if not modules_dir in sys.path:
520 sys.path.append(modules_dir)
521 try:
522 for op, mlist in self.module_ops.items():
523 for mname in mlist:
524 if self.modules.get(mname):
525 continue
526 mpath = modules_dir + '/' + mname + '.py'
527 if os.path.exists(mpath):
528 try:
529 m = __import__(mname)
530 mclass = getattr(m, mname)
531 except:
532 raise
533 minstance = mclass(force=self.FORCE,
534 dryrun=self.DRYRUN,
535 nowait=self.NOWAIT,
536 perfmode=self.PERFMODE,
537 cache=self.CACHE,
538 cacheflags=self.CACHE_FLAGS)
539 self.modules[mname] = minstance
540 try:
541 self.module_attrs[mname] = minstance.get_modinfo()
542 except:
543 pass
544 except:
545 raise
546
547 # Assign all modules to query operations
548 self.module_ops['query-checkcurr'] = self.modules.keys()
549 self.module_ops['query-running'] = self.modules.keys()
550 self.module_ops['query-dependency'] = self.modules.keys()
551 self.module_ops['query'] = self.modules.keys()
552 self.module_ops['query-raw'] = self.modules.keys()
553
554
555 def _modules_help(self):
556 """ Prints addon modules supported syntax """
557
558 indent = ' '
559 for m, mdict in self.module_attrs.items():
560 if not mdict:
561 continue
562 print('%s: %s' %(m, mdict.get('mhelp')))
563 attrdict = mdict.get('attrs')
564 if not attrdict:
565 continue
566 try:
567 for attrname, attrvaldict in attrdict.items():
568 if attrvaldict.get('compat', False):
569 continue
570 print('%s%s' %(indent, attrname))
571 print('%shelp: %s' %(indent + ' ',
572 attrvaldict.get('help', '')))
573 print ('%srequired: %s' %(indent + ' ',
574 attrvaldict.get('required', False)))
575 default = attrvaldict.get('default')
576 if default:
577 print('%sdefault: %s' %(indent + ' ', default))
578
579 validrange = attrvaldict.get('validrange')
580 if validrange:
581 print('%svalidrange: %s-%s'
582 %(indent + ' ', validrange[0], validrange[1]))
583
584 validvals = attrvaldict.get('validvals')
585 if validvals:
586 print('%svalidvals: %s'
587 %(indent + ' ', ','.join(validvals)))
588
589 examples = attrvaldict.get('example')
590 if not examples:
591 continue
592
593 print '%sexample:' %(indent + ' ')
594 for e in examples:
595 print '%s%s' %(indent + ' ', e)
596 except:
597 pass
598 print ''
599
600 def load_scripts(self, modules_dir):
601 """ loading user modules from /etc/network/.
602
603 Note that previously loaded python modules override modules found
604 under /etc/network if any
605
606 """
607
608 self.logger.info('looking for user scripts under %s' %modules_dir)
609 for op, mlist in self.script_ops.items():
610 msubdir = modules_dir + '/if-%s.d' %op
611 self.logger.info('loading scripts under %s ...' %msubdir)
612 try:
613 module_list = os.listdir(msubdir)
614 for module in module_list:
615 if self.modules.get(module) is not None:
616 continue
617 self.script_ops[op].append(
618 msubdir + '/' + module)
619 except:
620 # continue reading
621 pass
622
623 def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False):
624 self.logger.debug('scheduling \'%s\' for %s'
625 %(str(ops), str(ifacenames)))
626 self._pretty_print_ordered_dict('dependency graph',
627 self.dependency_graph)
628 return ifaceScheduler.sched_ifaces(self, ifacenames, ops,
629 dependency_graph=self.dependency_graph,
630 order=ifaceSchedulerFlags.INORDER
631 if 'down' in ops[0]
632 else ifaceSchedulerFlags.POSTORDER,
633 followdependents=True if self.WITH_DEPENDS else False,
634 skipupperifaces=skipupperifaces)
635
636 def _render_ifacename(self, ifacename):
637 new_ifacenames = []
638 vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
639 if vlan_match:
640 vlan_groups = vlan_match.groups()
641 if vlan_groups[0] and vlan_groups[1]:
642 [new_ifacenames.append('%d' %v)
643 for v in range(int(vlan_groups[0]),
644 int(vlan_groups[1])+1)]
645 return new_ifacenames
646
647 def _preprocess_ifacenames(self, ifacenames):
648 """ validates interface list for config existance.
649
650 returns -1 if one or more interface not found. else, returns 0
651
652 """
653 new_ifacenames = []
654 err_iface = ''
655 for i in ifacenames:
656 ifaceobjs = self.get_ifaceobjs(i)
657 if not ifaceobjs:
658 # if name not available, render interface name and check again
659 rendered_ifacenames = utils.expand_iface_range(i)
660 if rendered_ifacenames:
661 for ri in rendered_ifacenames:
662 ifaceobjs = self.get_ifaceobjs(ri)
663 if not ifaceobjs:
664 err_iface += ' ' + ri
665 else:
666 new_ifacenames.append(ri)
667 else:
668 err_iface += ' ' + i
669 else:
670 new_ifacenames.append(i)
671 if err_iface:
672 raise Exception('cannot find interfaces:%s' %err_iface)
673 return new_ifacenames
674
675 def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
676 """ Checks if interface is whitelisted depending on set of parameters.
677
678 interfaces are checked against the allow_classes and auto lists.
679
680 """
681 if excludepats:
682 for e in excludepats:
683 if re.search(e, ifacename):
684 return False
685 ifaceobjs = self.get_ifaceobjs(ifacename)
686 if not ifaceobjs:
687 self.logger.debug('iface %s' %ifacename + ' not found')
688 return False
689 # We check classes first
690 if allow_classes:
691 for i in ifaceobjs:
692 if i.classes:
693 common = Set([allow_classes]).intersection(
694 Set(i.classes))
695 if common:
696 return True
697 return False
698 if auto:
699 for i in ifaceobjs:
700 if i.auto:
701 return True
702 return False
703 return True
704
705 def _compat_conv_op_to_mode(self, op):
706 """ Returns old op name to work with existing scripts """
707 if op == 'pre-up':
708 return 'start'
709 elif op == 'pre-down':
710 return 'stop'
711 else:
712 return op
713
714 def generate_running_env(self, ifaceobj, op):
715 """ Generates a dictionary with env variables required for
716 an interface. Used to support script execution for interfaces.
717 """
718
719 cenv = None
720 iface_env = ifaceobj.env
721 if iface_env:
722 cenv = os.environ
723 if cenv:
724 cenv.update(iface_env)
725 else:
726 cenv = iface_env
727 cenv['MODE'] = self._compat_conv_op_to_mode(op)
728 return cenv
729
730 def _save_state(self):
731 if not self.STATEMANAGER_ENABLE or not self.STATEMANAGER_UPDATE:
732 return
733 try:
734 # Update persistant iface states
735 self.statemanager.save_state()
736 except Exception, e:
737 if self.logger.isEnabledFor(logging.DEBUG):
738 t = sys.exc_info()[2]
739 traceback.print_tb(t)
740 self.logger.warning('error saving state (%s)' %str(e))
741
742 def set_type(self, type):
743 if type == 'iface':
744 self.type = ifaceType.IFACE
745 elif type == 'vlan':
746 self.type = ifaceType.BRIDGE_VLAN
747 else:
748 self.type = ifaceType.UNKNOWN
749
750 def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
751 excludepats=None, printdependency=None, syntaxcheck=False,
752 type=None, skipupperifaces=False):
753 """This brings the interface(s) up
754
755 Args:
756 ops (list): list of ops to perform on the interface(s).
757 Eg: ['pre-up', 'up', 'post-up'
758
759 Kwargs:
760 auto (bool): act on interfaces marked auto
761 allow_classes (list): act on interfaces belonging to classes in the list
762 ifacenames (list): act on interfaces specified in this list
763 excludepats (list): list of patterns of interfaces to exclude
764 syntaxcheck (bool): only perform syntax check
765 """
766
767 self.set_type(type)
768
769 if allow_classes:
770 self.IFACE_CLASS = True
771 if not self.ADDONS_ENABLE: self.STATEMANAGER_UPDATE = False
772 if auto:
773 self.ALL = True
774 self.WITH_DEPENDS = True
775 try:
776 self.read_iface_config()
777 except Exception:
778 raise
779
780 # If only syntax check was requested, return here
781 if syntaxcheck:
782 return
783
784 if ifacenames:
785 ifacenames = self._preprocess_ifacenames(ifacenames)
786
787 # if iface list not given by user, assume all from config file
788 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
789
790 # filter interfaces based on auto and allow classes
791 filtered_ifacenames = [i for i in ifacenames
792 if self._iface_whitelisted(auto, allow_classes,
793 excludepats, i)]
794 if not filtered_ifacenames:
795 raise Exception('no ifaces found matching given allow lists')
796
797 if printdependency:
798 self.populate_dependency_info(ops, filtered_ifacenames)
799 self.print_dependency(filtered_ifacenames, printdependency)
800 return
801 else:
802 self.populate_dependency_info(ops)
803
804 try:
805 self._sched_ifaces(filtered_ifacenames, ops,
806 skipupperifaces=skipupperifaces)
807 finally:
808 if not self.DRYRUN and self.ADDONS_ENABLE:
809 self._save_state()
810
811 def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
812 excludepats=None, printdependency=None, usecurrentconfig=False,
813 type=None):
814 """ down an interface """
815
816 self.set_type(type)
817
818 if allow_classes:
819 self.IFACE_CLASS = True
820 if not self.ADDONS_ENABLE: self.STATEMANAGER_UPDATE = False
821 if auto:
822 self.ALL = True
823 self.WITH_DEPENDS = True
824 # For down we need to look at old state, unless usecurrentconfig
825 # is set
826 if (not usecurrentconfig and self.STATEMANAGER_ENABLE and
827 self.statemanager.ifaceobjdict):
828 # Since we are using state manager objects,
829 # skip the updating of state manager objects
830 self.logger.debug('Looking at old state ..')
831 self.read_old_iface_config()
832 else:
833 # If no old state available
834 try:
835 self.read_iface_config()
836 except Exception, e:
837 raise Exception('error reading iface config (%s)' %str(e))
838 if ifacenames:
839 # If iface list is given by the caller, always check if iface
840 # is present
841 try:
842 ifacenames = self._preprocess_ifacenames(ifacenames)
843 except Exception, e:
844 raise Exception('%s' %str(e) +
845 ' (interface was probably never up ?)')
846
847 # if iface list not given by user, assume all from config file
848 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
849
850 # filter interfaces based on auto and allow classes
851 filtered_ifacenames = [i for i in ifacenames
852 if self._iface_whitelisted(auto, allow_classes,
853 excludepats, i)]
854 if not filtered_ifacenames:
855 raise Exception('no ifaces found matching given allow lists ' +
856 '(or interfaces were probably never up ?)')
857
858 if printdependency:
859 self.populate_dependency_info(ops, filtered_ifacenames)
860 self.print_dependency(filtered_ifacenames, printdependency)
861 return
862 else:
863 self.populate_dependency_info(ops)
864
865 try:
866 self._sched_ifaces(filtered_ifacenames, ops)
867 finally:
868 if not self.DRYRUN and self.ADDONS_ENABLE:
869 self._save_state()
870
871 def query(self, ops, auto=False, allow_classes=None, ifacenames=None,
872 excludepats=None, printdependency=None,
873 format='native', type=None):
874 """ query an interface """
875
876 self.set_type(type)
877
878 if allow_classes:
879 self.IFACE_CLASS = True
880 if self.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
881 return self.statemanager.dump_pretty(ifacenames)
882 self.STATEMANAGER_UPDATE = False
883 if auto:
884 self.logger.debug('setting flag ALL')
885 self.ALL = True
886 self.WITH_DEPENDS = True
887
888 if ops[0] == 'query-syntax':
889 self._modules_help()
890 return
891 elif ops[0] == 'query-running':
892 # create fake devices to all dependents that dont have config
893 map(lambda i: self.create_n_save_ifaceobj(i, self.NOCONFIG),
894 ifacenames)
895 else:
896 try:
897 self.read_iface_config()
898 except Exception:
899 raise
900
901 if ifacenames and ops[0] != 'query-running':
902 # If iface list is given, always check if iface is present
903 ifacenames = self._preprocess_ifacenames(ifacenames)
904
905 # if iface list not given by user, assume all from config file
906 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
907
908 # filter interfaces based on auto and allow classes
909 if ops[0] == 'query-running':
910 filtered_ifacenames = ifacenames
911 else:
912 filtered_ifacenames = [i for i in ifacenames
913 if self._iface_whitelisted(auto, allow_classes,
914 excludepats, i)]
915 if not filtered_ifacenames:
916 raise Exception('no ifaces found matching ' +
917 'given allow lists')
918
919 self.populate_dependency_info(ops)
920 if ops[0] == 'query-dependency' and printdependency:
921 self.print_dependency(filtered_ifacenames, printdependency)
922 return
923
924 if ops[0] == 'query':
925 return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
926 elif ops[0] == 'query-raw':
927 return self.print_ifaceobjs_raw(filtered_ifacenames)
928
929 self._sched_ifaces(filtered_ifacenames, ops)
930
931 if ops[0] == 'query-checkcurr':
932 ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames, format)
933 if ret != 0:
934 # if any of the object has an error, signal that silently
935 raise Exception('')
936 elif ops[0] == 'query-running':
937 self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
938 return
939
940 def _reload_currentlyup(self, upops, downops, auto=True, allow=None,
941 ifacenames=None, excludepats=None, usecurrentconfig=False,
942 **extra_args):
943 """ reload currently up interfaces """
944 allow_classes = []
945 new_ifaceobjdict = {}
946
947 # Override auto to true
948 auto = True
949 if auto:
950 self.ALL = True
951 self.WITH_DEPENDS = True
952
953 try:
954 self.read_iface_config()
955 except:
956 raise
957 if not self.ifaceobjdict:
958 self.logger.warn("nothing to reload ..exiting.")
959 return
960 already_up_ifacenames = []
961 # generate dependency graph of interfaces
962 self.populate_dependency_info(upops)
963 if (not usecurrentconfig and self.STATEMANAGER_ENABLE
964 and self.statemanager.ifaceobjdict):
965 already_up_ifacenames = self.statemanager.ifaceobjdict.keys()
966
967 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
968 filtered_ifacenames = [i for i in ifacenames
969 if self._iface_whitelisted(auto, allow_classes,
970 excludepats, i)]
971
972 # Get already up interfaces that still exist in the interfaces file
973 already_up_ifacenames_not_present = Set(
974 already_up_ifacenames).difference(ifacenames)
975 already_up_ifacenames_still_present = Set(
976 already_up_ifacenames).difference(
977 already_up_ifacenames_not_present)
978 interfaces_to_up = Set(already_up_ifacenames_still_present).union(
979 filtered_ifacenames)
980
981 if (already_up_ifacenames_not_present and
982 self.config.get('ifreload_currentlyup_down_notpresent') == '1'):
983 self.logger.info('reload: schedule down on interfaces: %s'
984 %str(already_up_ifacenames_not_present))
985
986 # Save a copy of new iface objects and dependency_graph
987 new_ifaceobjdict = dict(self.ifaceobjdict)
988 new_dependency_graph = dict(self.dependency_graph)
989
990 # old interface config is read into self.ifaceobjdict
991 self.read_old_iface_config()
992
993 # reinitialize dependency graph
994 self.dependency_graph = OrderedDict({})
995 self.populate_dependency_info(downops,
996 already_up_ifacenames_not_present)
997 self._sched_ifaces(already_up_ifacenames_not_present, downops)
998 else:
999 self.logger.debug('no interfaces to down ..')
1000
1001 # Now, run 'up' with new config dict
1002 # reset statemanager update flag to default
1003 if new_ifaceobjdict:
1004 self.ifaceobjdict = new_ifaceobjdict
1005 self.dependency_graph = new_dependency_graph
1006
1007 if not self.ifaceobjdict:
1008 return
1009 self.logger.info('reload: scheduling up on interfaces: %s'
1010 %str(interfaces_to_up))
1011 self._sched_ifaces(interfaces_to_up, upops)
1012 if self.DRYRUN:
1013 return
1014 self._save_state()
1015
1016 def _reload_default(self, upops, downops, auto=False, allow=None,
1017 ifacenames=None, excludepats=None, usecurrentconfig=False,
1018 **extra_args):
1019 """ reload interface config """
1020 allow_classes = []
1021 new_ifaceobjdict = {}
1022
1023 if auto:
1024 self.ALL = True
1025 self.WITH_DEPENDS = True
1026 try:
1027 self.read_iface_config()
1028 except:
1029 raise
1030
1031 if not self.ifaceobjdict:
1032 self.logger.warn("nothing to reload ..exiting.")
1033 return
1034 # generate dependency graph of interfaces
1035 self.populate_dependency_info(upops)
1036 if (not usecurrentconfig and self.STATEMANAGER_ENABLE
1037 and self.statemanager.ifaceobjdict):
1038 # Save a copy of new iface objects and dependency_graph
1039 new_ifaceobjdict = dict(self.ifaceobjdict)
1040 new_dependency_graph = dict(self.dependency_graph)
1041
1042 # if old state is present, read old state and mark op for 'down'
1043 # followed by 'up' aka: reload
1044 # old interface config is read into self.ifaceobjdict
1045 self.read_old_iface_config()
1046 op = 'reload'
1047 else:
1048 # oldconfig not available, continue with 'up' with new config
1049 op = 'up'
1050
1051 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
1052 if op == 'reload' and ifacenames:
1053 filtered_ifacenames = [i for i in ifacenames
1054 if self._iface_whitelisted(auto, allow_classes,
1055 excludepats, i)]
1056 # Generate the interface down list
1057 # Interfaces that go into the down list:
1058 # - interfaces that were present in last config and are not
1059 # present in the new config
1060 # - interfaces that were changed between the last and current
1061 # config
1062 #
1063 ifacedownlist = []
1064 for ifname in filtered_ifacenames:
1065 lastifaceobjlist = self.ifaceobjdict.get(ifname)
1066 objidx = 0
1067 # If interface is not present in the new file
1068 # append it to the down list
1069 newifaceobjlist = new_ifaceobjdict.get(ifname)
1070 if not newifaceobjlist:
1071 ifacedownlist.append(ifname)
1072 continue
1073 # If interface has changed between the current file
1074 # and the last installed append it to the down list
1075 if len(newifaceobjlist) != len(lastifaceobjlist):
1076 ifacedownlist.append(ifname)
1077 continue
1078 # compare object list
1079 for objidx in range(0, len(lastifaceobjlist)):
1080 oldobj = lastifaceobjlist[objidx]
1081 newobj = newifaceobjlist[objidx]
1082 if not newobj.compare(oldobj):
1083 ifacedownlist.append(ifname)
1084 continue
1085
1086 if ifacedownlist:
1087 self.logger.info('reload: scheduling down on interfaces: %s'
1088 %str(ifacedownlist))
1089 # reinitialize dependency graph
1090 self.dependency_graph = OrderedDict({})
1091 # Generate dependency info for old config
1092 self.populate_dependency_info(downops, ifacedownlist)
1093 self._sched_ifaces(ifacedownlist, downops)
1094 else:
1095 self.logger.debug('no interfaces to down ..')
1096
1097 # Now, run 'up' with new config dict
1098 # reset statemanager update flag to default
1099 if not new_ifaceobjdict:
1100 return
1101 self.ifaceobjdict = new_ifaceobjdict
1102 self.dependency_graph = new_dependency_graph
1103 ifacenames = self.ifaceobjdict.keys()
1104 filtered_ifacenames = [i for i in ifacenames
1105 if self._iface_whitelisted(auto, allow_classes,
1106 excludepats, i)]
1107
1108 self.logger.info('reload: scheduling up on interfaces: %s'
1109 %str(filtered_ifacenames))
1110 self._sched_ifaces(filtered_ifacenames, upops)
1111 if self.DRYRUN:
1112 return
1113 self._save_state()
1114
1115 def reload(self, *args, **kargs):
1116 """ reload interface config """
1117 self.logger.debug('reloading interface config ..')
1118 if kargs.get('currentlyup', False):
1119 self._reload_currentlyup(*args, **kargs)
1120 else:
1121 self._reload_default(*args, **kargs)
1122
1123 def _pretty_print_ordered_dict(self, prefix, argdict):
1124 outbuf = prefix + ' {\n'
1125 for k, vlist in argdict.items():
1126 outbuf += '\t%s : %s\n' %(k, str(vlist))
1127 self.logger.debug(outbuf + '}')
1128
1129 def print_dependency(self, ifacenames, format):
1130 """ prints iface dependency information """
1131
1132 if not ifacenames:
1133 ifacenames = self.ifaceobjdict.keys()
1134 if format == 'list':
1135 for k,v in self.dependency_graph.items():
1136 print '%s : %s' %(k, str(v))
1137 elif format == 'dot':
1138 indegrees = {}
1139 map(lambda i: indegrees.update({i :
1140 self.get_iface_refcnt(i)}),
1141 self.dependency_graph.keys())
1142 graph.generate_dots(self.dependency_graph, indegrees)
1143
1144 def print_ifaceobjs_raw(self, ifacenames):
1145 """ prints raw lines for ifaces from config file """
1146
1147 for i in ifacenames:
1148 for ifaceobj in self.get_ifaceobjs(i):
1149 if (self.is_ifaceobj_builtin(ifaceobj) or
1150 not ifaceobj.is_config_present()):
1151 continue
1152 ifaceobj.dump_raw(self.logger)
1153 print '\n'
1154 if self.WITH_DEPENDS and not self.ALL:
1155 dlist = ifaceobj.lowerifaces
1156 if not dlist: continue
1157 self.print_ifaceobjs_raw(dlist)
1158
1159 def _get_ifaceobjs_pretty(self, ifacenames, ifaceobjs, running=False):
1160 """ returns iface obj list """
1161
1162 for i in ifacenames:
1163 for ifaceobj in self.get_ifaceobjs(i):
1164 if ((not running and self.is_ifaceobj_noconfig(ifaceobj)) or
1165 (running and not ifaceobj.is_config_present())):
1166 continue
1167 ifaceobjs.append(ifaceobj)
1168 if self.WITH_DEPENDS and not self.ALL:
1169 dlist = ifaceobj.lowerifaces
1170 if not dlist: continue
1171 self._get_ifaceobjs_pretty(dlist, ifaceobjs, running)
1172
1173 def print_ifaceobjs_pretty(self, ifacenames, format='native'):
1174 """ pretty prints iface in format given by keyword arg format """
1175
1176 ifaceobjs = []
1177 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs)
1178 if not ifaceobjs: return
1179 if format == 'json':
1180 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
1181 indent=4, separators=(',', ': '))
1182 else:
1183 expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
1184 for i in ifaceobjs:
1185 if not expand and (i.flags & iface.IFACERANGE_ENTRY):
1186 # print only the first one
1187 if i.flags & iface.IFACERANGE_START:
1188 i.dump_pretty(use_realname=True)
1189 else:
1190 i.dump_pretty()
1191
1192 def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
1193 ret = 0
1194 for i in ifacenames:
1195 ifaceobjscurr = self.get_ifaceobjcurr(i)
1196 if not ifaceobjscurr: continue
1197 for ifaceobj in ifaceobjscurr:
1198 if (ifaceobj.status == ifaceStatus.NOTFOUND or
1199 ifaceobj.status == ifaceStatus.ERROR):
1200 ret = 1
1201 if self.is_ifaceobj_noconfig(ifaceobj):
1202 continue
1203 ifaceobjs.append(ifaceobj)
1204 if self.WITH_DEPENDS and not self.ALL:
1205 dlist = ifaceobj.lowerifaces
1206 if not dlist: continue
1207 dret = self._get_ifaceobjscurr_pretty(dlist, ifaceobjs)
1208 if dret: ret = 1
1209 return ret
1210
1211 def print_ifaceobjscurr_pretty(self, ifacenames, format='native'):
1212 """ pretty prints current running state of interfaces with status.
1213
1214 returns 1 if any of the interface has an error,
1215 else returns 0
1216 """
1217
1218 ifaceobjs = []
1219 ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
1220 if not ifaceobjs: return
1221 if format == 'json':
1222 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
1223 separators=(',', ': '))
1224 else:
1225 map(lambda i: i.dump_pretty(with_status=True,
1226 successstr=self.config.get('ifquery_check_success_str',
1227 _success_sym),
1228 errorstr=self.config.get('ifquery_check_error_str', _error_sym),
1229 unknownstr=self.config.get('ifquery_check_unknown_str', '')),
1230 ifaceobjs)
1231 return ret
1232
1233 def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
1234 """ pretty prints iface running state """
1235
1236 ifaceobjs = []
1237 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs, running=True)
1238 if not ifaceobjs: return
1239 if format == 'json':
1240 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
1241 separators=(',', ': '))
1242 else:
1243 map(lambda i: i.dump_pretty(), ifaceobjs)
1244
1245 def _dump(self):
1246 print 'ifupdown main object dump'
1247 print self.pp.pprint(self.modules)
1248 print self.pp.pprint(self.ifaceobjdict)
1249
1250 def _dump_ifaceobjs(self, ifacenames):
1251 for i in ifacenames:
1252 ifaceobjs = self.get_ifaceobjs(i)
1253 for i in ifaceobjs:
1254 i.dump(self.logger)
1255 print '\n'