]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown/ifupdownmain.py
ifupdownmain: squash multiple iface stanzas for the same interface
[mirror_ifupdown2.git] / ifupdown / ifupdownmain.py
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
10 import os
11 import re
12 import imp
13 import pprint
14 import logging
15 import sys, traceback
16 import copy
17 import json
18 import ifupdown.statemanager as statemanager
19 import ifupdown.ifupdownconfig as ifupdownConfig
20 from networkinterfaces import *
21 from iface import *
22 from scheduler import *
23 from collections import deque
24 from collections import OrderedDict
25 from graph import *
26 from sets import Set
27
28 """
29 .. module:: ifupdownmain
30 :synopsis: main module for ifupdown package
31
32 .. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
33
34 """
35
36 _tickmark = u'\u2713'
37 _crossmark = u'\u2717'
38 _success_sym = '(%s)' %_tickmark
39 _error_sym = '(%s)' %_crossmark
40
41 class ifupdownFlags():
42 FORCE = False
43 DRYRUN = False
44 NOWAIT = False
45 PERFMODE = False
46 CACHE = False
47
48 # Flags
49 CACHE_FLAGS = 0x0
50
51 class ifupdownMainFlags():
52 WITH_DEPENDS = False
53 ALL = False
54 IFACE_CLASS = False
55 COMPAT_EXEC_SCRIPTS = False
56 STATEMANAGER_ENABLE = True
57 STATEMANAGER_UPDATE = True
58 ADDONS_ENABLE = False
59 DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
60 SCHED_SKIP_CHECK_UPPERIFACES = False
61 CHECK_SHARED_DEPENDENTS = True
62
63 class ifacePrivFlags():
64 # priv flags to mark iface objects
65 BUILTIN = False
66 NOCONFIG = False
67
68 def __init__(self, builtin=False, noconfig=False):
69 self.BUILTIN = builtin
70 self.NOCONFIG = noconfig
71
72 class ifupdownMain(ifupdownBase):
73 """ ifupdown2 main class """
74
75 scripts_dir='/etc/network'
76 addon_modules_dir='/usr/share/ifupdown2/addons'
77 addon_modules_configfile='/etc/network/ifupdown2/addons.conf'
78
79 # iface dictionary in the below format:
80 # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
81 # eg:
82 # { 'swp1' : [<iface swp1>, <iface swp2> ..] }
83 #
84 # Each ifaceobject corresponds to a configuration block for
85 # that interface
86 # The value in the dictionary is a list because the network
87 # interface configuration file supports more than one iface section
88 # in the interfaces file
89 ifaceobjdict = OrderedDict()
90
91 # iface dictionary representing the curr running state of an iface
92 # in the below format:
93 # {'<ifacename>' : <ifaceobject>}
94 ifaceobjcurrdict = OrderedDict()
95
96 # Dictionary representing operation and modules
97 # for every operation
98 module_ops = OrderedDict([('pre-up', []),
99 ('up' , []),
100 ('post-up' , []),
101 ('query-checkcurr', []),
102 ('query-running', []),
103 ('query-dependency', []),
104 ('query', []),
105 ('query-raw', []),
106 ('pre-down', []),
107 ('down' , []),
108 ('post-down' , [])])
109
110 # For old style /etc/network/ bash scripts
111 script_ops = OrderedDict([('pre-up', []),
112 ('up' , []),
113 ('post-up' , []),
114 ('pre-down', []),
115 ('down' , []),
116 ('post-down' , [])])
117
118 # Handlers for ops that ifupdown2 owns
119 def run_up(self, ifaceobj):
120 # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs).
121 # there is no real interface behind it
122 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
123 return
124 if (ifaceobj.addr_method and
125 ifaceobj.addr_method == 'manual'):
126 return
127 if self._delay_admin_state:
128 self._delay_admin_state_iface_queue.append(ifaceobj.name)
129 return
130 # If this object is a link slave, ie its link is controlled
131 # by its link master interface, then dont set the link state.
132 # But do allow user to change state of the link if the interface
133 # is already with its link master (hence the master check).
134 if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
135 return
136 if not self.link_exists(ifaceobj.name):
137 return
138 self.link_up(ifaceobj.name)
139
140 def run_down(self, ifaceobj):
141 # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs)
142 # there is no real interface behind it
143 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
144 return
145 if (ifaceobj.addr_method and
146 ifaceobj.addr_method == 'manual'):
147 return
148 if self._delay_admin_state:
149 self._delay_admin_state_iface_queue.append(ifaceobj.name)
150 return
151 # If this object is a link slave, ie its link is controlled
152 # by its link master interface, then dont set the link state.
153 # But do allow user to change state of the link if the interface
154 # is already with its link master (hence the master check).
155 if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
156 return
157 if not self.link_exists(ifaceobj.name):
158 return
159 self.link_down(ifaceobj.name)
160
161 # ifupdown object interface operation handlers
162 ops_handlers = OrderedDict([('up', run_up),
163 ('down', run_down)])
164
165 def run_sched_ifaceobj_posthook(self, ifaceobj, op):
166 if (ifaceobj.priv_flags and (ifaceobj.priv_flags.BUILTIN or
167 ifaceobj.priv_flags.NOCONFIG)):
168 return
169 if self.flags.STATEMANAGER_UPDATE:
170 self.statemanager.ifaceobj_sync(ifaceobj, op)
171
172 # ifupdown object interface scheduler pre and posthooks
173 sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
174
175 def __init__(self, config={},
176 force=False, dryrun=False, nowait=False,
177 perfmode=False, withdepends=False, njobs=1,
178 cache=False, addons_enable=True, statemanager_enable=True,
179 interfacesfile='/etc/network/interfaces',
180 interfacesfileiobuf=None,
181 interfacesfileformat='native'):
182 """This member function initializes the ifupdownmain object.
183
184 Kwargs:
185 config (dict): config dict from /etc/network/ifupdown2/ifupdown2.conf
186 force (bool): force interface configuration
187 dryrun (bool): dryrun interface configuration
188 withdepends (bool): apply interface configuration on all depends
189 interfacesfile (str): interfaces file. default is /etc/network/interfaces
190 interfacesfileformat (str): default is 'native'. Other choices are 'json'
191
192 Raises:
193 AttributeError, KeyError """
194
195 self.logger = logging.getLogger('ifupdown')
196 self.FORCE = force
197 self.DRYRUN = dryrun
198 self.NOWAIT = nowait
199 self.PERFMODE = perfmode
200 self.CACHE = cache
201 # Can be used to provide hints for caching
202 self.CACHE_FLAGS = 0x0
203
204 self.flags = ifupdownMainFlags()
205
206 self.flags.WITH_DEPENDS = withdepends
207 self.flags.STATEMANAGER_ENABLE = statemanager_enable
208 self.interfacesfile = interfacesfile
209 self.interfacesfileiobuf = interfacesfileiobuf
210 self.interfacesfileformat = interfacesfileformat
211 self.config = config
212 self.logger.debug(self.config)
213 self.blacklisted_ifaces_present = False
214
215 self.type = ifaceType.UNKNOWN
216
217 self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
218 self.flags.ADDONS_ENABLE = addons_enable
219
220 # Copy flags into ifupdownFlags
221 # XXX: before we transition fully to ifupdownFlags
222 ifupdownFlags.FORCE = force
223 ifupdownFlags.DRYRUN = dryrun
224 ifupdownFlags.NOWAIT = nowait
225 ifupdownFlags.PERFMODE = perfmode
226 ifupdownFlags.CACHE = cache
227
228 self.ifaces = OrderedDict()
229 self.njobs = njobs
230 self.pp = pprint.PrettyPrinter(indent=4)
231 self.modules = OrderedDict({})
232 self.module_attrs = {}
233
234 self.load_addon_modules(self.addon_modules_dir)
235 if self.flags.COMPAT_EXEC_SCRIPTS:
236 self.load_scripts(self.scripts_dir)
237 self.dependency_graph = OrderedDict({})
238
239 self._cache_no_repeats = {}
240
241 if self.flags.STATEMANAGER_ENABLE:
242 try:
243 self.statemanager = statemanager.statemanager_api
244 self.statemanager.read_saved_state()
245 except Exception, e:
246 # XXX Maybe we should continue by ignoring old state
247 self.logger.warning('error reading state (%s)' %str(e))
248 raise
249 else:
250 self.flags.STATEMANAGER_UPDATE = False
251 self._delay_admin_state = True if self.config.get(
252 'delay_admin_state_change', '0') == '1' else False
253 self._delay_admin_state_iface_queue = []
254 if self._delay_admin_state:
255 self.logger.info('\'delay_admin_state_change\' is set. admin ' +
256 'state changes will be delayed till the end.')
257
258 self._link_master_slave = True if self.config.get(
259 'link_master_slave', '0') == '1' else False
260 if self._link_master_slave:
261 self.logger.info('\'link_master_slave\' is set. slave admin ' +
262 'state changes will be delayed till the ' +
263 'masters admin state change.')
264
265 self._ifaceobj_squash = True if self.config.get(
266 'ifaceobj_squash', '0') == '1' else False
267
268 # initialize global config object with config passed by the user
269 # This makes config available to addon modules
270 ifupdownConfig.config = self.config
271
272 def link_master_slave_ignore_error(self, errorstr):
273 # If link master slave flag is set,
274 # there may be cases where the lowerdev may not be
275 # up resulting in 'Network is down' error
276 # This can happen if the lowerdev is a LINK_SLAVE
277 # of another interface which is not up yet
278 # example of such a case:
279 # bringing up a vlan on a bond interface and the bond
280 # is a LINK_SLAVE of a bridge (in other words the bond is
281 # part of a bridge) which is not up yet
282 if self._link_master_slave:
283 if 'Network is down':
284 return True
285 return False
286
287 def get_ifaceobjs(self, ifacename):
288 return self.ifaceobjdict.get(ifacename)
289
290 def get_ifaceobjs_saved(self, ifacename):
291 """ Return ifaceobjects from statemanager """
292 if self.flags.STATEMANAGER_ENABLE:
293 return self.statemanager.get_ifaceobjs(ifacename)
294 else:
295 None
296
297 def get_ifaceobj_first(self, ifacename):
298 ifaceobjs = self.get_ifaceobjs(ifacename)
299 if ifaceobjs:
300 return ifaceobjs[0]
301 return None
302
303 def get_ifacenames(self):
304 return self.ifaceobjdict.keys()
305
306 def get_iface_obj_last(self, ifacename):
307 return self.ifaceobjdict.get(ifacename)[-1]
308
309
310 def must_follow_upperifaces(self, ifacename):
311 #
312 # XXX: This bleeds the knowledge of iface
313 # types in the infrastructure module.
314 # Cant think of a better fix at the moment.
315 # In future maybe the module can set a flag
316 # to indicate if we should follow upperifaces
317 #
318 ifaceobj = self.get_ifaceobj_first(ifacename)
319 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
320 return False
321 return True
322
323 def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
324 increfcnt=False):
325 """ creates a iface object and adds it to the iface dictionary """
326 ifaceobj = iface()
327 ifaceobj.name = ifacename
328 ifaceobj.priv_flags = priv_flags
329 ifaceobj.auto = True
330 if not self._link_master_slave:
331 ifaceobj.link_type = ifaceLinkType.LINK_NA
332 if increfcnt:
333 ifaceobj.inc_refcnt()
334 self.ifaceobjdict[ifacename] = [ifaceobj]
335 return ifaceobj
336
337 def create_n_save_ifaceobjcurr(self, ifaceobj):
338 """ creates a copy of iface object and adds it to the iface
339 dict containing current iface objects
340 """
341 ifaceobjcurr = iface()
342 ifaceobjcurr.name = ifaceobj.name
343 ifaceobjcurr.type = ifaceobj.type
344 ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
345 ifaceobjcurr.priv_flags = copy.deepcopy(ifaceobj.priv_flags)
346 ifaceobjcurr.auto = ifaceobj.auto
347 self.ifaceobjcurrdict.setdefault(ifaceobj.name,
348 []).append(ifaceobjcurr)
349 return ifaceobjcurr
350
351 def get_ifaceobjcurr(self, ifacename, idx=0):
352 ifaceobjlist = self.ifaceobjcurrdict.get(ifacename)
353 if not ifaceobjlist:
354 return None
355 if not idx:
356 return ifaceobjlist
357 else:
358 return ifaceobjlist[idx]
359
360 def get_ifaceobjrunning(self, ifacename):
361 return self.ifaceobjrunningdict.get(ifacename)
362
363 def get_iface_refcnt(self, ifacename):
364 """ Return iface ref count """
365 max = 0
366 ifaceobjs = self.get_ifaceobjs(ifacename)
367 if not ifaceobjs:
368 return 0
369 for i in ifaceobjs:
370 if i.refcnt > max:
371 max = i.refcnt
372 return max
373
374 def is_iface_builtin_byname(self, ifacename):
375 """ Returns true if iface name is a builtin interface.
376
377 A builtin interface is an interface which ifupdown understands.
378 The following are currently considered builtin ifaces:
379 - vlan interfaces in the format <ifacename>.<vlanid>
380 """
381 return '.' in ifacename
382
383 def is_ifaceobj_builtin(self, ifaceobj):
384 """ Returns true if iface name is a builtin interface.
385
386 A builtin interface is an interface which ifupdown understands.
387 The following are currently considered builtin ifaces:
388 - vlan interfaces in the format <ifacename>.<vlanid>
389 """
390
391 return (ifaceobj.priv_flags and ifaceobj.priv_flags.BUILTIN)
392
393 def is_ifaceobj_noconfig(self, ifaceobj):
394 """ Returns true if iface object did not have a user defined config.
395
396 These interfaces appear only when they are dependents of interfaces
397 which have user defined config
398 """
399 return (ifaceobj.priv_flags and ifaceobj.priv_flags.NOCONFIG)
400
401 def is_iface_noconfig(self, ifacename):
402 """ Returns true if iface has no config """
403
404 ifaceobj = self.get_ifaceobj_first(ifacename)
405 if not ifaceobj: return True
406 return self.is_ifaceobj_noconfig(ifaceobj)
407
408 def check_shared_dependents(self, ifaceobj, dlist):
409 """ Check if dlist intersects with any other
410 interface with slave dependents.
411 example: bond and bridges.
412 This function logs such errors """
413 setdlist = Set(dlist)
414 for ifacename, ifacedlist in self.dependency_graph.items():
415 if not ifacedlist:
416 continue
417 check_depends = False
418 iobjs = self.get_ifaceobjs(ifacename)
419 if not iobjs:
420 continue
421 for i in iobjs:
422 if (i.dependency_type == ifaceDependencyType.MASTER_SLAVE):
423 check_depends = True
424 if check_depends:
425 common = Set(ifacedlist).intersection(setdlist)
426 if common:
427 self.logger.error('misconfig..?. iface %s and %s '
428 %(ifaceobj.name, ifacename) +
429 'seem to share dependents/ports %s' %str(list(common)))
430
431 def _set_iface_role_n_kind(self, ifaceobj, upperifaceobj):
432 if (upperifaceobj.link_kind & ifaceLinkKind.BOND):
433 ifaceobj.role |= ifaceRole.SLAVE
434 ifaceobj.link_kind |= ifaceLinkKind.BOND_SLAVE
435 if (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
436 ifaceobj.role |= ifaceRole.SLAVE
437 ifaceobj.link_kind |= ifaceLinkKind.BRIDGE_PORT
438 if self._link_master_slave:
439 if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
440 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
441 else:
442 upperifaceobj.link_type = ifaceLinkType.LINK_NA
443 ifaceobj.link_type = ifaceLinkType.LINK_NA
444 if (ifaceobj.link_kind == ifaceLinkKind.BOND_SLAVE and
445 len(ifaceobj.upperifaces) > 1):
446 self.logger.warn("misconfig..? bond slave \'%s\' is enslaved to multiple interfaces %s" %(ifaceobj.name, str(ifaceobj.upperifaces)))
447
448 def dump_iface_dependency_info(self):
449 """ debug funtion to print raw dependency
450 info - lower and upper devices"""
451
452 for ifacename, ifaceobjs in self.ifaceobjdict.iteritems():
453 iobj = ifaceobjs[0]
454 self.logger.info("%s: refcnt: %d, lower: %s, upper: %s" %(ifacename,
455 self.get_iface_refcnt(ifacename),
456 str(iobj.lowerifaces) if iobj.lowerifaces else [],
457 str(iobj.upperifaces) if iobj.upperifaces else []))
458
459
460 def preprocess_dependency_list(self, upperifaceobj, dlist, ops):
461 """ We go through the dependency list and
462 delete or add interfaces from the interfaces dict by
463 applying the following rules:
464 if flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
465 we only consider devices whose configuration was
466 specified in the network interfaces file. We delete
467 any interface whose config was not specified except
468 for vlan devices. vlan devices get special treatment.
469 Even if they are not present they are created and added
470 to the ifacesdict
471 elif flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
472 we create objects for all dependent devices that are not
473 present in the ifacesdict
474 """
475 del_list = []
476
477 if (upperifaceobj.dependency_type == ifaceDependencyType.MASTER_SLAVE
478 and self.flags.CHECK_SHARED_DEPENDENTS):
479 self.check_shared_dependents(upperifaceobj, dlist)
480
481 for d in dlist:
482 dilist = self.get_ifaceobjs(d)
483 if not dilist:
484 ni = None
485 if self.is_iface_builtin_byname(d):
486 ni = self.create_n_save_ifaceobj(d,
487 ifacePrivFlags(True, True), True)
488 elif not self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
489 ni = self.create_n_save_ifaceobj(d,
490 ifacePrivFlags(False, True), True)
491 else:
492 del_list.append(d)
493 if ni:
494 ni.add_to_upperifaces(upperifaceobj.name)
495 self._set_iface_role_n_kind(ni, upperifaceobj)
496 else:
497 for di in dilist:
498 di.inc_refcnt()
499 di.add_to_upperifaces(upperifaceobj.name)
500 self._set_iface_role_n_kind(di, upperifaceobj)
501 for d in del_list:
502 dlist.remove(d)
503
504 def preprocess_upperiface(self, lowerifaceobj, ulist, ops):
505 for u in ulist:
506 if (lowerifaceobj.upperifaces and
507 u in lowerifaceobj.upperifaces):
508 continue
509 lowerifaceobj.add_to_upperifaces(u)
510 uifacelist = self.get_ifaceobjs(u)
511 if uifacelist:
512 for ui in uifacelist:
513 lowerifaceobj.inc_refcnt()
514 self._set_iface_role_n_kind(lowerifaceobj, ui)
515 ui.add_to_lowerifaces(lowerifaceobj.name)
516
517 def query_lowerifaces(self, ifaceobj, ops, ifacenames, type=None):
518 """ Gets iface dependents by calling into respective modules """
519 ret_dlist = []
520
521 # Get dependents for interface by querying respective modules
522 for module in self.modules.values():
523 try:
524 if ops[0] == 'query-running':
525 if (not hasattr(module,
526 'get_dependent_ifacenames_running')):
527 continue
528 dlist = module.get_dependent_ifacenames_running(ifaceobj)
529 else:
530 if (not hasattr(module, 'get_dependent_ifacenames')):
531 continue
532 dlist = module.get_dependent_ifacenames(ifaceobj,
533 ifacenames)
534 except Exception, e:
535 self.logger.warn('%s: error getting dependent interfaces (%s)'
536 %(ifaceobj.name, str(e)))
537 dlist = None
538 pass
539 if dlist: ret_dlist.extend(dlist)
540 return list(set(ret_dlist))
541
542 def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None):
543 """ Gets iface upperifaces by calling into respective modules """
544 ret_ulist = []
545
546 # Get upperifaces for interface by querying respective modules
547 for module in self.modules.values():
548 try:
549 if ops[0] == 'query-running':
550 if (not hasattr(module,
551 'get_upper_ifacenames_running')):
552 continue
553 ulist = module.get_upper_ifacenames_running(ifaceobj)
554 else:
555 if (not hasattr(module, 'get_upper_ifacenames')):
556 continue
557 ulist = module.get_upper_ifacenames(ifaceobj, ifacenames)
558 except Exception, e:
559 self.logger.warn('%s: error getting upper interfaces (%s)'
560 %(ifaceobj.name, str(e)))
561 ulist = None
562 pass
563 if ulist: ret_ulist.extend(ulist)
564 return list(set(ret_ulist))
565
566 def populate_dependency_info(self, ops, ifacenames=None):
567 """ recursive function to generate iface dependency info """
568
569 if not ifacenames:
570 ifacenames = self.ifaceobjdict.keys()
571
572 iqueue = deque(ifacenames)
573 while iqueue:
574 i = iqueue.popleft()
575 # Go through all modules and find dependent ifaces
576 dlist = None
577 ulist = None
578 ifaceobjs = self.get_ifaceobjs(i)
579 if not ifaceobjs:
580 continue
581 dependents_processed = False
582
583 # Store all dependency info in the first ifaceobj
584 # but get dependency info from all ifaceobjs
585 ifaceobj = ifaceobjs[0]
586 for iobj in ifaceobjs:
587 ulist = self.query_upperifaces(iobj, ops, ifacenames)
588 if iobj.lowerifaces:
589 dependents_processed = True
590 break
591 dlist = self.query_lowerifaces(iobj, ops, ifacenames)
592 if dlist:
593 break
594 if ulist:
595 self.preprocess_upperiface(ifaceobj, ulist, ops)
596 if dependents_processed:
597 continue
598 if dlist:
599 self.preprocess_dependency_list(ifaceobj,
600 dlist, ops)
601 ifaceobj.lowerifaces = dlist
602 [iqueue.append(d) for d in dlist]
603 #if not self.dependency_graph.get(i):
604 # self.dependency_graph[i] = dlist
605
606 for i in self.ifaceobjdict.keys():
607 iobj = self.get_ifaceobj_first(i)
608 if iobj.lowerifaces:
609 self.dependency_graph[i] = iobj.lowerifaces
610 else:
611 self.dependency_graph[i] = []
612
613 if not self.blacklisted_ifaces_present:
614 return
615
616 # Walk through the dependency graph and remove blacklisted
617 # interfaces that were picked up as dependents
618 for i in self.dependency_graph.keys():
619 ifaceobj = self.get_ifaceobj_first(i)
620 if not ifaceobj:
621 continue
622 if ifaceobj.blacklisted and not ifaceobj.upperifaces:
623 # if blacklisted and was not picked up as a
624 # dependent of a upper interface, delete the
625 # interface from the dependency graph
626 dlist = ifaceobj.lowerifaces
627 if dlist:
628 for d in dlist:
629 difaceobjs = self.get_ifaceobjs(d)
630 if not difaceobjs:
631 continue
632 try:
633 for d in difaceobjs:
634 d.dec_refcnt()
635 d.upperifaces.remove(i)
636 except:
637 self.logger.debug('error removing %s from %s upperifaces' %(i, d))
638 pass
639 self.logger.debug("populate_dependency_info: deleting blacklisted interface %s" %i)
640 del self.dependency_graph[i]
641
642 def _check_config_no_repeats(self, ifaceobj):
643 """ check if object has an attribute that is
644 restricted to a single object in the system.
645 if yes, warn and return """
646 for k,v in self._cache_no_repeats.items():
647 iv = ifaceobj.config.get(k)
648 if iv and iv[0] == v:
649 self.logger.error('ignoring interface %s. ' %ifaceobj.name +
650 'Only one object with attribute ' +
651 '\'%s %s\' allowed.' %(k, v))
652 return True
653 for k, v in self.config.get('no_repeats', {}).items():
654 iv = ifaceobj.config.get(k)
655 if iv and iv[0] == v:
656 self._cache_no_repeats[k] = v
657 return False
658
659 def _save_iface(self, ifaceobj):
660 if self._check_config_no_repeats(ifaceobj):
661 return
662 ifaceobj.priv_flags = ifacePrivFlags()
663 if not self._link_master_slave:
664 ifaceobj.link_type = ifaceLinkType.LINK_NA
665 currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
666 if not currentifaceobjlist:
667 self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
668 if not self._ifaceobj_squash:
669 ifaceobj.flags |= ifaceobj.YOUNGEST_SIBLING
670 return
671 if self._ifaceobj_squash:
672 # squash with old iface object
673 currentifaceobjlist[0].squash(ifaceobj)
674 return
675 if ifaceobj.compare(currentifaceobjlist[0]):
676 self.logger.warn('duplicate interface %s found' %ifaceobj.name)
677 return
678 if currentifaceobjlist[0].type == ifaceobj.type:
679 currentifaceobjlist[0].flags |= ifaceobj.HAS_SIBLINGS
680 ifaceobj.flags |= ifaceobj.HAS_SIBLINGS
681 # clear the OLDEST_SIBLING from all the siblings
682 for iface in self.ifaceobjdict[ifaceobj.name]:
683 iface.flags &= ~ifaceobj.OLDEST_SIBLING
684 # current sibling is the oldest
685 ifaceobj.flags |= ifaceobj.OLDEST_SIBLING
686 self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
687
688 def _iface_configattr_syntax_checker(self, attrname, attrval):
689 for m, mdict in self.module_attrs.items():
690 if not mdict:
691 continue
692 attrsdict = mdict.get('attrs')
693 try:
694 a = attrsdict.get(attrname)
695 if a:
696 if a.get('deprecated'):
697 newa = a.get('new-attribute')
698 if newa:
699 self.logger.warn('attribute %s is deprecated. Use %s instead' %(attrname, newa))
700 else:
701 self.logger.warn('attribute %s is deprecated'
702 %attrname)
703 return True
704 except AttributeError:
705 pass
706 return False
707
708 def _ifaceobj_syntax_checker(self, ifaceobj):
709 ret = True
710 for attrname, attrvalue in ifaceobj.config.items():
711 found = False
712 for k, v in self.module_attrs.items():
713 if v and v.get('attrs', {}).get(attrname):
714 found = True
715 break
716 if not found:
717 ret = False
718 self.logger.warn('%s: unsupported attribute \'%s\'' \
719 % (ifaceobj.name, attrname))
720 continue
721 return ret
722
723 def read_iface_config(self):
724 """ Reads default network interface config /etc/network/interfaces. """
725 ret = True
726 nifaces = networkInterfaces(self.interfacesfile,
727 self.interfacesfileiobuf,
728 self.interfacesfileformat,
729 template_engine=self.config.get('template_engine'),
730 template_lookuppath=self.config.get('template_lookuppath'))
731 nifaces.subscribe('iface_found', self._save_iface)
732 nifaces.subscribe('validateifaceattr',
733 self._iface_configattr_syntax_checker)
734 nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker)
735 nifaces.load()
736 if nifaces.errors or nifaces.warns:
737 ret = False
738 return ret
739
740 def read_old_iface_config(self):
741 """ Reads the saved iface config instead of default iface config.
742 And saved iface config is already read by the statemanager """
743 self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
744
745 def _load_addon_modules_config(self):
746 """ Load addon modules config file """
747
748 with open(self.addon_modules_configfile, 'r') as f:
749 lines = f.readlines()
750 for l in lines:
751 try:
752 litems = l.strip(' \n\t\r').split(',')
753 if not litems or len(litems) < 2:
754 continue
755 operation = litems[0]
756 mname = litems[1]
757 self.module_ops[operation].append(mname)
758 except Exception, e:
759 self.logger.warn('error reading line \'%s\'' %(l, str(e)))
760 continue
761
762 def load_addon_modules(self, modules_dir):
763 """ load python modules from modules_dir
764
765 Default modules_dir is /usr/share/ifupdownmodules
766
767 """
768 self.logger.info('loading builtin modules from %s' %modules_dir)
769 self._load_addon_modules_config()
770 if not modules_dir in sys.path:
771 sys.path.append(modules_dir)
772 try:
773 for op, mlist in self.module_ops.items():
774 for mname in mlist:
775 if self.modules.get(mname):
776 continue
777 mpath = modules_dir + '/' + mname + '.py'
778 if os.path.exists(mpath):
779 try:
780 m = __import__(mname)
781 mclass = getattr(m, mname)
782 except:
783 raise
784 minstance = mclass(force=self.FORCE,
785 dryrun=self.DRYRUN,
786 nowait=self.NOWAIT,
787 perfmode=self.PERFMODE,
788 cache=self.CACHE,
789 cacheflags=self.CACHE_FLAGS)
790 self.modules[mname] = minstance
791 try:
792 self.module_attrs[mname] = minstance.get_modinfo()
793 except:
794 pass
795 except:
796 raise
797
798 # Assign all modules to query operations
799 self.module_ops['query-checkcurr'] = self.modules.keys()
800 self.module_ops['query-running'] = self.modules.keys()
801 self.module_ops['query-dependency'] = self.modules.keys()
802 self.module_ops['query'] = self.modules.keys()
803 self.module_ops['query-raw'] = self.modules.keys()
804
805
806 def _modules_help(self):
807 """ Prints addon modules supported syntax """
808
809 indent = ' '
810 for m, mdict in self.module_attrs.items():
811 if not mdict:
812 continue
813 print('%s: %s' %(m, mdict.get('mhelp')))
814 attrdict = mdict.get('attrs')
815 if not attrdict:
816 continue
817 try:
818 for attrname, attrvaldict in attrdict.items():
819 if attrvaldict.get('compat', False):
820 continue
821 print('%s%s' %(indent, attrname))
822 print('%shelp: %s' %(indent + ' ',
823 attrvaldict.get('help', '')))
824 print ('%srequired: %s' %(indent + ' ',
825 attrvaldict.get('required', False)))
826 default = attrvaldict.get('default')
827 if default:
828 print('%sdefault: %s' %(indent + ' ', default))
829
830 validrange = attrvaldict.get('validrange')
831 if validrange:
832 print('%svalidrange: %s-%s'
833 %(indent + ' ', validrange[0], validrange[1]))
834
835 validvals = attrvaldict.get('validvals')
836 if validvals:
837 print('%svalidvals: %s'
838 %(indent + ' ', ','.join(validvals)))
839
840 examples = attrvaldict.get('example')
841 if not examples:
842 continue
843
844 print '%sexample:' %(indent + ' ')
845 for e in examples:
846 print '%s%s' %(indent + ' ', e)
847 except:
848 pass
849 print ''
850
851 def load_scripts(self, modules_dir):
852 """ loading user modules from /etc/network/.
853
854 Note that previously loaded python modules override modules found
855 under /etc/network if any
856
857 """
858
859 self.logger.info('looking for user scripts under %s' %modules_dir)
860 for op, mlist in self.script_ops.items():
861 msubdir = modules_dir + '/if-%s.d' %op
862 self.logger.info('loading scripts under %s ...' %msubdir)
863 try:
864 module_list = os.listdir(msubdir)
865 for module in module_list:
866 if self.modules.get(module) is not None:
867 continue
868 self.script_ops[op].append(
869 msubdir + '/' + module)
870 except:
871 # continue reading
872 pass
873
874 def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False,
875 followdependents=True, sort=False):
876 self.logger.debug('scheduling \'%s\' for %s'
877 %(str(ops), str(ifacenames)))
878 self._pretty_print_ordered_dict('dependency graph',
879 self.dependency_graph)
880 return ifaceScheduler.sched_ifaces(self, ifacenames, ops,
881 dependency_graph=self.dependency_graph,
882 order=ifaceSchedulerFlags.INORDER
883 if 'down' in ops[0]
884 else ifaceSchedulerFlags.POSTORDER,
885 followdependents=followdependents,
886 skipupperifaces=skipupperifaces,
887 sort=True if (sort or self.flags.IFACE_CLASS) else False)
888
889 def _render_ifacename(self, ifacename):
890 new_ifacenames = []
891 vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
892 if vlan_match:
893 vlan_groups = vlan_match.groups()
894 if vlan_groups[0] and vlan_groups[1]:
895 [new_ifacenames.append('%d' %v)
896 for v in range(int(vlan_groups[0]),
897 int(vlan_groups[1])+1)]
898 return new_ifacenames
899
900 def _preprocess_ifacenames(self, ifacenames):
901 """ validates interface list for config existance.
902
903 returns -1 if one or more interface not found. else, returns 0
904
905 """
906 new_ifacenames = []
907 err_iface = ''
908 for i in ifacenames:
909 ifaceobjs = self.get_ifaceobjs(i)
910 if not ifaceobjs:
911 # if name not available, render interface name and check again
912 rendered_ifacenames = utils.expand_iface_range(i)
913 if rendered_ifacenames:
914 for ri in rendered_ifacenames:
915 ifaceobjs = self.get_ifaceobjs(ri)
916 if not ifaceobjs:
917 err_iface += ' ' + ri
918 else:
919 new_ifacenames.append(ri)
920 else:
921 err_iface += ' ' + i
922 else:
923 new_ifacenames.append(i)
924 if err_iface:
925 raise Exception('cannot find interfaces:%s' %err_iface)
926 return new_ifacenames
927
928 def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
929 """ Checks if interface is whitelisted depending on set of parameters.
930
931 interfaces are checked against the allow_classes and auto lists.
932
933 """
934
935 ret = True
936
937 # Check if interface matches the exclude patter
938 if excludepats:
939 for e in excludepats:
940 if re.search(e, ifacename):
941 ret = False
942 ifaceobjs = self.get_ifaceobjs(ifacename)
943 if not ifaceobjs:
944 if ret:
945 self.logger.debug('iface %s' %ifacename + ' not found')
946 return ret
947 # If matched exclude pattern, return false
948 if not ret:
949 for i in ifaceobjs:
950 i.blacklisted = True
951 self.blacklisted_ifaces_present = True
952 return ret
953 # Check if interface belongs to the class
954 # the user is interested in, if not return false
955 if allow_classes:
956 ret = False
957 for i in ifaceobjs:
958 if i.classes:
959 common = Set([allow_classes]).intersection(
960 Set(i.classes))
961 if common:
962 ret = True
963 if not ret:
964 # If a class was requested and interface does not belong
965 # to the class, only then mark the ifaceobjs as blacklisted
966 self.blacklisted_ifaces_present = True
967 for i in ifaceobjs:
968 i.blacklisted = True
969 return ret
970 # If the user has requested auto class, check if the interface
971 # is marked auto
972 if auto:
973 ret = False
974 for i in ifaceobjs:
975 if i.auto:
976 ret = True
977 if not ret:
978 # If auto was requested and interface was not marked auto,
979 # only then mark all of them as blacklisted
980 self.blacklisted_ifaces_present = True
981 for i in ifaceobjs:
982 i.blacklisted = True
983 return ret
984
985 def _compat_conv_op_to_mode(self, op):
986 """ Returns old op name to work with existing scripts """
987 if op == 'pre-up':
988 return 'start'
989 elif op == 'pre-down':
990 return 'stop'
991 else:
992 return op
993
994 def generate_running_env(self, ifaceobj, op):
995 """ Generates a dictionary with env variables required for
996 an interface. Used to support script execution for interfaces.
997 """
998
999 cenv = None
1000 iface_env = ifaceobj.env
1001 if iface_env:
1002 cenv = os.environ
1003 if cenv:
1004 cenv.update(iface_env)
1005 else:
1006 cenv = iface_env
1007 cenv['MODE'] = self._compat_conv_op_to_mode(op)
1008 return cenv
1009
1010 def _save_state(self):
1011 if (not self.flags.STATEMANAGER_ENABLE or
1012 not self.flags.STATEMANAGER_UPDATE):
1013 return
1014 try:
1015 # Update persistant iface states
1016 self.statemanager.save_state()
1017 except Exception, e:
1018 if self.logger.isEnabledFor(logging.DEBUG):
1019 t = sys.exc_info()[2]
1020 traceback.print_tb(t)
1021 self.logger.warning('error saving state (%s)' %str(e))
1022
1023 def set_type(self, type):
1024 if type == 'iface':
1025 self.type = ifaceType.IFACE
1026 elif type == 'vlan':
1027 self.type = ifaceType.BRIDGE_VLAN
1028 else:
1029 self.type = ifaceType.UNKNOWN
1030
1031 def _process_delay_admin_state_queue(self, op):
1032 if not self._delay_admin_state_iface_queue:
1033 return
1034 if op == 'up':
1035 func = self.link_up
1036 elif op == 'down':
1037 func = self.link_down
1038 else:
1039 return
1040 for i in self._delay_admin_state_iface_queue:
1041 try:
1042 if self.link_exists(i):
1043 func(i)
1044 except Exception, e:
1045 self.logger.warn(str(e))
1046 pass
1047
1048 def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
1049 excludepats=None, printdependency=None, syntaxcheck=False,
1050 type=None, skipupperifaces=False):
1051 """This brings the interface(s) up
1052
1053 Args:
1054 ops (list): list of ops to perform on the interface(s).
1055 Eg: ['pre-up', 'up', 'post-up'
1056
1057 Kwargs:
1058 auto (bool): act on interfaces marked auto
1059 allow_classes (list): act on interfaces belonging to classes in the list
1060 ifacenames (list): act on interfaces specified in this list
1061 excludepats (list): list of patterns of interfaces to exclude
1062 syntaxcheck (bool): only perform syntax check
1063 """
1064
1065 self.set_type(type)
1066
1067 if allow_classes:
1068 self.flags.IFACE_CLASS = True
1069 if not self.flags.ADDONS_ENABLE:
1070 self.flags.STATEMANAGER_UPDATE = False
1071 if auto:
1072 self.flags.ALL = True
1073 self.flags.WITH_DEPENDS = True
1074 try:
1075 iface_read_ret = self.read_iface_config()
1076 except Exception:
1077 raise
1078
1079 if ifacenames:
1080 ifacenames = self._preprocess_ifacenames(ifacenames)
1081
1082 # if iface list not given by user, assume all from config file
1083 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
1084
1085 # filter interfaces based on auto and allow classes
1086 filtered_ifacenames = [i for i in ifacenames
1087 if self._iface_whitelisted(auto, allow_classes,
1088 excludepats, i)]
1089 if not filtered_ifacenames:
1090 raise Exception('no ifaces found matching given allow lists')
1091
1092 if printdependency:
1093 self.populate_dependency_info(ops, filtered_ifacenames)
1094 self.print_dependency(filtered_ifacenames, printdependency)
1095 return
1096 else:
1097 self.populate_dependency_info(ops)
1098
1099 # If only syntax check was requested, return here.
1100 # return here because we want to make sure most
1101 # errors above are caught and reported.
1102 if syntaxcheck:
1103 if not iface_read_ret:
1104 raise Exception()
1105 return
1106
1107 try:
1108 self._sched_ifaces(filtered_ifacenames, ops,
1109 skipupperifaces=skipupperifaces,
1110 followdependents=True if self.flags.WITH_DEPENDS else False)
1111 finally:
1112 self._process_delay_admin_state_queue('up')
1113 if not self.DRYRUN and self.flags.ADDONS_ENABLE:
1114 self._save_state()
1115
1116 def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
1117 excludepats=None, printdependency=None, usecurrentconfig=False,
1118 type=None):
1119 """ down an interface """
1120
1121 self.set_type(type)
1122
1123 if allow_classes:
1124 self.flags.IFACE_CLASS = True
1125 if not self.flags.ADDONS_ENABLE:
1126 self.flags.STATEMANAGER_UPDATE = False
1127 if auto:
1128 self.flags.ALL = True
1129 self.flags.WITH_DEPENDS = True
1130 # For down we need to look at old state, unless usecurrentconfig
1131 # is set
1132 if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE and
1133 self.statemanager.ifaceobjdict):
1134 # Since we are using state manager objects,
1135 # skip the updating of state manager objects
1136 self.logger.debug('Looking at old state ..')
1137 self.read_old_iface_config()
1138 else:
1139 # If no old state available
1140 try:
1141 self.read_iface_config()
1142 except Exception, e:
1143 raise Exception('error reading iface config (%s)' %str(e))
1144 if ifacenames:
1145 # If iface list is given by the caller, always check if iface
1146 # is present
1147 try:
1148 ifacenames = self._preprocess_ifacenames(ifacenames)
1149 except Exception, e:
1150 raise Exception('%s' %str(e) +
1151 ' (interface was probably never up ?)')
1152
1153 # if iface list not given by user, assume all from config file
1154 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
1155
1156 # filter interfaces based on auto and allow classes
1157 filtered_ifacenames = [i for i in ifacenames
1158 if self._iface_whitelisted(auto, allow_classes,
1159 excludepats, i)]
1160 if not filtered_ifacenames:
1161 raise Exception('no ifaces found matching given allow lists ' +
1162 '(or interfaces were probably never up ?)')
1163
1164 if printdependency:
1165 self.populate_dependency_info(ops, filtered_ifacenames)
1166 self.print_dependency(filtered_ifacenames, printdependency)
1167 return
1168 else:
1169 self.populate_dependency_info(ops)
1170
1171 try:
1172 self._sched_ifaces(filtered_ifacenames, ops,
1173 followdependents=True
1174 if self.flags.WITH_DEPENDS else False)
1175 finally:
1176 self._process_delay_admin_state_queue('down')
1177 if not self.DRYRUN and self.flags.ADDONS_ENABLE:
1178 self._save_state()
1179
1180 def query(self, ops, auto=False, allow_classes=None, ifacenames=None,
1181 excludepats=None, printdependency=None,
1182 format='native', type=None):
1183 """ query an interface """
1184
1185 self.set_type(type)
1186
1187 if allow_classes:
1188 self.flags.IFACE_CLASS = True
1189 if self.flags.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
1190 return self.statemanager.dump_pretty(ifacenames)
1191 self.flags.STATEMANAGER_UPDATE = False
1192 if auto:
1193 self.logger.debug('setting flag ALL')
1194 self.flags.ALL = True
1195 self.flags.WITH_DEPENDS = True
1196
1197 if ops[0] == 'query-syntax':
1198 self._modules_help()
1199 return
1200 elif ops[0] == 'query-running':
1201 # create fake devices to all dependents that dont have config
1202 map(lambda i: self.create_n_save_ifaceobj(i,
1203 ifacePrivFlags(False, True)), ifacenames)
1204 else:
1205 try:
1206 self.read_iface_config()
1207 except Exception:
1208 raise
1209
1210 if ifacenames and ops[0] != 'query-running':
1211 # If iface list is given, always check if iface is present
1212 ifacenames = self._preprocess_ifacenames(ifacenames)
1213
1214 # if iface list not given by user, assume all from config file
1215 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
1216
1217 # filter interfaces based on auto and allow classes
1218 if ops[0] == 'query-running':
1219 filtered_ifacenames = ifacenames
1220 else:
1221 filtered_ifacenames = [i for i in ifacenames
1222 if self._iface_whitelisted(auto, allow_classes,
1223 excludepats, i)]
1224 if not filtered_ifacenames:
1225 raise Exception('no ifaces found matching ' +
1226 'given allow lists')
1227
1228 self.populate_dependency_info(ops)
1229 if ops[0] == 'query-dependency' and printdependency:
1230 self.print_dependency(filtered_ifacenames, printdependency)
1231 return
1232
1233 if ops[0] == 'query':
1234 return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
1235 elif ops[0] == 'query-raw':
1236 return self.print_ifaceobjs_raw(filtered_ifacenames)
1237
1238 self._sched_ifaces(filtered_ifacenames, ops,
1239 followdependents=True
1240 if self.flags.WITH_DEPENDS else False)
1241
1242 if ops[0] == 'query-checkcurr':
1243 ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames, format)
1244 if ret != 0:
1245 # if any of the object has an error, signal that silently
1246 raise Exception('')
1247 elif ops[0] == 'query-running':
1248 self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
1249 return
1250
1251 def _reload_currentlyup(self, upops, downops, auto=True, allow=None,
1252 ifacenames=None, excludepats=None, usecurrentconfig=False,
1253 syntaxcheck=False, **extra_args):
1254 """ reload currently up interfaces """
1255 new_ifaceobjdict = {}
1256
1257 # Override auto to true
1258 auto = True
1259 try:
1260 iface_read_ret = self.read_iface_config()
1261 except:
1262 raise
1263 if not self.ifaceobjdict:
1264 self.logger.warn("nothing to reload ..exiting.")
1265 return
1266 already_up_ifacenames = []
1267 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
1268 filtered_ifacenames = [i for i in ifacenames
1269 if self._iface_whitelisted(auto, allow,
1270 excludepats, i)]
1271
1272 # generate dependency graph of interfaces
1273 self.populate_dependency_info(upops)
1274
1275 # If only syntax check was requested, return here.
1276 # return here because we want to make sure most
1277 # errors above are caught and reported.
1278 if syntaxcheck:
1279 if not iface_read_ret:
1280 raise Exception()
1281 return
1282
1283 if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
1284 and self.statemanager.ifaceobjdict):
1285 already_up_ifacenames = self.statemanager.ifaceobjdict.keys()
1286
1287 # Get already up interfaces that still exist in the interfaces file
1288 already_up_ifacenames_not_present = Set(
1289 already_up_ifacenames).difference(ifacenames)
1290 already_up_ifacenames_still_present = Set(
1291 already_up_ifacenames).difference(
1292 already_up_ifacenames_not_present)
1293 interfaces_to_up = Set(already_up_ifacenames_still_present).union(
1294 filtered_ifacenames)
1295
1296 if (already_up_ifacenames_not_present and
1297 self.config.get('ifreload_currentlyup_down_notpresent') == '1'):
1298 self.logger.info('reload: schedule down on interfaces: %s'
1299 %str(already_up_ifacenames_not_present))
1300
1301 # Save a copy of new iface objects and dependency_graph
1302 new_ifaceobjdict = dict(self.ifaceobjdict)
1303 new_dependency_graph = dict(self.dependency_graph)
1304
1305 # old interface config is read into self.ifaceobjdict
1306 self.read_old_iface_config()
1307
1308 # reinitialize dependency graph
1309 self.dependency_graph = OrderedDict({})
1310 falready_up_ifacenames_not_present = [i for i in
1311 already_up_ifacenames_not_present
1312 if self._iface_whitelisted(auto, allow,
1313 excludepats, i)]
1314 self.populate_dependency_info(downops,
1315 falready_up_ifacenames_not_present)
1316 self._sched_ifaces(falready_up_ifacenames_not_present, downops,
1317 followdependents=False, sort=True)
1318 else:
1319 self.logger.debug('no interfaces to down ..')
1320
1321 # Now, run 'up' with new config dict
1322 # reset statemanager update flag to default
1323 if auto:
1324 self.flags.ALL = True
1325 self.flags.WITH_DEPENDS = True
1326 if new_ifaceobjdict:
1327 # and now, ifaceobjdict is back to current config
1328 self.ifaceobjdict = new_ifaceobjdict
1329 self.dependency_graph = new_dependency_graph
1330
1331 if not self.ifaceobjdict:
1332 return
1333 self.logger.info('reload: scheduling up on interfaces: %s'
1334 %str(interfaces_to_up))
1335 self._sched_ifaces(interfaces_to_up, upops,
1336 followdependents=True
1337 if self.flags.WITH_DEPENDS else False)
1338 if self.DRYRUN:
1339 return
1340 self._save_state()
1341
1342 def _reload_default(self, upops, downops, auto=False, allow=None,
1343 ifacenames=None, excludepats=None, usecurrentconfig=False,
1344 syntaxcheck=False, **extra_args):
1345 """ reload interface config """
1346 new_ifaceobjdict = {}
1347
1348 try:
1349 iface_read_ret = self.read_iface_config()
1350 except:
1351 raise
1352
1353 if not self.ifaceobjdict:
1354 self.logger.warn("nothing to reload ..exiting.")
1355 return
1356
1357 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
1358 new_filtered_ifacenames = [i for i in ifacenames
1359 if self._iface_whitelisted(auto, allow,
1360 excludepats, i)]
1361 # generate dependency graph of interfaces
1362 self.populate_dependency_info(upops)
1363
1364 # If only syntax check was requested, return here.
1365 # return here because we want to make sure most
1366 # errors above are caught and reported.
1367 if syntaxcheck:
1368 if not iface_read_ret:
1369 raise Exception()
1370 return
1371
1372 if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
1373 and self.statemanager.ifaceobjdict):
1374 # Save a copy of new iface objects and dependency_graph
1375 new_ifaceobjdict = dict(self.ifaceobjdict)
1376 new_dependency_graph = dict(self.dependency_graph)
1377
1378 self.ifaceobjdict = OrderedDict({})
1379 self.dependency_graph = OrderedDict({})
1380
1381 # if old state is present, read old state and mark op for 'down'
1382 # followed by 'up' aka: reload
1383 # old interface config is read into self.ifaceobjdict
1384 self.read_old_iface_config()
1385 op = 'reload'
1386 else:
1387 # oldconfig not available, continue with 'up' with new config
1388 op = 'up'
1389
1390 if op == 'reload' and ifacenames:
1391 ifacenames = self.ifaceobjdict.keys()
1392 old_filtered_ifacenames = [i for i in ifacenames
1393 if self._iface_whitelisted(auto, allow,
1394 excludepats, i)]
1395
1396 # generate dependency graph of old interfaces,
1397 # This should make sure built in interfaces are
1398 # populated. disable check shared dependents as an optimization.
1399 # these are saved interfaces and dependency for these
1400 # have been checked before they became part of saved state.
1401 try:
1402 self.flags.CHECK_SHARED_DEPENDENTS = False
1403 self.populate_dependency_info(upops)
1404 self.flags.CHECK_SHARED_DEPENDENTS = True
1405 except Exception, e:
1406 self.logger.info("error generating dependency graph for "
1407 "saved interfaces (%s)" %str(e))
1408 pass
1409
1410 # make sure we pick up built-in interfaces
1411 # if config file had 'ifreload_down_changed' variable
1412 # set, also look for interfaces that changed to down them
1413 down_changed = int(self.config.get('ifreload_down_changed', '1'))
1414
1415 # Generate the interface down list
1416 # Interfaces that go into the down list:
1417 # - interfaces that were present in last config and are not
1418 # present in the new config
1419 # - interfaces that were changed between the last and current
1420 # config
1421 ifacedownlist = []
1422 for ifname in self.ifaceobjdict.keys():
1423 lastifaceobjlist = self.ifaceobjdict.get(ifname)
1424 if not self.is_ifaceobj_builtin(lastifaceobjlist[0]):
1425 # if interface is not built-in and is not in
1426 # old filtered ifacenames
1427 if ifname not in old_filtered_ifacenames:
1428 continue
1429 objidx = 0
1430 # If interface is not present in the new file
1431 # append it to the down list
1432 newifaceobjlist = new_ifaceobjdict.get(ifname)
1433 if not newifaceobjlist:
1434 ifacedownlist.append(ifname)
1435 continue
1436 # If ifaceobj was present in the old interfaces file,
1437 # and does not have a config in the new interfaces file
1438 # but has been picked up as a dependent of another
1439 # interface, catch it here. This catches a common error
1440 # for example: remove a bond section from the interfaces
1441 # file, but leave it around as a bridge port
1442 # XXX: Ideally its better to just add it to the
1443 # ifacedownlist. But we will be cautious here
1444 # and just print a warning
1445 if (self.is_ifaceobj_noconfig(newifaceobjlist[0]) and
1446 not self.is_ifaceobj_builtin(newifaceobjlist[0]) and
1447 lastifaceobjlist[0].is_config_present()):
1448 self.logger.warn('%s: misconfig ? removed but still exists as a dependency of %s' %(newifaceobjlist[objidx].name,
1449 str(newifaceobjlist[objidx].upperifaces)))
1450 if not down_changed:
1451 continue
1452 if len(newifaceobjlist) != len(lastifaceobjlist):
1453 ifacedownlist.append(ifname)
1454 continue
1455
1456 # If interface has changed between the current file
1457 # and the last installed append it to the down list
1458 # compare object list
1459 for objidx in range(0, len(lastifaceobjlist)):
1460 oldobj = lastifaceobjlist[objidx]
1461 newobj = newifaceobjlist[objidx]
1462 if not newobj.compare(oldobj):
1463 ifacedownlist.append(ifname)
1464 continue
1465
1466 if ifacedownlist:
1467 self.logger.info('reload: scheduling down on interfaces: %s'
1468 %str(ifacedownlist))
1469 # reinitialize dependency graph
1470 self.dependency_graph = OrderedDict({})
1471
1472 # Generate dependency info for old config
1473 self.flags.CHECK_SHARED_DEPENDENTS = False
1474 self.populate_dependency_info(downops, ifacedownlist)
1475 self.flags.CHECK_SHARED_DEPENDENTS = True
1476
1477 try:
1478 # XXX: Hack to skip checking upperifaces during down.
1479 # the dependency list is not complete here
1480 # and we dont want to down the upperiface.
1481 # Hence during reload, set this to true.
1482 # This is being added to avoid a failure in
1483 # scheduler._check_upperifaces when we are dowing
1484 # a builtin bridge port
1485 self.flags.SCHED_SKIP_CHECK_UPPERIFACES = True
1486 self._sched_ifaces(ifacedownlist, downops,
1487 followdependents=False,
1488 sort=True)
1489 except Exception, e:
1490 self.logger.error(str(e))
1491 pass
1492 finally:
1493 self.flags.SCHED_SKIP_CHECK_UPPERIFACES = False
1494 self._process_delay_admin_state_queue('down')
1495 else:
1496 self.logger.debug('no interfaces to down ..')
1497
1498 # Now, run 'up' with new config dict
1499 # reset statemanager update flag to default
1500 if not new_ifaceobjdict:
1501 return
1502
1503 if auto:
1504 self.flags.ALL = True
1505 self.flags.WITH_DEPENDS = True
1506 # and now, we are back to the current config in ifaceobjdict
1507 self.ifaceobjdict = new_ifaceobjdict
1508 self.dependency_graph = new_dependency_graph
1509
1510 self.logger.info('reload: scheduling up on interfaces: %s'
1511 %str(new_filtered_ifacenames))
1512 try:
1513 self._sched_ifaces(new_filtered_ifacenames, upops,
1514 followdependents=True
1515 if self.flags.WITH_DEPENDS else False)
1516 except Exception, e:
1517 self.logger.error(str(e))
1518 pass
1519 finally:
1520 self._process_delay_admin_state_queue('up')
1521 if self.DRYRUN:
1522 return
1523 self._save_state()
1524
1525 def reload(self, *args, **kargs):
1526 """ reload interface config """
1527 self.logger.debug('reloading interface config ..')
1528 if kargs.get('currentlyup', False):
1529 self._reload_currentlyup(*args, **kargs)
1530 else:
1531 self._reload_default(*args, **kargs)
1532
1533 def _pretty_print_ordered_dict(self, prefix, argdict):
1534 outbuf = prefix + ' {\n'
1535 for k, vlist in argdict.items():
1536 outbuf += '\t%s : %s\n' %(k, str(vlist))
1537 self.logger.debug(outbuf + '}')
1538
1539 def print_dependency(self, ifacenames, format):
1540 """ prints iface dependency information """
1541
1542 if not ifacenames:
1543 ifacenames = self.ifaceobjdict.keys()
1544 if format == 'list':
1545 for k,v in self.dependency_graph.items():
1546 print '%s : %s' %(k, str(v))
1547 elif format == 'dot':
1548 indegrees = {}
1549 map(lambda i: indegrees.update({i :
1550 self.get_iface_refcnt(i)}),
1551 self.dependency_graph.keys())
1552 graph.generate_dots(self.dependency_graph, indegrees)
1553
1554 def print_ifaceobjs_raw(self, ifacenames):
1555 """ prints raw lines for ifaces from config file """
1556
1557 for i in ifacenames:
1558 for ifaceobj in self.get_ifaceobjs(i):
1559 if (self.is_ifaceobj_builtin(ifaceobj) or
1560 not ifaceobj.is_config_present()):
1561 continue
1562 ifaceobj.dump_raw(self.logger)
1563 print '\n'
1564 if self.flags.WITH_DEPENDS and not self.flags.ALL:
1565 dlist = ifaceobj.lowerifaces
1566 if not dlist: continue
1567 self.print_ifaceobjs_raw(dlist)
1568
1569 def _get_ifaceobjs_pretty(self, ifacenames, ifaceobjs, running=False):
1570 """ returns iface obj list """
1571
1572 for i in ifacenames:
1573 for ifaceobj in self.get_ifaceobjs(i):
1574 if ((not running and self.is_ifaceobj_noconfig(ifaceobj)) or
1575 (running and not ifaceobj.is_config_present())):
1576 continue
1577 ifaceobjs.append(ifaceobj)
1578 if self.flags.WITH_DEPENDS and not self.flags.ALL:
1579 dlist = ifaceobj.lowerifaces
1580 if not dlist: continue
1581 self._get_ifaceobjs_pretty(dlist, ifaceobjs, running)
1582
1583 def print_ifaceobjs_pretty(self, ifacenames, format='native'):
1584 """ pretty prints iface in format given by keyword arg format """
1585
1586 ifaceobjs = []
1587 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs)
1588 if not ifaceobjs: return
1589 if format == 'json':
1590 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
1591 indent=4, separators=(',', ': '))
1592 else:
1593 expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
1594 for i in ifaceobjs:
1595 if not expand and (i.flags & iface.IFACERANGE_ENTRY):
1596 # print only the first one
1597 if i.flags & iface.IFACERANGE_START:
1598 i.dump_pretty(use_realname=True)
1599 else:
1600 i.dump_pretty()
1601
1602 def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
1603 ret = 0
1604 for i in ifacenames:
1605 ifaceobjscurr = self.get_ifaceobjcurr(i)
1606 if not ifaceobjscurr: continue
1607 for ifaceobj in ifaceobjscurr:
1608 if (ifaceobj.status == ifaceStatus.NOTFOUND or
1609 ifaceobj.status == ifaceStatus.ERROR):
1610 ret = 1
1611 if self.is_ifaceobj_noconfig(ifaceobj):
1612 continue
1613 ifaceobjs.append(ifaceobj)
1614 if self.flags.WITH_DEPENDS and not self.flags.ALL:
1615 dlist = ifaceobj.lowerifaces
1616 if not dlist: continue
1617 dret = self._get_ifaceobjscurr_pretty(dlist, ifaceobjs)
1618 if dret: ret = 1
1619 return ret
1620
1621 def print_ifaceobjscurr_pretty(self, ifacenames, format='native'):
1622 """ pretty prints current running state of interfaces with status.
1623
1624 returns 1 if any of the interface has an error,
1625 else returns 0
1626 """
1627
1628 ifaceobjs = []
1629 ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
1630 if not ifaceobjs: return
1631
1632 # override ifaceStatusUserStrs
1633 ifaceStatusUserStrs.SUCCESS = self.config.get('ifquery_check_success_str', _success_sym)
1634 ifaceStatusUserStrs.ERROR = self.config.get('ifquery_check_error_str', _error_sym)
1635 ifaceStatusUserStrs.UNKNOWN = self.config.get('ifquery_check_unknown_str', '')
1636 if format == 'json':
1637 print json.dumps(ifaceobjs, cls=ifaceJsonEncoderWithStatus,
1638 indent=2, separators=(',', ': '))
1639 else:
1640 map(lambda i: i.dump_pretty(with_status=True), ifaceobjs)
1641 return ret
1642
1643 def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
1644 """ pretty prints iface running state """
1645
1646 ifaceobjs = []
1647 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs, running=True)
1648 if not ifaceobjs: return
1649 if format == 'json':
1650 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
1651 separators=(',', ': '))
1652 else:
1653 map(lambda i: i.dump_pretty(), ifaceobjs)
1654
1655 def _dump(self):
1656 print 'ifupdown main object dump'
1657 print self.pp.pprint(self.modules)
1658 print self.pp.pprint(self.ifaceobjdict)
1659
1660 def _dump_ifaceobjs(self, ifacenames):
1661 for i in ifacenames:
1662 ifaceobjs = self.get_ifaceobjs(i)
1663 for i in ifaceobjs:
1664 i.dump(self.logger)
1665 print '\n'