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 preprocess_dependency_list(self
, upperifaceobj
, dlist
, ops
):
388 """ We go through the dependency list and
389 delete or add interfaces from the interfaces dict by
390 applying the following rules:
391 if flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
392 we only consider devices whose configuration was
393 specified in the network interfaces file. We delete
394 any interface whose config was not specified except
395 for vlan devices. vlan devices get special treatment.
396 Even if they are not present they are created and added
398 elif flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
399 we create objects for all dependent devices that are not
400 present in the ifacesdict
405 dilist
= self
.get_ifaceobjs(d
)
408 if self
.is_iface_builtin_byname(d
):
409 ni
= self
.create_n_save_ifaceobj(d
,
410 self
.BUILTIN | self
.NOCONFIG
, True)
411 elif not self
._DELETE
_DEPENDENT
_IFACES
_WITH
_NOCONFIG
:
412 ni
= self
.create_n_save_ifaceobj(d
, self
.NOCONFIG
,
417 ni
.add_to_upperifaces(upperifaceobj
.name
)
418 if upperifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
419 ni
.link_type
= ifaceLinkType
.LINK_SLAVE
423 di
.add_to_upperifaces(upperifaceobj
.name
)
424 if upperifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
425 di
.link_type
= ifaceLinkType
.LINK_SLAVE
429 def query_dependents(self
, ifaceobj
, ops
, ifacenames
, type=None):
430 """ Gets iface dependents by calling into respective modules """
433 # Get dependents for interface by querying respective modules
434 for module
in self
.modules
.values():
436 if ops
[0] == 'query-running':
437 if (not hasattr(module
,
438 'get_dependent_ifacenames_running')):
440 dlist
= module
.get_dependent_ifacenames_running(ifaceobj
)
442 if (not hasattr(module
, 'get_dependent_ifacenames')):
444 dlist
= module
.get_dependent_ifacenames(ifaceobj
,
447 self
.logger
.warn('%s: error getting dependent interfaces (%s)'
448 %(ifaceobj
.name
, str(e
)))
451 if dlist
: ret_dlist
.extend(dlist
)
452 return list(set(ret_dlist
))
454 def populate_dependency_info(self
, ops
, ifacenames
=None):
455 """ recursive function to generate iface dependency info """
458 ifacenames
= self
.ifaceobjdict
.keys()
460 iqueue
= deque(ifacenames
)
463 # Go through all modules and find dependent ifaces
465 ifaceobj
= self
.get_ifaceobj_first(i
)
468 dlist
= ifaceobj
.lowerifaces
470 dlist
= self
.query_dependents(ifaceobj
, ops
, ifacenames
)
474 self
.preprocess_dependency_list(ifaceobj
,
476 ifaceobj
.lowerifaces
= dlist
477 [iqueue
.append(d
) for d
in dlist
]
478 if not self
.dependency_graph
.get(i
):
479 self
.dependency_graph
[i
] = dlist
481 def _check_config_no_repeats(self
, ifaceobj
):
482 """ check if object has an attribute that is
483 restricted to a single object in the system.
484 if yes, warn and return """
485 for k
,v
in self
._cache
_no
_repeats
.items():
486 iv
= ifaceobj
.config
.get(k
)
487 if iv
and iv
[0] == v
:
488 self
.logger
.error('ignoring interface %s. ' %ifaceobj
.name
+
489 'Only one object with attribute ' +
490 '\'%s %s\' allowed.' %(k
, v
))
492 for k
, v
in self
.config
.get('no_repeats', {}).items():
493 iv
= ifaceobj
.config
.get(k
)
494 if iv
and iv
[0] == v
:
495 self
._cache
_no
_repeats
[k
] = v
498 def _save_iface(self
, ifaceobj
):
499 if self
._check
_config
_no
_repeats
(ifaceobj
):
501 if not self
._link
_master
_slave
:
502 ifaceobj
.link_type
= ifaceLinkType
.LINK_NA
503 currentifaceobjlist
= self
.ifaceobjdict
.get(ifaceobj
.name
)
504 if not currentifaceobjlist
:
505 self
.ifaceobjdict
[ifaceobj
.name
]= [ifaceobj
]
507 if ifaceobj
.compare(currentifaceobjlist
[0]):
508 self
.logger
.warn('duplicate interface %s found' %ifaceobj
.name
)
510 if currentifaceobjlist
[0].type == ifaceobj
.type:
511 currentifaceobjlist
[0].flags |
= iface
.HAS_SIBLINGS
512 ifaceobj
.flags |
= iface
.HAS_SIBLINGS
513 self
.ifaceobjdict
[ifaceobj
.name
].append(ifaceobj
)
515 def _iface_configattr_syntax_checker(self
, attrname
, attrval
):
516 for m
, mdict
in self
.module_attrs
.items():
519 attrsdict
= mdict
.get('attrs')
521 if attrsdict
.get(attrname
):
523 except AttributeError:
527 def _ifaceobj_syntax_checker(self
, ifaceobj
):
529 for attrname
, attrvalue
in ifaceobj
.config
.items():
531 for k
, v
in self
.module_attrs
.items():
532 if v
and v
.get('attrs', {}).get(attrname
):
537 self
.logger
.warn('%s: unsupported attribute \'%s\'' \
538 % (ifaceobj
.name
, attrname
))
542 def read_iface_config(self
):
543 """ Reads default network interface config /etc/network/interfaces. """
544 nifaces
= networkInterfaces(self
.interfacesfile
,
545 self
.interfacesfileiobuf
,
546 self
.interfacesfileformat
,
547 template_engine
=self
.config
.get('template_engine'),
548 template_lookuppath
=self
.config
.get('template_lookuppath'))
549 nifaces
.subscribe('iface_found', self
._save
_iface
)
550 nifaces
.subscribe('validateifaceattr',
551 self
._iface
_configattr
_syntax
_checker
)
552 nifaces
.subscribe('validateifaceobj', self
._ifaceobj
_syntax
_checker
)
555 def read_old_iface_config(self
):
556 """ Reads the saved iface config instead of default iface config.
557 And saved iface config is already read by the statemanager """
558 self
.ifaceobjdict
= copy
.deepcopy(self
.statemanager
.ifaceobjdict
)
560 def _load_addon_modules_config(self
):
561 """ Load addon modules config file """
563 with
open(self
.addon_modules_configfile
, 'r') as f
:
564 lines
= f
.readlines()
567 litems
= l
.strip(' \n\t\r').split(',')
568 if not litems
or len(litems
) < 2:
570 operation
= litems
[0]
572 self
.module_ops
[operation
].append(mname
)
574 self
.logger
.warn('error reading line \'%s\'' %(l
, str(e
)))
577 def load_addon_modules(self
, modules_dir
):
578 """ load python modules from modules_dir
580 Default modules_dir is /usr/share/ifupdownmodules
583 self
.logger
.info('loading builtin modules from %s' %modules_dir
)
584 self
._load
_addon
_modules
_config
()
585 if not modules_dir
in sys
.path
:
586 sys
.path
.append(modules_dir
)
588 for op
, mlist
in self
.module_ops
.items():
590 if self
.modules
.get(mname
):
592 mpath
= modules_dir
+ '/' + mname
+ '.py'
593 if os
.path
.exists(mpath
):
595 m
= __import__(mname
)
596 mclass
= getattr(m
, mname
)
599 minstance
= mclass(force
=self
.FORCE
,
602 perfmode
=self
.PERFMODE
,
604 cacheflags
=self
.CACHE_FLAGS
)
605 self
.modules
[mname
] = minstance
607 self
.module_attrs
[mname
] = minstance
.get_modinfo()
613 # Assign all modules to query operations
614 self
.module_ops
['query-checkcurr'] = self
.modules
.keys()
615 self
.module_ops
['query-running'] = self
.modules
.keys()
616 self
.module_ops
['query-dependency'] = self
.modules
.keys()
617 self
.module_ops
['query'] = self
.modules
.keys()
618 self
.module_ops
['query-raw'] = self
.modules
.keys()
621 def _modules_help(self
):
622 """ Prints addon modules supported syntax """
625 for m
, mdict
in self
.module_attrs
.items():
628 print('%s: %s' %(m
, mdict
.get('mhelp')))
629 attrdict
= mdict
.get('attrs')
633 for attrname
, attrvaldict
in attrdict
.items():
634 if attrvaldict
.get('compat', False):
636 print('%s%s' %(indent
, attrname
))
637 print('%shelp: %s' %(indent
+ ' ',
638 attrvaldict
.get('help', '')))
639 print ('%srequired: %s' %(indent
+ ' ',
640 attrvaldict
.get('required', False)))
641 default
= attrvaldict
.get('default')
643 print('%sdefault: %s' %(indent
+ ' ', default
))
645 validrange
= attrvaldict
.get('validrange')
647 print('%svalidrange: %s-%s'
648 %(indent
+ ' ', validrange
[0], validrange
[1]))
650 validvals
= attrvaldict
.get('validvals')
652 print('%svalidvals: %s'
653 %(indent
+ ' ', ','.join(validvals
)))
655 examples
= attrvaldict
.get('example')
659 print '%sexample:' %(indent
+ ' ')
661 print '%s%s' %(indent
+ ' ', e
)
666 def load_scripts(self
, modules_dir
):
667 """ loading user modules from /etc/network/.
669 Note that previously loaded python modules override modules found
670 under /etc/network if any
674 self
.logger
.info('looking for user scripts under %s' %modules_dir
)
675 for op
, mlist
in self
.script_ops
.items():
676 msubdir
= modules_dir
+ '/if-%s.d' %op
677 self
.logger
.info('loading scripts under %s ...' %msubdir
)
679 module_list
= os
.listdir(msubdir
)
680 for module
in module_list
:
681 if self
.modules
.get(module
) is not None:
683 self
.script_ops
[op
].append(
684 msubdir
+ '/' + module
)
689 def _sched_ifaces(self
, ifacenames
, ops
, skipupperifaces
=False,
690 followdependents
=True):
691 self
.logger
.debug('scheduling \'%s\' for %s'
692 %(str(ops
), str(ifacenames
)))
693 self
._pretty
_print
_ordered
_dict
('dependency graph',
694 self
.dependency_graph
)
695 return ifaceScheduler
.sched_ifaces(self
, ifacenames
, ops
,
696 dependency_graph
=self
.dependency_graph
,
697 order
=ifaceSchedulerFlags
.INORDER
699 else ifaceSchedulerFlags
.POSTORDER
,
700 followdependents
=followdependents
,
701 skipupperifaces
=skipupperifaces
)
703 def _render_ifacename(self
, ifacename
):
705 vlan_match
= re
.match("^([\d]+)-([\d]+)", ifacename
)
707 vlan_groups
= vlan_match
.groups()
708 if vlan_groups
[0] and vlan_groups
[1]:
709 [new_ifacenames
.append('%d' %v
)
710 for v
in range(int(vlan_groups
[0]),
711 int(vlan_groups
[1])+1)]
712 return new_ifacenames
714 def _preprocess_ifacenames(self
, ifacenames
):
715 """ validates interface list for config existance.
717 returns -1 if one or more interface not found. else, returns 0
723 ifaceobjs
= self
.get_ifaceobjs(i
)
725 # if name not available, render interface name and check again
726 rendered_ifacenames
= utils
.expand_iface_range(i
)
727 if rendered_ifacenames
:
728 for ri
in rendered_ifacenames
:
729 ifaceobjs
= self
.get_ifaceobjs(ri
)
731 err_iface
+= ' ' + ri
733 new_ifacenames
.append(ri
)
737 new_ifacenames
.append(i
)
739 raise Exception('cannot find interfaces:%s' %err_iface
)
740 return new_ifacenames
742 def _iface_whitelisted(self
, auto
, allow_classes
, excludepats
, ifacename
):
743 """ Checks if interface is whitelisted depending on set of parameters.
745 interfaces are checked against the allow_classes and auto lists.
749 for e
in excludepats
:
750 if re
.search(e
, ifacename
):
752 ifaceobjs
= self
.get_ifaceobjs(ifacename
)
754 self
.logger
.debug('iface %s' %ifacename
+ ' not found')
756 # We check classes first
760 common
= Set([allow_classes
]).intersection(
772 def _compat_conv_op_to_mode(self
, op
):
773 """ Returns old op name to work with existing scripts """
776 elif op
== 'pre-down':
781 def generate_running_env(self
, ifaceobj
, op
):
782 """ Generates a dictionary with env variables required for
783 an interface. Used to support script execution for interfaces.
787 iface_env
= ifaceobj
.env
791 cenv
.update(iface_env
)
794 cenv
['MODE'] = self
._compat
_conv
_op
_to
_mode
(op
)
797 def _save_state(self
):
798 if not self
.STATEMANAGER_ENABLE
or not self
.STATEMANAGER_UPDATE
:
801 # Update persistant iface states
802 self
.statemanager
.save_state()
804 if self
.logger
.isEnabledFor(logging
.DEBUG
):
805 t
= sys
.exc_info()[2]
806 traceback
.print_tb(t
)
807 self
.logger
.warning('error saving state (%s)' %str
(e
))
809 def set_type(self
, type):
811 self
.type = ifaceType
.IFACE
813 self
.type = ifaceType
.BRIDGE_VLAN
815 self
.type = ifaceType
.UNKNOWN
817 def _process_delay_admin_state_queue(self
, op
):
818 if not self
._delay
_admin
_state
_iface
_queue
:
823 func
= self
.link_down
826 for i
in self
._delay
_admin
_state
_iface
_queue
:
828 if self
.link_exists(i
):
831 self
.logger
.warn(str(e
))
834 def up(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
835 excludepats
=None, printdependency
=None, syntaxcheck
=False,
836 type=None, skipupperifaces
=False):
837 """This brings the interface(s) up
840 ops (list): list of ops to perform on the interface(s).
841 Eg: ['pre-up', 'up', 'post-up'
844 auto (bool): act on interfaces marked auto
845 allow_classes (list): act on interfaces belonging to classes in the list
846 ifacenames (list): act on interfaces specified in this list
847 excludepats (list): list of patterns of interfaces to exclude
848 syntaxcheck (bool): only perform syntax check
854 self
.IFACE_CLASS
= True
855 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
858 self
.WITH_DEPENDS
= True
860 self
.read_iface_config()
864 # If only syntax check was requested, return here
869 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
871 # if iface list not given by user, assume all from config file
872 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
874 # filter interfaces based on auto and allow classes
875 filtered_ifacenames
= [i
for i
in ifacenames
876 if self
._iface
_whitelisted
(auto
, allow_classes
,
878 if not filtered_ifacenames
:
879 raise Exception('no ifaces found matching given allow lists')
882 self
.populate_dependency_info(ops
, filtered_ifacenames
)
883 self
.print_dependency(filtered_ifacenames
, printdependency
)
886 self
.populate_dependency_info(ops
)
889 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
890 skipupperifaces
=skipupperifaces
,
891 followdependents
=True if self
.WITH_DEPENDS
else False)
893 self
._process
_delay
_admin
_state
_queue
('up')
894 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
897 def down(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
898 excludepats
=None, printdependency
=None, usecurrentconfig
=False,
900 """ down an interface """
905 self
.IFACE_CLASS
= True
906 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
909 self
.WITH_DEPENDS
= True
910 # For down we need to look at old state, unless usecurrentconfig
912 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
and
913 self
.statemanager
.ifaceobjdict
):
914 # Since we are using state manager objects,
915 # skip the updating of state manager objects
916 self
.logger
.debug('Looking at old state ..')
917 self
.read_old_iface_config()
919 # If no old state available
921 self
.read_iface_config()
923 raise Exception('error reading iface config (%s)' %str
(e
))
925 # If iface list is given by the caller, always check if iface
928 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
930 raise Exception('%s' %str
(e
) +
931 ' (interface was probably never up ?)')
933 # if iface list not given by user, assume all from config file
934 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
936 # filter interfaces based on auto and allow classes
937 filtered_ifacenames
= [i
for i
in ifacenames
938 if self
._iface
_whitelisted
(auto
, allow_classes
,
940 if not filtered_ifacenames
:
941 raise Exception('no ifaces found matching given allow lists ' +
942 '(or interfaces were probably never up ?)')
945 self
.populate_dependency_info(ops
, filtered_ifacenames
)
946 self
.print_dependency(filtered_ifacenames
, printdependency
)
949 self
.populate_dependency_info(ops
)
952 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
953 followdependents
=True if self
.WITH_DEPENDS
else False)
955 self
._process
_delay
_admin
_state
_queue
('down')
956 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
959 def query(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
960 excludepats
=None, printdependency
=None,
961 format
='native', type=None):
962 """ query an interface """
967 self
.IFACE_CLASS
= True
968 if self
.STATEMANAGER_ENABLE
and ops
[0] == 'query-savedstate':
969 return self
.statemanager
.dump_pretty(ifacenames
)
970 self
.STATEMANAGER_UPDATE
= False
972 self
.logger
.debug('setting flag ALL')
974 self
.WITH_DEPENDS
= True
976 if ops
[0] == 'query-syntax':
979 elif ops
[0] == 'query-running':
980 # create fake devices to all dependents that dont have config
981 map(lambda i
: self
.create_n_save_ifaceobj(i
, self
.NOCONFIG
),
985 self
.read_iface_config()
989 if ifacenames
and ops
[0] != 'query-running':
990 # If iface list is given, always check if iface is present
991 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
993 # if iface list not given by user, assume all from config file
994 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
996 # filter interfaces based on auto and allow classes
997 if ops
[0] == 'query-running':
998 filtered_ifacenames
= ifacenames
1000 filtered_ifacenames
= [i
for i
in ifacenames
1001 if self
._iface
_whitelisted
(auto
, allow_classes
,
1003 if not filtered_ifacenames
:
1004 raise Exception('no ifaces found matching ' +
1005 'given allow lists')
1007 self
.populate_dependency_info(ops
)
1008 if ops
[0] == 'query-dependency' and printdependency
:
1009 self
.print_dependency(filtered_ifacenames
, printdependency
)
1012 if ops
[0] == 'query':
1013 return self
.print_ifaceobjs_pretty(filtered_ifacenames
, format
)
1014 elif ops
[0] == 'query-raw':
1015 return self
.print_ifaceobjs_raw(filtered_ifacenames
)
1017 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
1018 followdependents
=True if self
.WITH_DEPENDS
else False)
1020 if ops
[0] == 'query-checkcurr':
1021 ret
= self
.print_ifaceobjscurr_pretty(filtered_ifacenames
, format
)
1023 # if any of the object has an error, signal that silently
1025 elif ops
[0] == 'query-running':
1026 self
.print_ifaceobjsrunning_pretty(filtered_ifacenames
, format
)
1029 def _reload_currentlyup(self
, upops
, downops
, auto
=True, allow
=None,
1030 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1032 """ reload currently up interfaces """
1034 new_ifaceobjdict
= {}
1036 # Override auto to true
1040 self
.WITH_DEPENDS
= True
1042 self
.read_iface_config()
1045 if not self
.ifaceobjdict
:
1046 self
.logger
.warn("nothing to reload ..exiting.")
1048 already_up_ifacenames
= []
1049 # generate dependency graph of interfaces
1050 self
.populate_dependency_info(upops
)
1051 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1052 and self
.statemanager
.ifaceobjdict
):
1053 already_up_ifacenames
= self
.statemanager
.ifaceobjdict
.keys()
1055 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1056 filtered_ifacenames
= [i
for i
in ifacenames
1057 if self
._iface
_whitelisted
(auto
, allow_classes
,
1060 # Get already up interfaces that still exist in the interfaces file
1061 already_up_ifacenames_not_present
= Set(
1062 already_up_ifacenames
).difference(ifacenames
)
1063 already_up_ifacenames_still_present
= Set(
1064 already_up_ifacenames
).difference(
1065 already_up_ifacenames_not_present
)
1066 interfaces_to_up
= Set(already_up_ifacenames_still_present
).union(
1067 filtered_ifacenames
)
1069 if (already_up_ifacenames_not_present
and
1070 self
.config
.get('ifreload_currentlyup_down_notpresent') == '1'):
1071 self
.logger
.info('reload: schedule down on interfaces: %s'
1072 %str
(already_up_ifacenames_not_present
))
1074 # Save a copy of new iface objects and dependency_graph
1075 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1076 new_dependency_graph
= dict(self
.dependency_graph
)
1078 # old interface config is read into self.ifaceobjdict
1079 self
.read_old_iface_config()
1081 # reinitialize dependency graph
1082 self
.dependency_graph
= OrderedDict({})
1083 self
.populate_dependency_info(downops
,
1084 already_up_ifacenames_not_present
)
1085 self
._sched
_ifaces
(already_up_ifacenames_not_present
, downops
,
1086 followdependents
=True if self
.WITH_DEPENDS
else False)
1088 self
.logger
.debug('no interfaces to down ..')
1090 # Now, run 'up' with new config dict
1091 # reset statemanager update flag to default
1092 if new_ifaceobjdict
:
1093 self
.ifaceobjdict
= new_ifaceobjdict
1094 self
.dependency_graph
= new_dependency_graph
1096 if not self
.ifaceobjdict
:
1098 self
.logger
.info('reload: scheduling up on interfaces: %s'
1099 %str
(interfaces_to_up
))
1100 self
._sched
_ifaces
(interfaces_to_up
, upops
,
1101 followdependents
=True if self
.WITH_DEPENDS
else False)
1106 def _reload_default(self
, upops
, downops
, auto
=False, allow
=None,
1107 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1109 """ reload interface config """
1111 new_ifaceobjdict
= {}
1115 self
.WITH_DEPENDS
= True
1117 self
.read_iface_config()
1121 if not self
.ifaceobjdict
:
1122 self
.logger
.warn("nothing to reload ..exiting.")
1124 # generate dependency graph of interfaces
1125 self
.populate_dependency_info(upops
)
1126 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1127 and self
.statemanager
.ifaceobjdict
):
1128 # Save a copy of new iface objects and dependency_graph
1129 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1130 new_dependency_graph
= dict(self
.dependency_graph
)
1132 # if old state is present, read old state and mark op for 'down'
1133 # followed by 'up' aka: reload
1134 # old interface config is read into self.ifaceobjdict
1135 self
.read_old_iface_config()
1138 # oldconfig not available, continue with 'up' with new config
1141 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1142 if op
== 'reload' and ifacenames
:
1143 filtered_ifacenames
= [i
for i
in ifacenames
1144 if self
._iface
_whitelisted
(auto
, allow_classes
,
1146 # Generate the interface down list
1147 # Interfaces that go into the down list:
1148 # - interfaces that were present in last config and are not
1149 # present in the new config
1150 # - interfaces that were changed between the last and current
1154 for ifname
in filtered_ifacenames
:
1155 lastifaceobjlist
= self
.ifaceobjdict
.get(ifname
)
1157 # If interface is not present in the new file
1158 # append it to the down list
1159 newifaceobjlist
= new_ifaceobjdict
.get(ifname
)
1160 if not newifaceobjlist
:
1161 ifacedownlist
.append(ifname
)
1163 # If interface has changed between the current file
1164 # and the last installed append it to the down list
1165 if len(newifaceobjlist
) != len(lastifaceobjlist
):
1166 ifacedownlist
.append(ifname
)
1168 # compare object list
1169 for objidx
in range(0, len(lastifaceobjlist
)):
1170 oldobj
= lastifaceobjlist
[objidx
]
1171 newobj
= newifaceobjlist
[objidx
]
1172 if not newobj
.compare(oldobj
):
1173 ifacedownlist
.append(ifname
)
1177 self
.logger
.info('reload: scheduling down on interfaces: %s'
1178 %str
(ifacedownlist
))
1179 # reinitialize dependency graph
1180 self
.dependency_graph
= OrderedDict({})
1181 # Generate dependency info for old config
1182 self
.populate_dependency_info(downops
, ifacedownlist
)
1184 self
._sched
_ifaces
(ifacedownlist
, downops
,
1185 followdependents
=False)
1186 except Exception, e
:
1187 self
.logger
.error(str(e
))
1190 self
._process
_delay
_admin
_state
_queue
('down')
1192 self
.logger
.debug('no interfaces to down ..')
1194 # Now, run 'up' with new config dict
1195 # reset statemanager update flag to default
1196 if not new_ifaceobjdict
:
1198 self
.ifaceobjdict
= new_ifaceobjdict
1199 self
.dependency_graph
= new_dependency_graph
1200 ifacenames
= self
.ifaceobjdict
.keys()
1201 filtered_ifacenames
= [i
for i
in ifacenames
1202 if self
._iface
_whitelisted
(auto
, allow_classes
,
1205 self
.logger
.info('reload: scheduling up on interfaces: %s'
1206 %str
(filtered_ifacenames
))
1208 self
._sched
_ifaces
(filtered_ifacenames
, upops
,
1209 followdependents
=True if self
.WITH_DEPENDS
else False)
1210 except Exception, e
:
1211 self
.logger
.error(str(e
))
1214 self
._process
_delay
_admin
_state
_queue
('up')
1219 def reload(self
, *args
, **kargs
):
1220 """ reload interface config """
1221 self
.logger
.debug('reloading interface config ..')
1222 if kargs
.get('currentlyup', False):
1223 self
._reload
_currentlyup
(*args
, **kargs
)
1225 self
._reload
_default
(*args
, **kargs
)
1227 def _pretty_print_ordered_dict(self
, prefix
, argdict
):
1228 outbuf
= prefix
+ ' {\n'
1229 for k
, vlist
in argdict
.items():
1230 outbuf
+= '\t%s : %s\n' %(k
, str(vlist
))
1231 self
.logger
.debug(outbuf
+ '}')
1233 def print_dependency(self
, ifacenames
, format
):
1234 """ prints iface dependency information """
1237 ifacenames
= self
.ifaceobjdict
.keys()
1238 if format
== 'list':
1239 for k
,v
in self
.dependency_graph
.items():
1240 print '%s : %s' %(k
, str(v
))
1241 elif format
== 'dot':
1243 map(lambda i
: indegrees
.update({i
:
1244 self
.get_iface_refcnt(i
)}),
1245 self
.dependency_graph
.keys())
1246 graph
.generate_dots(self
.dependency_graph
, indegrees
)
1248 def print_ifaceobjs_raw(self
, ifacenames
):
1249 """ prints raw lines for ifaces from config file """
1251 for i
in ifacenames
:
1252 for ifaceobj
in self
.get_ifaceobjs(i
):
1253 if (self
.is_ifaceobj_builtin(ifaceobj
) or
1254 not ifaceobj
.is_config_present()):
1256 ifaceobj
.dump_raw(self
.logger
)
1258 if self
.WITH_DEPENDS
and not self
.ALL
:
1259 dlist
= ifaceobj
.lowerifaces
1260 if not dlist
: continue
1261 self
.print_ifaceobjs_raw(dlist
)
1263 def _get_ifaceobjs_pretty(self
, ifacenames
, ifaceobjs
, running
=False):
1264 """ returns iface obj list """
1266 for i
in ifacenames
:
1267 for ifaceobj
in self
.get_ifaceobjs(i
):
1268 if ((not running
and self
.is_ifaceobj_noconfig(ifaceobj
)) or
1269 (running
and not ifaceobj
.is_config_present())):
1271 ifaceobjs
.append(ifaceobj
)
1272 if self
.WITH_DEPENDS
and not self
.ALL
:
1273 dlist
= ifaceobj
.lowerifaces
1274 if not dlist
: continue
1275 self
._get
_ifaceobjs
_pretty
(dlist
, ifaceobjs
, running
)
1277 def print_ifaceobjs_pretty(self
, ifacenames
, format
='native'):
1278 """ pretty prints iface in format given by keyword arg format """
1281 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
)
1282 if not ifaceobjs
: return
1283 if format
== 'json':
1284 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
,
1285 indent
=4, separators
=(',', ': '))
1287 expand
= int(self
.config
.get('ifquery_ifacename_expand_range', '0'))
1289 if not expand
and (i
.flags
& iface
.IFACERANGE_ENTRY
):
1290 # print only the first one
1291 if i
.flags
& iface
.IFACERANGE_START
:
1292 i
.dump_pretty(use_realname
=True)
1296 def _get_ifaceobjscurr_pretty(self
, ifacenames
, ifaceobjs
):
1298 for i
in ifacenames
:
1299 ifaceobjscurr
= self
.get_ifaceobjcurr(i
)
1300 if not ifaceobjscurr
: continue
1301 for ifaceobj
in ifaceobjscurr
:
1302 if (ifaceobj
.status
== ifaceStatus
.NOTFOUND
or
1303 ifaceobj
.status
== ifaceStatus
.ERROR
):
1305 if self
.is_ifaceobj_noconfig(ifaceobj
):
1307 ifaceobjs
.append(ifaceobj
)
1308 if self
.WITH_DEPENDS
and not self
.ALL
:
1309 dlist
= ifaceobj
.lowerifaces
1310 if not dlist
: continue
1311 dret
= self
._get
_ifaceobjscurr
_pretty
(dlist
, ifaceobjs
)
1315 def print_ifaceobjscurr_pretty(self
, ifacenames
, format
='native'):
1316 """ pretty prints current running state of interfaces with status.
1318 returns 1 if any of the interface has an error,
1323 ret
= self
._get
_ifaceobjscurr
_pretty
(ifacenames
, ifaceobjs
)
1324 if not ifaceobjs
: return
1325 if format
== 'json':
1326 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1327 separators
=(',', ': '))
1329 map(lambda i
: i
.dump_pretty(with_status
=True,
1330 successstr
=self
.config
.get('ifquery_check_success_str',
1332 errorstr
=self
.config
.get('ifquery_check_error_str', _error_sym
),
1333 unknownstr
=self
.config
.get('ifquery_check_unknown_str', '')),
1337 def print_ifaceobjsrunning_pretty(self
, ifacenames
, format
='native'):
1338 """ pretty prints iface running state """
1341 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
, running
=True)
1342 if not ifaceobjs
: return
1343 if format
== 'json':
1344 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1345 separators
=(',', ': '))
1347 map(lambda i
: i
.dump_pretty(), ifaceobjs
)
1350 print 'ifupdown main object dump'
1351 print self
.pp
.pprint(self
.modules
)
1352 print self
.pp
.pprint(self
.ifaceobjdict
)
1354 def _dump_ifaceobjs(self
, ifacenames
):
1355 for i
in ifacenames
:
1356 ifaceobjs
= self
.get_ifaceobjs(i
)