3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
18 from statemanager
import *
19 from networkinterfaces
import *
21 from scheduler
import *
22 from collections
import deque
23 from collections
import OrderedDict
28 .. module:: ifupdownmain
29 :synopsis: main module for ifupdown package
31 .. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
36 _crossmark
= u
'\u2717'
37 _success_sym
= '(%s)' %_tickmark
38 _error_sym
= '(%s)' %_crossmark
40 class ifupdownFlags():
50 class ifupdownMain(ifupdownBase
):
51 """ ifupdown2 main class """
57 COMPAT_EXEC_SCRIPTS
= False
58 STATEMANAGER_ENABLE
= True
59 STATEMANAGER_UPDATE
= True
62 # priv flags to mark iface objects
66 scripts_dir
='/etc/network'
67 addon_modules_dir
='/usr/share/ifupdownaddons'
68 addon_modules_configfile
='/var/lib/ifupdownaddons/addons.conf'
70 # iface dictionary in the below format:
71 # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
73 # { 'swp1' : [<iface swp1>, <iface swp2> ..] }
75 # Each ifaceobject corresponds to a configuration block for
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()
82 # iface dictionary representing the curr running state of an iface
83 # in the below format:
84 # {'<ifacename>' : <ifaceobject>}
85 ifaceobjcurrdict
= OrderedDict()
87 # Dictionary representing operation and modules
89 module_ops
= OrderedDict([('pre-up', []),
92 ('query-checkcurr', []),
93 ('query-running', []),
94 ('query-dependency', []),
101 # For old style /etc/network/ bash scripts
102 script_ops
= OrderedDict([('pre-up', []),
109 # Handlers for ops that ifupdown2 owns
110 def run_up(self
, ifaceobj
):
111 # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs).
112 # there is no real interface behind it
113 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
115 if (ifaceobj
.addr_method
and
116 ifaceobj
.addr_method
== 'manual'):
118 if self
._delay
_admin
_state
:
119 self
._delay
_admin
_state
_iface
_queue
.append(ifaceobj
.name
)
121 # If this object is a link slave, ie its link is controlled
122 # by its link master interface, then dont set the link state.
123 # But do allow user to change state of the link if the interface
124 # is already with its link master (hence the master check).
125 if ifaceobj
.link_type
== ifaceLinkType
.LINK_SLAVE
:
127 if not self
.link_exists(ifaceobj
.name
):
129 self
.link_up(ifaceobj
.name
)
131 def run_down(self
, ifaceobj
):
132 # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs)
133 # there is no real interface behind it
134 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
136 if (ifaceobj
.addr_method
and
137 ifaceobj
.addr_method
== 'manual'):
139 if self
._delay
_admin
_state
:
140 self
._delay
_admin
_state
_iface
_queue
.append(ifaceobj
.name
)
142 # If this object is a link slave, ie its link is controlled
143 # by its link master interface, then dont set the link state.
144 # But do allow user to change state of the link if the interface
145 # is already with its link master (hence the master check).
146 if ifaceobj
.link_type
== ifaceLinkType
.LINK_SLAVE
:
148 if not self
.link_exists(ifaceobj
.name
):
150 self
.link_down(ifaceobj
.name
)
152 # ifupdown object interface operation handlers
153 ops_handlers
= OrderedDict([('up', run_up
),
156 def run_sched_ifaceobj_posthook(self
, ifaceobj
, op
):
157 if ((ifaceobj
.priv_flags
& self
.BUILTIN
) or
158 (ifaceobj
.priv_flags
& self
.NOCONFIG
)):
160 if self
.STATEMANAGER_UPDATE
:
161 self
.statemanager
.ifaceobj_sync(ifaceobj
, op
)
163 # ifupdown object interface scheduler pre and posthooks
164 sched_hooks
= {'posthook' : run_sched_ifaceobj_posthook
}
166 def __init__(self
, config
={},
167 force
=False, dryrun
=False, nowait
=False,
168 perfmode
=False, withdepends
=False, njobs
=1,
169 cache
=False, addons_enable
=True, statemanager_enable
=True,
170 interfacesfile
='/etc/network/interfaces',
171 interfacesfileiobuf
=None,
172 interfacesfileformat
='native'):
173 """This member function initializes the ifupdownmain object.
176 config (dict): config dict from /etc/network/ifupdown2/ifupdown2.conf
177 force (bool): force interface configuration
178 dryrun (bool): dryrun interface configuration
179 withdepends (bool): apply interface configuration on all depends
180 interfacesfile (str): interfaces file. default is /etc/network/interfaces
181 interfacesfileformat (str): default is 'native'. Other choices are 'json'
184 AttributeError, KeyError """
186 self
.logger
= logging
.getLogger('ifupdown')
190 self
.PERFMODE
= perfmode
191 self
.WITH_DEPENDS
= withdepends
192 self
.STATEMANAGER_ENABLE
= statemanager_enable
194 self
.interfacesfile
= interfacesfile
195 self
.interfacesfileiobuf
= interfacesfileiobuf
196 self
.interfacesfileformat
= interfacesfileformat
198 self
.logger
.debug(self
.config
)
200 self
.type = ifaceType
.UNKNOWN
202 # Can be used to provide hints for caching
203 self
.CACHE_FLAGS
= 0x0
204 self
._DELETE
_DEPENDENT
_IFACES
_WITH
_NOCONFIG
= False
205 self
.ADDONS_ENABLE
= addons_enable
207 # Copy flags into ifupdownFlags
208 # XXX: before we transition fully to ifupdownFlags
209 ifupdownFlags
.FORCE
= force
210 ifupdownFlags
.DRYRUN
= dryrun
211 ifupdownFlags
.NOWAIT
= nowait
212 ifupdownFlags
.PERFMODE
= perfmode
213 ifupdownFlags
.CACHE
= cache
215 self
.ifaces
= OrderedDict()
217 self
.pp
= pprint
.PrettyPrinter(indent
=4)
218 self
.modules
= OrderedDict({})
219 self
.module_attrs
= {}
221 self
.load_addon_modules(self
.addon_modules_dir
)
222 if self
.COMPAT_EXEC_SCRIPTS
:
223 self
.load_scripts(self
.scripts_dir
)
224 self
.dependency_graph
= OrderedDict({})
226 self
._cache
_no
_repeats
= {}
228 if self
.STATEMANAGER_ENABLE
:
230 self
.statemanager
= stateManager()
231 self
.statemanager
.read_saved_state()
233 # XXX Maybe we should continue by ignoring old state
234 self
.logger
.warning('error reading state (%s)' %str
(e
))
237 self
.STATEMANAGER_UPDATE
= False
238 self
._delay
_admin
_state
= True if self
.config
.get(
239 'delay_admin_state_change', '0') == '1' else False
240 self
._delay
_admin
_state
_iface
_queue
= []
241 if self
._delay
_admin
_state
:
242 self
.logger
.info('\'delay_admin_state_change\' is set. admin ' +
243 'state changes will be delayed till the end.')
245 self
._link
_master
_slave
= True if self
.config
.get(
246 'link_master_slave', '0') == '1' else False
247 if self
._link
_master
_slave
:
248 self
.logger
.info('\'link_master_slave\' is set. slave admin ' +
249 'state changes will be delayed till the ' +
250 'masters admin state change.')
252 def link_master_slave_ignore_error(self
, errorstr
):
253 # If link master slave flag is set,
254 # there may be cases where the lowerdev may not be
255 # up resulting in 'Network is down' error
256 # This can happen if the lowerdev is a LINK_SLAVE
257 # of another interface which is not up yet
258 # example of such a case:
259 # bringing up a vlan on a bond interface and the bond
260 # is a LINK_SLAVE of a bridge (in other words the bond is
261 # part of a bridge) which is not up yet
262 if self
._link
_master
_slave
:
263 if 'Network is down':
267 def get_ifaceobjs(self
, ifacename
):
268 return self
.ifaceobjdict
.get(ifacename
)
270 def get_ifaceobjs_saved(self
, ifacename
):
271 """ Return ifaceobjects from statemanager """
272 if self
.STATEMANAGER_ENABLE
:
273 return self
.statemanager
.get_ifaceobjs(ifacename
)
277 def get_ifaceobj_first(self
, ifacename
):
278 ifaceobjs
= self
.get_ifaceobjs(ifacename
)
283 def get_ifacenames(self
):
284 return self
.ifaceobjdict
.keys()
286 def get_iface_obj_last(self
, ifacename
):
287 return self
.ifaceobjdict
.get(ifacename
)[-1]
290 def must_follow_upperifaces(self
, ifacename
):
292 # XXX: This bleeds the knowledge of iface
293 # types in the infrastructure module.
294 # Cant think of a better fix at the moment.
295 # In future maybe the module can set a flag
296 # to indicate if we should follow upperifaces
298 ifaceobj
= self
.get_ifaceobj_first(ifacename
)
299 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
303 def create_n_save_ifaceobj(self
, ifacename
, priv_flags
=None,
305 """ creates a iface object and adds it to the iface dictionary """
307 ifaceobj
.name
= ifacename
308 ifaceobj
.priv_flags
= priv_flags
310 if not self
._link
_master
_slave
:
311 ifaceobj
.link_type
= ifaceLinkType
.LINK_NA
313 ifaceobj
.inc_refcnt()
314 self
.ifaceobjdict
[ifacename
] = [ifaceobj
]
317 def create_n_save_ifaceobjcurr(self
, ifaceobj
):
318 """ creates a copy of iface object and adds it to the iface
319 dict containing current iface objects
321 ifaceobjcurr
= iface()
322 ifaceobjcurr
.name
= ifaceobj
.name
323 ifaceobjcurr
.type = ifaceobj
.type
324 ifaceobjcurr
.lowerifaces
= ifaceobj
.lowerifaces
325 ifaceobjcurr
.priv_flags
= ifaceobj
.priv_flags
326 ifaceobjcurr
.auto
= ifaceobj
.auto
327 self
.ifaceobjcurrdict
.setdefault(ifaceobj
.name
,
328 []).append(ifaceobjcurr
)
331 def get_ifaceobjcurr(self
, ifacename
, idx
=0):
332 ifaceobjlist
= self
.ifaceobjcurrdict
.get(ifacename
)
338 return ifaceobjlist
[idx
]
340 def get_ifaceobjrunning(self
, ifacename
):
341 return self
.ifaceobjrunningdict
.get(ifacename
)
343 def get_iface_refcnt(self
, ifacename
):
344 """ Return iface ref count """
346 ifaceobjs
= self
.get_ifaceobjs(ifacename
)
354 def is_iface_builtin_byname(self
, ifacename
):
355 """ Returns true if iface name is a builtin interface.
357 A builtin interface is an interface which ifupdown understands.
358 The following are currently considered builtin ifaces:
359 - vlan interfaces in the format <ifacename>.<vlanid>
361 return '.' in ifacename
363 def is_ifaceobj_builtin(self
, ifaceobj
):
364 """ Returns true if iface name is a builtin interface.
366 A builtin interface is an interface which ifupdown understands.
367 The following are currently considered builtin ifaces:
368 - vlan interfaces in the format <ifacename>.<vlanid>
370 return (ifaceobj
.priv_flags
& self
.BUILTIN
)
372 def is_ifaceobj_noconfig(self
, ifaceobj
):
373 """ Returns true if iface object did not have a user defined config.
375 These interfaces appear only when they are dependents of interfaces
376 which have user defined config
378 return (ifaceobj
.priv_flags
& self
.NOCONFIG
)
380 def is_iface_noconfig(self
, ifacename
):
381 """ Returns true if iface has no config """
383 ifaceobj
= self
.get_ifaceobj_first(ifacename
)
384 if not ifaceobj
: return True
385 return self
.is_ifaceobj_noconfig(ifaceobj
)
387 def check_shared_dependents(self
, ifaceobj
, dlist
):
388 """ Check if dlist intersects with any other
389 interface with slave dependents.
390 example: bond and bridges.
391 This function logs such errors """
392 setdlist
= Set(dlist
)
393 for ifacename
, ifacedlist
in self
.dependency_graph
.items():
396 check_depends
= False
397 iobjs
= self
.get_ifaceobjs(ifacename
)
399 if (i
.dependency_type
== ifaceDependencyType
.MASTER_SLAVE
):
402 common
= Set(ifacedlist
).intersection(setdlist
)
404 self
.logger
.error('misconfig..?. iface %s and %s '
405 %(ifaceobj
.name
, ifacename
) +
406 'seem to share dependents/ports %s' %str
(list(common
)))
408 def preprocess_dependency_list(self
, upperifaceobj
, dlist
, ops
):
409 """ We go through the dependency list and
410 delete or add interfaces from the interfaces dict by
411 applying the following rules:
412 if flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
413 we only consider devices whose configuration was
414 specified in the network interfaces file. We delete
415 any interface whose config was not specified except
416 for vlan devices. vlan devices get special treatment.
417 Even if they are not present they are created and added
419 elif flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
420 we create objects for all dependent devices that are not
421 present in the ifacesdict
425 if (upperifaceobj
.dependency_type
==
426 ifaceDependencyType
.MASTER_SLAVE
):
427 self
.check_shared_dependents(upperifaceobj
, dlist
)
430 dilist
= self
.get_ifaceobjs(d
)
433 if self
.is_iface_builtin_byname(d
):
434 ni
= self
.create_n_save_ifaceobj(d
,
435 self
.BUILTIN | self
.NOCONFIG
, True)
436 elif not self
._DELETE
_DEPENDENT
_IFACES
_WITH
_NOCONFIG
:
437 ni
= self
.create_n_save_ifaceobj(d
, self
.NOCONFIG
,
442 ni
.add_to_upperifaces(upperifaceobj
.name
)
443 if upperifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
444 ni
.link_type
= ifaceLinkType
.LINK_SLAVE
448 di
.add_to_upperifaces(upperifaceobj
.name
)
449 if upperifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
450 di
.link_type
= ifaceLinkType
.LINK_SLAVE
454 def query_dependents(self
, ifaceobj
, ops
, ifacenames
, type=None):
455 """ Gets iface dependents by calling into respective modules """
458 # Get dependents for interface by querying respective modules
459 for module
in self
.modules
.values():
461 if ops
[0] == 'query-running':
462 if (not hasattr(module
,
463 'get_dependent_ifacenames_running')):
465 dlist
= module
.get_dependent_ifacenames_running(ifaceobj
)
467 if (not hasattr(module
, 'get_dependent_ifacenames')):
469 dlist
= module
.get_dependent_ifacenames(ifaceobj
,
472 self
.logger
.warn('%s: error getting dependent interfaces (%s)'
473 %(ifaceobj
.name
, str(e
)))
476 if dlist
: ret_dlist
.extend(dlist
)
477 return list(set(ret_dlist
))
480 def populate_dependency_info(self
, ops
, ifacenames
=None):
481 """ recursive function to generate iface dependency info """
484 ifacenames
= self
.ifaceobjdict
.keys()
486 iqueue
= deque(ifacenames
)
489 # Go through all modules and find dependent ifaces
491 ifaceobj
= self
.get_ifaceobj_first(i
)
494 dlist
= ifaceobj
.lowerifaces
496 dlist
= self
.query_dependents(ifaceobj
, ops
, ifacenames
)
500 self
.preprocess_dependency_list(ifaceobj
,
502 ifaceobj
.lowerifaces
= dlist
503 [iqueue
.append(d
) for d
in dlist
]
504 if not self
.dependency_graph
.get(i
):
505 self
.dependency_graph
[i
] = dlist
507 def _check_config_no_repeats(self
, ifaceobj
):
508 """ check if object has an attribute that is
509 restricted to a single object in the system.
510 if yes, warn and return """
511 for k
,v
in self
._cache
_no
_repeats
.items():
512 iv
= ifaceobj
.config
.get(k
)
513 if iv
and iv
[0] == v
:
514 self
.logger
.error('ignoring interface %s. ' %ifaceobj
.name
+
515 'Only one object with attribute ' +
516 '\'%s %s\' allowed.' %(k
, v
))
518 for k
, v
in self
.config
.get('no_repeats', {}).items():
519 iv
= ifaceobj
.config
.get(k
)
520 if iv
and iv
[0] == v
:
521 self
._cache
_no
_repeats
[k
] = v
524 def _save_iface(self
, ifaceobj
):
525 if self
._check
_config
_no
_repeats
(ifaceobj
):
527 if not self
._link
_master
_slave
:
528 ifaceobj
.link_type
= ifaceLinkType
.LINK_NA
529 currentifaceobjlist
= self
.ifaceobjdict
.get(ifaceobj
.name
)
530 if not currentifaceobjlist
:
531 self
.ifaceobjdict
[ifaceobj
.name
]= [ifaceobj
]
533 if ifaceobj
.compare(currentifaceobjlist
[0]):
534 self
.logger
.warn('duplicate interface %s found' %ifaceobj
.name
)
536 if currentifaceobjlist
[0].type == ifaceobj
.type:
537 currentifaceobjlist
[0].flags |
= iface
.HAS_SIBLINGS
538 ifaceobj
.flags |
= iface
.HAS_SIBLINGS
539 self
.ifaceobjdict
[ifaceobj
.name
].append(ifaceobj
)
541 def _iface_configattr_syntax_checker(self
, attrname
, attrval
):
542 for m
, mdict
in self
.module_attrs
.items():
545 attrsdict
= mdict
.get('attrs')
547 if attrsdict
.get(attrname
):
549 except AttributeError:
553 def _ifaceobj_syntax_checker(self
, ifaceobj
):
555 for attrname
, attrvalue
in ifaceobj
.config
.items():
557 for k
, v
in self
.module_attrs
.items():
558 if v
and v
.get('attrs', {}).get(attrname
):
563 self
.logger
.warn('%s: unsupported attribute \'%s\'' \
564 % (ifaceobj
.name
, attrname
))
568 def read_iface_config(self
):
569 """ Reads default network interface config /etc/network/interfaces. """
570 nifaces
= networkInterfaces(self
.interfacesfile
,
571 self
.interfacesfileiobuf
,
572 self
.interfacesfileformat
,
573 template_engine
=self
.config
.get('template_engine'),
574 template_lookuppath
=self
.config
.get('template_lookuppath'))
575 nifaces
.subscribe('iface_found', self
._save
_iface
)
576 nifaces
.subscribe('validateifaceattr',
577 self
._iface
_configattr
_syntax
_checker
)
578 nifaces
.subscribe('validateifaceobj', self
._ifaceobj
_syntax
_checker
)
581 def read_old_iface_config(self
):
582 """ Reads the saved iface config instead of default iface config.
583 And saved iface config is already read by the statemanager """
584 self
.ifaceobjdict
= copy
.deepcopy(self
.statemanager
.ifaceobjdict
)
586 def _load_addon_modules_config(self
):
587 """ Load addon modules config file """
589 with
open(self
.addon_modules_configfile
, 'r') as f
:
590 lines
= f
.readlines()
593 litems
= l
.strip(' \n\t\r').split(',')
594 if not litems
or len(litems
) < 2:
596 operation
= litems
[0]
598 self
.module_ops
[operation
].append(mname
)
600 self
.logger
.warn('error reading line \'%s\'' %(l
, str(e
)))
603 def load_addon_modules(self
, modules_dir
):
604 """ load python modules from modules_dir
606 Default modules_dir is /usr/share/ifupdownmodules
609 self
.logger
.info('loading builtin modules from %s' %modules_dir
)
610 self
._load
_addon
_modules
_config
()
611 if not modules_dir
in sys
.path
:
612 sys
.path
.append(modules_dir
)
614 for op
, mlist
in self
.module_ops
.items():
616 if self
.modules
.get(mname
):
618 mpath
= modules_dir
+ '/' + mname
+ '.py'
619 if os
.path
.exists(mpath
):
621 m
= __import__(mname
)
622 mclass
= getattr(m
, mname
)
625 minstance
= mclass(force
=self
.FORCE
,
628 perfmode
=self
.PERFMODE
,
630 cacheflags
=self
.CACHE_FLAGS
)
631 self
.modules
[mname
] = minstance
633 self
.module_attrs
[mname
] = minstance
.get_modinfo()
639 # Assign all modules to query operations
640 self
.module_ops
['query-checkcurr'] = self
.modules
.keys()
641 self
.module_ops
['query-running'] = self
.modules
.keys()
642 self
.module_ops
['query-dependency'] = self
.modules
.keys()
643 self
.module_ops
['query'] = self
.modules
.keys()
644 self
.module_ops
['query-raw'] = self
.modules
.keys()
647 def _modules_help(self
):
648 """ Prints addon modules supported syntax """
651 for m
, mdict
in self
.module_attrs
.items():
654 print('%s: %s' %(m
, mdict
.get('mhelp')))
655 attrdict
= mdict
.get('attrs')
659 for attrname
, attrvaldict
in attrdict
.items():
660 if attrvaldict
.get('compat', False):
662 print('%s%s' %(indent
, attrname
))
663 print('%shelp: %s' %(indent
+ ' ',
664 attrvaldict
.get('help', '')))
665 print ('%srequired: %s' %(indent
+ ' ',
666 attrvaldict
.get('required', False)))
667 default
= attrvaldict
.get('default')
669 print('%sdefault: %s' %(indent
+ ' ', default
))
671 validrange
= attrvaldict
.get('validrange')
673 print('%svalidrange: %s-%s'
674 %(indent
+ ' ', validrange
[0], validrange
[1]))
676 validvals
= attrvaldict
.get('validvals')
678 print('%svalidvals: %s'
679 %(indent
+ ' ', ','.join(validvals
)))
681 examples
= attrvaldict
.get('example')
685 print '%sexample:' %(indent
+ ' ')
687 print '%s%s' %(indent
+ ' ', e
)
692 def load_scripts(self
, modules_dir
):
693 """ loading user modules from /etc/network/.
695 Note that previously loaded python modules override modules found
696 under /etc/network if any
700 self
.logger
.info('looking for user scripts under %s' %modules_dir
)
701 for op
, mlist
in self
.script_ops
.items():
702 msubdir
= modules_dir
+ '/if-%s.d' %op
703 self
.logger
.info('loading scripts under %s ...' %msubdir
)
705 module_list
= os
.listdir(msubdir
)
706 for module
in module_list
:
707 if self
.modules
.get(module
) is not None:
709 self
.script_ops
[op
].append(
710 msubdir
+ '/' + module
)
715 def _sched_ifaces(self
, ifacenames
, ops
, skipupperifaces
=False,
716 followdependents
=True, sort
=False):
717 self
.logger
.debug('scheduling \'%s\' for %s'
718 %(str(ops
), str(ifacenames
)))
719 self
._pretty
_print
_ordered
_dict
('dependency graph',
720 self
.dependency_graph
)
721 return ifaceScheduler
.sched_ifaces(self
, ifacenames
, ops
,
722 dependency_graph
=self
.dependency_graph
,
723 order
=ifaceSchedulerFlags
.INORDER
725 else ifaceSchedulerFlags
.POSTORDER
,
726 followdependents
=followdependents
,
727 skipupperifaces
=skipupperifaces
,
728 sort
=True if (sort
or self
.IFACE_CLASS
) else False)
730 def _render_ifacename(self
, ifacename
):
732 vlan_match
= re
.match("^([\d]+)-([\d]+)", ifacename
)
734 vlan_groups
= vlan_match
.groups()
735 if vlan_groups
[0] and vlan_groups
[1]:
736 [new_ifacenames
.append('%d' %v
)
737 for v
in range(int(vlan_groups
[0]),
738 int(vlan_groups
[1])+1)]
739 return new_ifacenames
741 def _preprocess_ifacenames(self
, ifacenames
):
742 """ validates interface list for config existance.
744 returns -1 if one or more interface not found. else, returns 0
750 ifaceobjs
= self
.get_ifaceobjs(i
)
752 # if name not available, render interface name and check again
753 rendered_ifacenames
= utils
.expand_iface_range(i
)
754 if rendered_ifacenames
:
755 for ri
in rendered_ifacenames
:
756 ifaceobjs
= self
.get_ifaceobjs(ri
)
758 err_iface
+= ' ' + ri
760 new_ifacenames
.append(ri
)
764 new_ifacenames
.append(i
)
766 raise Exception('cannot find interfaces:%s' %err_iface
)
767 return new_ifacenames
769 def _iface_whitelisted(self
, auto
, allow_classes
, excludepats
, ifacename
):
770 """ Checks if interface is whitelisted depending on set of parameters.
772 interfaces are checked against the allow_classes and auto lists.
776 for e
in excludepats
:
777 if re
.search(e
, ifacename
):
779 ifaceobjs
= self
.get_ifaceobjs(ifacename
)
781 self
.logger
.debug('iface %s' %ifacename
+ ' not found')
783 # We check classes first
787 common
= Set([allow_classes
]).intersection(
799 def _compat_conv_op_to_mode(self
, op
):
800 """ Returns old op name to work with existing scripts """
803 elif op
== 'pre-down':
808 def generate_running_env(self
, ifaceobj
, op
):
809 """ Generates a dictionary with env variables required for
810 an interface. Used to support script execution for interfaces.
814 iface_env
= ifaceobj
.env
818 cenv
.update(iface_env
)
821 cenv
['MODE'] = self
._compat
_conv
_op
_to
_mode
(op
)
824 def _save_state(self
):
825 if not self
.STATEMANAGER_ENABLE
or not self
.STATEMANAGER_UPDATE
:
828 # Update persistant iface states
829 self
.statemanager
.save_state()
831 if self
.logger
.isEnabledFor(logging
.DEBUG
):
832 t
= sys
.exc_info()[2]
833 traceback
.print_tb(t
)
834 self
.logger
.warning('error saving state (%s)' %str
(e
))
836 def set_type(self
, type):
838 self
.type = ifaceType
.IFACE
840 self
.type = ifaceType
.BRIDGE_VLAN
842 self
.type = ifaceType
.UNKNOWN
844 def _process_delay_admin_state_queue(self
, op
):
845 if not self
._delay
_admin
_state
_iface
_queue
:
850 func
= self
.link_down
853 for i
in self
._delay
_admin
_state
_iface
_queue
:
855 if self
.link_exists(i
):
858 self
.logger
.warn(str(e
))
861 def up(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
862 excludepats
=None, printdependency
=None, syntaxcheck
=False,
863 type=None, skipupperifaces
=False):
864 """This brings the interface(s) up
867 ops (list): list of ops to perform on the interface(s).
868 Eg: ['pre-up', 'up', 'post-up'
871 auto (bool): act on interfaces marked auto
872 allow_classes (list): act on interfaces belonging to classes in the list
873 ifacenames (list): act on interfaces specified in this list
874 excludepats (list): list of patterns of interfaces to exclude
875 syntaxcheck (bool): only perform syntax check
881 self
.IFACE_CLASS
= True
882 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
885 self
.WITH_DEPENDS
= True
887 self
.read_iface_config()
891 # If only syntax check was requested, return here
896 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
898 # if iface list not given by user, assume all from config file
899 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
901 # filter interfaces based on auto and allow classes
902 filtered_ifacenames
= [i
for i
in ifacenames
903 if self
._iface
_whitelisted
(auto
, allow_classes
,
905 if not filtered_ifacenames
:
906 raise Exception('no ifaces found matching given allow lists')
909 self
.populate_dependency_info(ops
, filtered_ifacenames
)
910 self
.print_dependency(filtered_ifacenames
, printdependency
)
913 self
.populate_dependency_info(ops
)
916 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
917 skipupperifaces
=skipupperifaces
,
918 followdependents
=True if self
.WITH_DEPENDS
else False)
920 self
._process
_delay
_admin
_state
_queue
('up')
921 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
924 def down(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
925 excludepats
=None, printdependency
=None, usecurrentconfig
=False,
927 """ down an interface """
932 self
.IFACE_CLASS
= True
933 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
936 self
.WITH_DEPENDS
= True
937 # For down we need to look at old state, unless usecurrentconfig
939 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
and
940 self
.statemanager
.ifaceobjdict
):
941 # Since we are using state manager objects,
942 # skip the updating of state manager objects
943 self
.logger
.debug('Looking at old state ..')
944 self
.read_old_iface_config()
946 # If no old state available
948 self
.read_iface_config()
950 raise Exception('error reading iface config (%s)' %str
(e
))
952 # If iface list is given by the caller, always check if iface
955 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
957 raise Exception('%s' %str
(e
) +
958 ' (interface was probably never up ?)')
960 # if iface list not given by user, assume all from config file
961 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
963 # filter interfaces based on auto and allow classes
964 filtered_ifacenames
= [i
for i
in ifacenames
965 if self
._iface
_whitelisted
(auto
, allow_classes
,
967 if not filtered_ifacenames
:
968 raise Exception('no ifaces found matching given allow lists ' +
969 '(or interfaces were probably never up ?)')
972 self
.populate_dependency_info(ops
, filtered_ifacenames
)
973 self
.print_dependency(filtered_ifacenames
, printdependency
)
976 self
.populate_dependency_info(ops
)
979 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
980 followdependents
=True if self
.WITH_DEPENDS
else False)
982 self
._process
_delay
_admin
_state
_queue
('down')
983 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
986 def query(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
987 excludepats
=None, printdependency
=None,
988 format
='native', type=None):
989 """ query an interface """
994 self
.IFACE_CLASS
= True
995 if self
.STATEMANAGER_ENABLE
and ops
[0] == 'query-savedstate':
996 return self
.statemanager
.dump_pretty(ifacenames
)
997 self
.STATEMANAGER_UPDATE
= False
999 self
.logger
.debug('setting flag ALL')
1001 self
.WITH_DEPENDS
= True
1003 if ops
[0] == 'query-syntax':
1004 self
._modules
_help
()
1006 elif ops
[0] == 'query-running':
1007 # create fake devices to all dependents that dont have config
1008 map(lambda i
: self
.create_n_save_ifaceobj(i
, self
.NOCONFIG
),
1012 self
.read_iface_config()
1016 if ifacenames
and ops
[0] != 'query-running':
1017 # If iface list is given, always check if iface is present
1018 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
1020 # if iface list not given by user, assume all from config file
1021 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1023 # filter interfaces based on auto and allow classes
1024 if ops
[0] == 'query-running':
1025 filtered_ifacenames
= ifacenames
1027 filtered_ifacenames
= [i
for i
in ifacenames
1028 if self
._iface
_whitelisted
(auto
, allow_classes
,
1030 if not filtered_ifacenames
:
1031 raise Exception('no ifaces found matching ' +
1032 'given allow lists')
1034 self
.populate_dependency_info(ops
)
1035 if ops
[0] == 'query-dependency' and printdependency
:
1036 self
.print_dependency(filtered_ifacenames
, printdependency
)
1039 if ops
[0] == 'query':
1040 return self
.print_ifaceobjs_pretty(filtered_ifacenames
, format
)
1041 elif ops
[0] == 'query-raw':
1042 return self
.print_ifaceobjs_raw(filtered_ifacenames
)
1044 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
1045 followdependents
=True if self
.WITH_DEPENDS
else False)
1047 if ops
[0] == 'query-checkcurr':
1048 ret
= self
.print_ifaceobjscurr_pretty(filtered_ifacenames
, format
)
1050 # if any of the object has an error, signal that silently
1052 elif ops
[0] == 'query-running':
1053 self
.print_ifaceobjsrunning_pretty(filtered_ifacenames
, format
)
1056 def _reload_currentlyup(self
, upops
, downops
, auto
=True, allow
=None,
1057 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1059 """ reload currently up interfaces """
1061 new_ifaceobjdict
= {}
1063 # Override auto to true
1066 self
.read_iface_config()
1069 if not self
.ifaceobjdict
:
1070 self
.logger
.warn("nothing to reload ..exiting.")
1072 already_up_ifacenames
= []
1073 # generate dependency graph of interfaces
1074 self
.populate_dependency_info(upops
)
1075 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1076 and self
.statemanager
.ifaceobjdict
):
1077 already_up_ifacenames
= self
.statemanager
.ifaceobjdict
.keys()
1079 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1080 filtered_ifacenames
= [i
for i
in ifacenames
1081 if self
._iface
_whitelisted
(auto
, allow_classes
,
1084 # Get already up interfaces that still exist in the interfaces file
1085 already_up_ifacenames_not_present
= Set(
1086 already_up_ifacenames
).difference(ifacenames
)
1087 already_up_ifacenames_still_present
= Set(
1088 already_up_ifacenames
).difference(
1089 already_up_ifacenames_not_present
)
1090 interfaces_to_up
= Set(already_up_ifacenames_still_present
).union(
1091 filtered_ifacenames
)
1093 if (already_up_ifacenames_not_present
and
1094 self
.config
.get('ifreload_currentlyup_down_notpresent') == '1'):
1095 self
.logger
.info('reload: schedule down on interfaces: %s'
1096 %str
(already_up_ifacenames_not_present
))
1098 # Save a copy of new iface objects and dependency_graph
1099 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1100 new_dependency_graph
= dict(self
.dependency_graph
)
1102 # old interface config is read into self.ifaceobjdict
1103 self
.read_old_iface_config()
1105 # reinitialize dependency graph
1106 self
.dependency_graph
= OrderedDict({})
1107 self
.populate_dependency_info(downops
,
1108 already_up_ifacenames_not_present
)
1109 self
._sched
_ifaces
(already_up_ifacenames_not_present
, downops
,
1110 followdependents
=False, sort
=True)
1112 self
.logger
.debug('no interfaces to down ..')
1114 # Now, run 'up' with new config dict
1115 # reset statemanager update flag to default
1118 self
.WITH_DEPENDS
= True
1119 if new_ifaceobjdict
:
1120 self
.ifaceobjdict
= new_ifaceobjdict
1121 self
.dependency_graph
= new_dependency_graph
1123 if not self
.ifaceobjdict
:
1125 self
.logger
.info('reload: scheduling up on interfaces: %s'
1126 %str
(interfaces_to_up
))
1127 self
._sched
_ifaces
(interfaces_to_up
, upops
,
1128 followdependents
=True if self
.WITH_DEPENDS
else False)
1133 def _reload_default(self
, upops
, downops
, auto
=False, allow
=None,
1134 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1136 """ reload interface config """
1138 new_ifaceobjdict
= {}
1141 self
.read_iface_config()
1145 if not self
.ifaceobjdict
:
1146 self
.logger
.warn("nothing to reload ..exiting.")
1148 # generate dependency graph of interfaces
1149 self
.populate_dependency_info(upops
)
1150 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1151 and self
.statemanager
.ifaceobjdict
):
1152 # Save a copy of new iface objects and dependency_graph
1153 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1154 new_dependency_graph
= dict(self
.dependency_graph
)
1156 # if old state is present, read old state and mark op for 'down'
1157 # followed by 'up' aka: reload
1158 # old interface config is read into self.ifaceobjdict
1159 self
.read_old_iface_config()
1162 # oldconfig not available, continue with 'up' with new config
1165 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1166 if op
== 'reload' and ifacenames
:
1167 filtered_ifacenames
= [i
for i
in ifacenames
1168 if self
._iface
_whitelisted
(auto
, allow_classes
,
1171 # if config file had 'ifreload_down_changed' variable
1172 # set, also look for interfaces that changed to down them
1173 down_changed
= int(self
.config
.get('ifreload_down_changed', '1'))
1175 # Generate the interface down list
1176 # Interfaces that go into the down list:
1177 # - interfaces that were present in last config and are not
1178 # present in the new config
1179 # - interfaces that were changed between the last and current
1182 for ifname
in filtered_ifacenames
:
1183 lastifaceobjlist
= self
.ifaceobjdict
.get(ifname
)
1185 # If interface is not present in the new file
1186 # append it to the down list
1187 newifaceobjlist
= new_ifaceobjdict
.get(ifname
)
1188 if not newifaceobjlist
:
1189 ifacedownlist
.append(ifname
)
1191 if len(newifaceobjlist
) != len(lastifaceobjlist
):
1192 ifacedownlist
.append(ifname
)
1194 if not down_changed
:
1197 # If interface has changed between the current file
1198 # and the last installed append it to the down list
1199 # compare object list
1200 for objidx
in range(0, len(lastifaceobjlist
)):
1201 oldobj
= lastifaceobjlist
[objidx
]
1202 newobj
= newifaceobjlist
[objidx
]
1203 if not newobj
.compare(oldobj
):
1204 ifacedownlist
.append(ifname
)
1208 self
.logger
.info('reload: scheduling down on interfaces: %s'
1209 %str
(ifacedownlist
))
1210 # reinitialize dependency graph
1211 self
.dependency_graph
= OrderedDict({})
1212 # Generate dependency info for old config
1213 self
.populate_dependency_info(downops
, ifacedownlist
)
1215 self
._sched
_ifaces
(ifacedownlist
, downops
,
1216 followdependents
=False,
1218 except Exception, e
:
1219 self
.logger
.error(str(e
))
1222 self
._process
_delay
_admin
_state
_queue
('down')
1224 self
.logger
.debug('no interfaces to down ..')
1226 # Now, run 'up' with new config dict
1227 # reset statemanager update flag to default
1228 if not new_ifaceobjdict
:
1233 self
.WITH_DEPENDS
= True
1234 self
.ifaceobjdict
= new_ifaceobjdict
1235 self
.dependency_graph
= new_dependency_graph
1236 ifacenames
= self
.ifaceobjdict
.keys()
1237 filtered_ifacenames
= [i
for i
in ifacenames
1238 if self
._iface
_whitelisted
(auto
, allow_classes
,
1241 self
.logger
.info('reload: scheduling up on interfaces: %s'
1242 %str
(filtered_ifacenames
))
1244 self
._sched
_ifaces
(filtered_ifacenames
, upops
,
1245 followdependents
=True if self
.WITH_DEPENDS
else False)
1246 except Exception, e
:
1247 self
.logger
.error(str(e
))
1250 self
._process
_delay
_admin
_state
_queue
('up')
1255 def reload(self
, *args
, **kargs
):
1256 """ reload interface config """
1257 self
.logger
.debug('reloading interface config ..')
1258 if kargs
.get('currentlyup', False):
1259 self
._reload
_currentlyup
(*args
, **kargs
)
1261 self
._reload
_default
(*args
, **kargs
)
1263 def _pretty_print_ordered_dict(self
, prefix
, argdict
):
1264 outbuf
= prefix
+ ' {\n'
1265 for k
, vlist
in argdict
.items():
1266 outbuf
+= '\t%s : %s\n' %(k
, str(vlist
))
1267 self
.logger
.debug(outbuf
+ '}')
1269 def print_dependency(self
, ifacenames
, format
):
1270 """ prints iface dependency information """
1273 ifacenames
= self
.ifaceobjdict
.keys()
1274 if format
== 'list':
1275 for k
,v
in self
.dependency_graph
.items():
1276 print '%s : %s' %(k
, str(v
))
1277 elif format
== 'dot':
1279 map(lambda i
: indegrees
.update({i
:
1280 self
.get_iface_refcnt(i
)}),
1281 self
.dependency_graph
.keys())
1282 graph
.generate_dots(self
.dependency_graph
, indegrees
)
1284 def print_ifaceobjs_raw(self
, ifacenames
):
1285 """ prints raw lines for ifaces from config file """
1287 for i
in ifacenames
:
1288 for ifaceobj
in self
.get_ifaceobjs(i
):
1289 if (self
.is_ifaceobj_builtin(ifaceobj
) or
1290 not ifaceobj
.is_config_present()):
1292 ifaceobj
.dump_raw(self
.logger
)
1294 if self
.WITH_DEPENDS
and not self
.ALL
:
1295 dlist
= ifaceobj
.lowerifaces
1296 if not dlist
: continue
1297 self
.print_ifaceobjs_raw(dlist
)
1299 def _get_ifaceobjs_pretty(self
, ifacenames
, ifaceobjs
, running
=False):
1300 """ returns iface obj list """
1302 for i
in ifacenames
:
1303 for ifaceobj
in self
.get_ifaceobjs(i
):
1304 if ((not running
and self
.is_ifaceobj_noconfig(ifaceobj
)) or
1305 (running
and not ifaceobj
.is_config_present())):
1307 ifaceobjs
.append(ifaceobj
)
1308 if self
.WITH_DEPENDS
and not self
.ALL
:
1309 dlist
= ifaceobj
.lowerifaces
1310 if not dlist
: continue
1311 self
._get
_ifaceobjs
_pretty
(dlist
, ifaceobjs
, running
)
1313 def print_ifaceobjs_pretty(self
, ifacenames
, format
='native'):
1314 """ pretty prints iface in format given by keyword arg format """
1317 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
)
1318 if not ifaceobjs
: return
1319 if format
== 'json':
1320 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
,
1321 indent
=4, separators
=(',', ': '))
1323 expand
= int(self
.config
.get('ifquery_ifacename_expand_range', '0'))
1325 if not expand
and (i
.flags
& iface
.IFACERANGE_ENTRY
):
1326 # print only the first one
1327 if i
.flags
& iface
.IFACERANGE_START
:
1328 i
.dump_pretty(use_realname
=True)
1332 def _get_ifaceobjscurr_pretty(self
, ifacenames
, ifaceobjs
):
1334 for i
in ifacenames
:
1335 ifaceobjscurr
= self
.get_ifaceobjcurr(i
)
1336 if not ifaceobjscurr
: continue
1337 for ifaceobj
in ifaceobjscurr
:
1338 if (ifaceobj
.status
== ifaceStatus
.NOTFOUND
or
1339 ifaceobj
.status
== ifaceStatus
.ERROR
):
1341 if self
.is_ifaceobj_noconfig(ifaceobj
):
1343 ifaceobjs
.append(ifaceobj
)
1344 if self
.WITH_DEPENDS
and not self
.ALL
:
1345 dlist
= ifaceobj
.lowerifaces
1346 if not dlist
: continue
1347 dret
= self
._get
_ifaceobjscurr
_pretty
(dlist
, ifaceobjs
)
1351 def print_ifaceobjscurr_pretty(self
, ifacenames
, format
='native'):
1352 """ pretty prints current running state of interfaces with status.
1354 returns 1 if any of the interface has an error,
1359 ret
= self
._get
_ifaceobjscurr
_pretty
(ifacenames
, ifaceobjs
)
1360 if not ifaceobjs
: return
1361 if format
== 'json':
1362 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1363 separators
=(',', ': '))
1365 map(lambda i
: i
.dump_pretty(with_status
=True,
1366 successstr
=self
.config
.get('ifquery_check_success_str',
1368 errorstr
=self
.config
.get('ifquery_check_error_str', _error_sym
),
1369 unknownstr
=self
.config
.get('ifquery_check_unknown_str', '')),
1373 def print_ifaceobjsrunning_pretty(self
, ifacenames
, format
='native'):
1374 """ pretty prints iface running state """
1377 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
, running
=True)
1378 if not ifaceobjs
: return
1379 if format
== 'json':
1380 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1381 separators
=(',', ': '))
1383 map(lambda i
: i
.dump_pretty(), ifaceobjs
)
1386 print 'ifupdown main object dump'
1387 print self
.pp
.pprint(self
.modules
)
1388 print self
.pp
.pprint(self
.ifaceobjdict
)
1390 def _dump_ifaceobjs(self
, ifacenames
):
1391 for i
in ifacenames
:
1392 ifaceobjs
= self
.get_ifaceobjs(i
)