3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
18 import ifupdown
.statemanager
as statemanager
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
.statemanager_api
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 _set_iface_role_n_kind(self
, ifaceobj
, upperifaceobj
):
409 if (upperifaceobj
.link_kind
& ifaceLinkKind
.BOND
):
410 ifaceobj
.role |
= ifaceRole
.SLAVE
411 ifaceobj
.link_kind |
= ifaceLinkKind
.BOND_SLAVE
412 if (upperifaceobj
.link_kind
& ifaceLinkKind
.BRIDGE
):
413 ifaceobj
.role |
= ifaceRole
.SLAVE
414 ifaceobj
.link_kind |
= ifaceLinkKind
.BRIDGE_PORT
415 if upperifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
416 ifaceobj
.link_type
= ifaceLinkType
.LINK_SLAVE
418 def preprocess_dependency_list(self
, upperifaceobj
, dlist
, ops
):
419 """ We go through the dependency list and
420 delete or add interfaces from the interfaces dict by
421 applying the following rules:
422 if flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
423 we only consider devices whose configuration was
424 specified in the network interfaces file. We delete
425 any interface whose config was not specified except
426 for vlan devices. vlan devices get special treatment.
427 Even if they are not present they are created and added
429 elif flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
430 we create objects for all dependent devices that are not
431 present in the ifacesdict
435 if (upperifaceobj
.dependency_type
==
436 ifaceDependencyType
.MASTER_SLAVE
):
437 self
.check_shared_dependents(upperifaceobj
, dlist
)
440 dilist
= self
.get_ifaceobjs(d
)
443 if self
.is_iface_builtin_byname(d
):
444 ni
= self
.create_n_save_ifaceobj(d
,
445 self
.BUILTIN | self
.NOCONFIG
, True)
446 elif not self
._DELETE
_DEPENDENT
_IFACES
_WITH
_NOCONFIG
:
447 ni
= self
.create_n_save_ifaceobj(d
, self
.NOCONFIG
,
452 self
._set
_iface
_role
_n
_kind
(ni
, upperifaceobj
)
453 ni
.add_to_upperifaces(upperifaceobj
.name
)
457 self
._set
_iface
_role
_n
_kind
(di
, upperifaceobj
)
458 di
.add_to_upperifaces(upperifaceobj
.name
)
462 def query_dependents(self
, ifaceobj
, ops
, ifacenames
, type=None):
463 """ Gets iface dependents by calling into respective modules """
466 # Get dependents for interface by querying respective modules
467 for module
in self
.modules
.values():
469 if ops
[0] == 'query-running':
470 if (not hasattr(module
,
471 'get_dependent_ifacenames_running')):
473 dlist
= module
.get_dependent_ifacenames_running(ifaceobj
)
475 if (not hasattr(module
, 'get_dependent_ifacenames')):
477 dlist
= module
.get_dependent_ifacenames(ifaceobj
,
480 self
.logger
.warn('%s: error getting dependent interfaces (%s)'
481 %(ifaceobj
.name
, str(e
)))
484 if dlist
: ret_dlist
.extend(dlist
)
485 return list(set(ret_dlist
))
488 def populate_dependency_info(self
, ops
, ifacenames
=None):
489 """ recursive function to generate iface dependency info """
492 ifacenames
= self
.ifaceobjdict
.keys()
494 iqueue
= deque(ifacenames
)
497 # Go through all modules and find dependent ifaces
499 ifaceobj
= self
.get_ifaceobj_first(i
)
502 if ifaceobj
.blacklisted
:
504 dlist
= ifaceobj
.lowerifaces
506 dlist
= self
.query_dependents(ifaceobj
, ops
, ifacenames
)
510 self
.preprocess_dependency_list(ifaceobj
,
512 ifaceobj
.lowerifaces
= dlist
513 [iqueue
.append(d
) for d
in dlist
]
514 if not self
.dependency_graph
.get(i
):
515 self
.dependency_graph
[i
] = dlist
517 def _check_config_no_repeats(self
, ifaceobj
):
518 """ check if object has an attribute that is
519 restricted to a single object in the system.
520 if yes, warn and return """
521 for k
,v
in self
._cache
_no
_repeats
.items():
522 iv
= ifaceobj
.config
.get(k
)
523 if iv
and iv
[0] == v
:
524 self
.logger
.error('ignoring interface %s. ' %ifaceobj
.name
+
525 'Only one object with attribute ' +
526 '\'%s %s\' allowed.' %(k
, v
))
528 for k
, v
in self
.config
.get('no_repeats', {}).items():
529 iv
= ifaceobj
.config
.get(k
)
530 if iv
and iv
[0] == v
:
531 self
._cache
_no
_repeats
[k
] = v
534 def _save_iface(self
, ifaceobj
):
535 if self
._check
_config
_no
_repeats
(ifaceobj
):
537 if not self
._link
_master
_slave
:
538 ifaceobj
.link_type
= ifaceLinkType
.LINK_NA
539 currentifaceobjlist
= self
.ifaceobjdict
.get(ifaceobj
.name
)
540 if not currentifaceobjlist
:
541 self
.ifaceobjdict
[ifaceobj
.name
]= [ifaceobj
]
543 if ifaceobj
.compare(currentifaceobjlist
[0]):
544 self
.logger
.warn('duplicate interface %s found' %ifaceobj
.name
)
546 if currentifaceobjlist
[0].type == ifaceobj
.type:
547 currentifaceobjlist
[0].flags |
= iface
.HAS_SIBLINGS
548 ifaceobj
.flags |
= iface
.HAS_SIBLINGS
549 self
.ifaceobjdict
[ifaceobj
.name
].append(ifaceobj
)
551 def _iface_configattr_syntax_checker(self
, attrname
, attrval
):
552 for m
, mdict
in self
.module_attrs
.items():
555 attrsdict
= mdict
.get('attrs')
557 if attrsdict
.get(attrname
):
559 except AttributeError:
563 def _ifaceobj_syntax_checker(self
, ifaceobj
):
565 for attrname
, attrvalue
in ifaceobj
.config
.items():
567 for k
, v
in self
.module_attrs
.items():
568 if v
and v
.get('attrs', {}).get(attrname
):
573 self
.logger
.warn('%s: unsupported attribute \'%s\'' \
574 % (ifaceobj
.name
, attrname
))
578 def read_iface_config(self
):
579 """ Reads default network interface config /etc/network/interfaces. """
580 nifaces
= networkInterfaces(self
.interfacesfile
,
581 self
.interfacesfileiobuf
,
582 self
.interfacesfileformat
,
583 template_engine
=self
.config
.get('template_engine'),
584 template_lookuppath
=self
.config
.get('template_lookuppath'))
585 nifaces
.subscribe('iface_found', self
._save
_iface
)
586 nifaces
.subscribe('validateifaceattr',
587 self
._iface
_configattr
_syntax
_checker
)
588 nifaces
.subscribe('validateifaceobj', self
._ifaceobj
_syntax
_checker
)
591 def read_old_iface_config(self
):
592 """ Reads the saved iface config instead of default iface config.
593 And saved iface config is already read by the statemanager """
594 self
.ifaceobjdict
= copy
.deepcopy(self
.statemanager
.ifaceobjdict
)
596 def _load_addon_modules_config(self
):
597 """ Load addon modules config file """
599 with
open(self
.addon_modules_configfile
, 'r') as f
:
600 lines
= f
.readlines()
603 litems
= l
.strip(' \n\t\r').split(',')
604 if not litems
or len(litems
) < 2:
606 operation
= litems
[0]
608 self
.module_ops
[operation
].append(mname
)
610 self
.logger
.warn('error reading line \'%s\'' %(l
, str(e
)))
613 def load_addon_modules(self
, modules_dir
):
614 """ load python modules from modules_dir
616 Default modules_dir is /usr/share/ifupdownmodules
619 self
.logger
.info('loading builtin modules from %s' %modules_dir
)
620 self
._load
_addon
_modules
_config
()
621 if not modules_dir
in sys
.path
:
622 sys
.path
.append(modules_dir
)
624 for op
, mlist
in self
.module_ops
.items():
626 if self
.modules
.get(mname
):
628 mpath
= modules_dir
+ '/' + mname
+ '.py'
629 if os
.path
.exists(mpath
):
631 m
= __import__(mname
)
632 mclass
= getattr(m
, mname
)
635 minstance
= mclass(force
=self
.FORCE
,
638 perfmode
=self
.PERFMODE
,
640 cacheflags
=self
.CACHE_FLAGS
)
641 self
.modules
[mname
] = minstance
643 self
.module_attrs
[mname
] = minstance
.get_modinfo()
649 # Assign all modules to query operations
650 self
.module_ops
['query-checkcurr'] = self
.modules
.keys()
651 self
.module_ops
['query-running'] = self
.modules
.keys()
652 self
.module_ops
['query-dependency'] = self
.modules
.keys()
653 self
.module_ops
['query'] = self
.modules
.keys()
654 self
.module_ops
['query-raw'] = self
.modules
.keys()
657 def _modules_help(self
):
658 """ Prints addon modules supported syntax """
661 for m
, mdict
in self
.module_attrs
.items():
664 print('%s: %s' %(m
, mdict
.get('mhelp')))
665 attrdict
= mdict
.get('attrs')
669 for attrname
, attrvaldict
in attrdict
.items():
670 if attrvaldict
.get('compat', False):
672 print('%s%s' %(indent
, attrname
))
673 print('%shelp: %s' %(indent
+ ' ',
674 attrvaldict
.get('help', '')))
675 print ('%srequired: %s' %(indent
+ ' ',
676 attrvaldict
.get('required', False)))
677 default
= attrvaldict
.get('default')
679 print('%sdefault: %s' %(indent
+ ' ', default
))
681 validrange
= attrvaldict
.get('validrange')
683 print('%svalidrange: %s-%s'
684 %(indent
+ ' ', validrange
[0], validrange
[1]))
686 validvals
= attrvaldict
.get('validvals')
688 print('%svalidvals: %s'
689 %(indent
+ ' ', ','.join(validvals
)))
691 examples
= attrvaldict
.get('example')
695 print '%sexample:' %(indent
+ ' ')
697 print '%s%s' %(indent
+ ' ', e
)
702 def load_scripts(self
, modules_dir
):
703 """ loading user modules from /etc/network/.
705 Note that previously loaded python modules override modules found
706 under /etc/network if any
710 self
.logger
.info('looking for user scripts under %s' %modules_dir
)
711 for op
, mlist
in self
.script_ops
.items():
712 msubdir
= modules_dir
+ '/if-%s.d' %op
713 self
.logger
.info('loading scripts under %s ...' %msubdir
)
715 module_list
= os
.listdir(msubdir
)
716 for module
in module_list
:
717 if self
.modules
.get(module
) is not None:
719 self
.script_ops
[op
].append(
720 msubdir
+ '/' + module
)
725 def _sched_ifaces(self
, ifacenames
, ops
, skipupperifaces
=False,
726 followdependents
=True, sort
=False):
727 self
.logger
.debug('scheduling \'%s\' for %s'
728 %(str(ops
), str(ifacenames
)))
729 self
._pretty
_print
_ordered
_dict
('dependency graph',
730 self
.dependency_graph
)
731 return ifaceScheduler
.sched_ifaces(self
, ifacenames
, ops
,
732 dependency_graph
=self
.dependency_graph
,
733 order
=ifaceSchedulerFlags
.INORDER
735 else ifaceSchedulerFlags
.POSTORDER
,
736 followdependents
=followdependents
,
737 skipupperifaces
=skipupperifaces
,
738 sort
=True if (sort
or self
.IFACE_CLASS
) else False)
740 def _render_ifacename(self
, ifacename
):
742 vlan_match
= re
.match("^([\d]+)-([\d]+)", ifacename
)
744 vlan_groups
= vlan_match
.groups()
745 if vlan_groups
[0] and vlan_groups
[1]:
746 [new_ifacenames
.append('%d' %v
)
747 for v
in range(int(vlan_groups
[0]),
748 int(vlan_groups
[1])+1)]
749 return new_ifacenames
751 def _preprocess_ifacenames(self
, ifacenames
):
752 """ validates interface list for config existance.
754 returns -1 if one or more interface not found. else, returns 0
760 ifaceobjs
= self
.get_ifaceobjs(i
)
762 # if name not available, render interface name and check again
763 rendered_ifacenames
= utils
.expand_iface_range(i
)
764 if rendered_ifacenames
:
765 for ri
in rendered_ifacenames
:
766 ifaceobjs
= self
.get_ifaceobjs(ri
)
768 err_iface
+= ' ' + ri
770 new_ifacenames
.append(ri
)
774 new_ifacenames
.append(i
)
776 raise Exception('cannot find interfaces:%s' %err_iface
)
777 return new_ifacenames
779 def _iface_whitelisted(self
, auto
, allow_classes
, excludepats
, ifacename
):
780 """ Checks if interface is whitelisted depending on set of parameters.
782 interfaces are checked against the allow_classes and auto lists.
787 # Check if interface matches the exclude patter
789 for e
in excludepats
:
790 if re
.search(e
, ifacename
):
792 ifaceobjs
= self
.get_ifaceobjs(ifacename
)
795 self
.logger
.debug('iface %s' %ifacename
+ ' not found')
797 # If matched exclude pattern, return false
802 # Check if interface belongs to the class
803 # the user is interested in, if not return false
807 common
= Set([allow_classes
]).intersection(
815 # If the user has requested auto class, check if the interface
825 def _compat_conv_op_to_mode(self
, op
):
826 """ Returns old op name to work with existing scripts """
829 elif op
== 'pre-down':
834 def generate_running_env(self
, ifaceobj
, op
):
835 """ Generates a dictionary with env variables required for
836 an interface. Used to support script execution for interfaces.
840 iface_env
= ifaceobj
.env
844 cenv
.update(iface_env
)
847 cenv
['MODE'] = self
._compat
_conv
_op
_to
_mode
(op
)
850 def _save_state(self
):
851 if not self
.STATEMANAGER_ENABLE
or not self
.STATEMANAGER_UPDATE
:
854 # Update persistant iface states
855 self
.statemanager
.save_state()
857 if self
.logger
.isEnabledFor(logging
.DEBUG
):
858 t
= sys
.exc_info()[2]
859 traceback
.print_tb(t
)
860 self
.logger
.warning('error saving state (%s)' %str
(e
))
862 def set_type(self
, type):
864 self
.type = ifaceType
.IFACE
866 self
.type = ifaceType
.BRIDGE_VLAN
868 self
.type = ifaceType
.UNKNOWN
870 def _process_delay_admin_state_queue(self
, op
):
871 if not self
._delay
_admin
_state
_iface
_queue
:
876 func
= self
.link_down
879 for i
in self
._delay
_admin
_state
_iface
_queue
:
881 if self
.link_exists(i
):
884 self
.logger
.warn(str(e
))
887 def up(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
888 excludepats
=None, printdependency
=None, syntaxcheck
=False,
889 type=None, skipupperifaces
=False):
890 """This brings the interface(s) up
893 ops (list): list of ops to perform on the interface(s).
894 Eg: ['pre-up', 'up', 'post-up'
897 auto (bool): act on interfaces marked auto
898 allow_classes (list): act on interfaces belonging to classes in the list
899 ifacenames (list): act on interfaces specified in this list
900 excludepats (list): list of patterns of interfaces to exclude
901 syntaxcheck (bool): only perform syntax check
907 self
.IFACE_CLASS
= True
908 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
911 self
.WITH_DEPENDS
= True
913 self
.read_iface_config()
917 # If only syntax check was requested, return here
922 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
924 # if iface list not given by user, assume all from config file
925 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
927 # filter interfaces based on auto and allow classes
928 filtered_ifacenames
= [i
for i
in ifacenames
929 if self
._iface
_whitelisted
(auto
, allow_classes
,
931 if not filtered_ifacenames
:
932 raise Exception('no ifaces found matching given allow lists')
935 self
.populate_dependency_info(ops
, filtered_ifacenames
)
936 self
.print_dependency(filtered_ifacenames
, printdependency
)
939 self
.populate_dependency_info(ops
)
942 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
943 skipupperifaces
=skipupperifaces
,
944 followdependents
=True if self
.WITH_DEPENDS
else False)
946 self
._process
_delay
_admin
_state
_queue
('up')
947 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
950 def down(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
951 excludepats
=None, printdependency
=None, usecurrentconfig
=False,
953 """ down an interface """
958 self
.IFACE_CLASS
= True
959 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
962 self
.WITH_DEPENDS
= True
963 # For down we need to look at old state, unless usecurrentconfig
965 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
and
966 self
.statemanager
.ifaceobjdict
):
967 # Since we are using state manager objects,
968 # skip the updating of state manager objects
969 self
.logger
.debug('Looking at old state ..')
970 self
.read_old_iface_config()
972 # If no old state available
974 self
.read_iface_config()
976 raise Exception('error reading iface config (%s)' %str
(e
))
978 # If iface list is given by the caller, always check if iface
981 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
983 raise Exception('%s' %str
(e
) +
984 ' (interface was probably never up ?)')
986 # if iface list not given by user, assume all from config file
987 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
989 # filter interfaces based on auto and allow classes
990 filtered_ifacenames
= [i
for i
in ifacenames
991 if self
._iface
_whitelisted
(auto
, allow_classes
,
993 if not filtered_ifacenames
:
994 raise Exception('no ifaces found matching given allow lists ' +
995 '(or interfaces were probably never up ?)')
998 self
.populate_dependency_info(ops
, filtered_ifacenames
)
999 self
.print_dependency(filtered_ifacenames
, printdependency
)
1002 self
.populate_dependency_info(ops
)
1005 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
1006 followdependents
=True if self
.WITH_DEPENDS
else False)
1008 self
._process
_delay
_admin
_state
_queue
('down')
1009 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
1012 def query(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
1013 excludepats
=None, printdependency
=None,
1014 format
='native', type=None):
1015 """ query an interface """
1020 self
.IFACE_CLASS
= True
1021 if self
.STATEMANAGER_ENABLE
and ops
[0] == 'query-savedstate':
1022 return self
.statemanager
.dump_pretty(ifacenames
)
1023 self
.STATEMANAGER_UPDATE
= False
1025 self
.logger
.debug('setting flag ALL')
1027 self
.WITH_DEPENDS
= True
1029 if ops
[0] == 'query-syntax':
1030 self
._modules
_help
()
1032 elif ops
[0] == 'query-running':
1033 # create fake devices to all dependents that dont have config
1034 map(lambda i
: self
.create_n_save_ifaceobj(i
, self
.NOCONFIG
),
1038 self
.read_iface_config()
1042 if ifacenames
and ops
[0] != 'query-running':
1043 # If iface list is given, always check if iface is present
1044 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
1046 # if iface list not given by user, assume all from config file
1047 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1049 # filter interfaces based on auto and allow classes
1050 if ops
[0] == 'query-running':
1051 filtered_ifacenames
= ifacenames
1053 filtered_ifacenames
= [i
for i
in ifacenames
1054 if self
._iface
_whitelisted
(auto
, allow_classes
,
1056 if not filtered_ifacenames
:
1057 raise Exception('no ifaces found matching ' +
1058 'given allow lists')
1060 self
.populate_dependency_info(ops
)
1061 if ops
[0] == 'query-dependency' and printdependency
:
1062 self
.print_dependency(filtered_ifacenames
, printdependency
)
1065 if ops
[0] == 'query':
1066 return self
.print_ifaceobjs_pretty(filtered_ifacenames
, format
)
1067 elif ops
[0] == 'query-raw':
1068 return self
.print_ifaceobjs_raw(filtered_ifacenames
)
1070 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
1071 followdependents
=True if self
.WITH_DEPENDS
else False)
1073 if ops
[0] == 'query-checkcurr':
1074 ret
= self
.print_ifaceobjscurr_pretty(filtered_ifacenames
, format
)
1076 # if any of the object has an error, signal that silently
1078 elif ops
[0] == 'query-running':
1079 self
.print_ifaceobjsrunning_pretty(filtered_ifacenames
, format
)
1082 def _reload_currentlyup(self
, upops
, downops
, auto
=True, allow
=None,
1083 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1085 """ reload currently up interfaces """
1087 new_ifaceobjdict
= {}
1089 # Override auto to true
1092 self
.read_iface_config()
1095 if not self
.ifaceobjdict
:
1096 self
.logger
.warn("nothing to reload ..exiting.")
1098 already_up_ifacenames
= []
1099 # generate dependency graph of interfaces
1100 self
.populate_dependency_info(upops
)
1101 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1102 and self
.statemanager
.ifaceobjdict
):
1103 already_up_ifacenames
= self
.statemanager
.ifaceobjdict
.keys()
1105 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1106 filtered_ifacenames
= [i
for i
in ifacenames
1107 if self
._iface
_whitelisted
(auto
, allow_classes
,
1110 # Get already up interfaces that still exist in the interfaces file
1111 already_up_ifacenames_not_present
= Set(
1112 already_up_ifacenames
).difference(ifacenames
)
1113 already_up_ifacenames_still_present
= Set(
1114 already_up_ifacenames
).difference(
1115 already_up_ifacenames_not_present
)
1116 interfaces_to_up
= Set(already_up_ifacenames_still_present
).union(
1117 filtered_ifacenames
)
1119 if (already_up_ifacenames_not_present
and
1120 self
.config
.get('ifreload_currentlyup_down_notpresent') == '1'):
1121 self
.logger
.info('reload: schedule down on interfaces: %s'
1122 %str
(already_up_ifacenames_not_present
))
1124 # Save a copy of new iface objects and dependency_graph
1125 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1126 new_dependency_graph
= dict(self
.dependency_graph
)
1128 # old interface config is read into self.ifaceobjdict
1129 self
.read_old_iface_config()
1131 # reinitialize dependency graph
1132 self
.dependency_graph
= OrderedDict({})
1133 self
.populate_dependency_info(downops
,
1134 already_up_ifacenames_not_present
)
1135 self
._sched
_ifaces
(already_up_ifacenames_not_present
, downops
,
1136 followdependents
=False, sort
=True)
1138 self
.logger
.debug('no interfaces to down ..')
1140 # Now, run 'up' with new config dict
1141 # reset statemanager update flag to default
1144 self
.WITH_DEPENDS
= True
1145 if new_ifaceobjdict
:
1146 # and now, ifaceobjdict is back to current config
1147 self
.ifaceobjdict
= new_ifaceobjdict
1148 self
.dependency_graph
= new_dependency_graph
1150 if not self
.ifaceobjdict
:
1152 self
.logger
.info('reload: scheduling up on interfaces: %s'
1153 %str
(interfaces_to_up
))
1154 self
._sched
_ifaces
(interfaces_to_up
, upops
,
1155 followdependents
=True if self
.WITH_DEPENDS
else False)
1160 def _reload_default(self
, upops
, downops
, auto
=False, allow
=None,
1161 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1163 """ reload interface config """
1165 new_ifaceobjdict
= {}
1168 self
.read_iface_config()
1172 if not self
.ifaceobjdict
:
1173 self
.logger
.warn("nothing to reload ..exiting.")
1175 # generate dependency graph of interfaces
1176 self
.populate_dependency_info(upops
)
1177 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1178 and self
.statemanager
.ifaceobjdict
):
1179 # Save a copy of new iface objects and dependency_graph
1180 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1181 new_dependency_graph
= dict(self
.dependency_graph
)
1183 # if old state is present, read old state and mark op for 'down'
1184 # followed by 'up' aka: reload
1185 # old interface config is read into self.ifaceobjdict
1186 self
.read_old_iface_config()
1189 # oldconfig not available, continue with 'up' with new config
1192 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1193 if op
== 'reload' and ifacenames
:
1194 filtered_ifacenames
= [i
for i
in ifacenames
1195 if self
._iface
_whitelisted
(auto
, allow_classes
,
1198 # if config file had 'ifreload_down_changed' variable
1199 # set, also look for interfaces that changed to down them
1200 down_changed
= int(self
.config
.get('ifreload_down_changed', '1'))
1202 # Generate the interface down list
1203 # Interfaces that go into the down list:
1204 # - interfaces that were present in last config and are not
1205 # present in the new config
1206 # - interfaces that were changed between the last and current
1209 for ifname
in filtered_ifacenames
:
1210 lastifaceobjlist
= self
.ifaceobjdict
.get(ifname
)
1212 # If interface is not present in the new file
1213 # append it to the down list
1214 newifaceobjlist
= new_ifaceobjdict
.get(ifname
)
1215 if not newifaceobjlist
:
1216 ifacedownlist
.append(ifname
)
1218 if not down_changed
:
1220 if len(newifaceobjlist
) != len(lastifaceobjlist
):
1221 ifacedownlist
.append(ifname
)
1224 # If interface has changed between the current file
1225 # and the last installed append it to the down list
1226 # compare object list
1227 for objidx
in range(0, len(lastifaceobjlist
)):
1228 oldobj
= lastifaceobjlist
[objidx
]
1229 newobj
= newifaceobjlist
[objidx
]
1230 if not newobj
.compare(oldobj
):
1231 ifacedownlist
.append(ifname
)
1235 self
.logger
.info('reload: scheduling down on interfaces: %s'
1236 %str
(ifacedownlist
))
1237 # reinitialize dependency graph
1238 self
.dependency_graph
= OrderedDict({})
1239 # Generate dependency info for old config
1240 self
.populate_dependency_info(downops
, ifacedownlist
)
1242 self
._sched
_ifaces
(ifacedownlist
, downops
,
1243 followdependents
=False,
1245 except Exception, e
:
1246 self
.logger
.error(str(e
))
1249 self
._process
_delay
_admin
_state
_queue
('down')
1251 self
.logger
.debug('no interfaces to down ..')
1253 # Now, run 'up' with new config dict
1254 # reset statemanager update flag to default
1255 if not new_ifaceobjdict
:
1260 self
.WITH_DEPENDS
= True
1261 # and now, we are back to the current config in ifaceobjdict
1262 self
.ifaceobjdict
= new_ifaceobjdict
1263 self
.dependency_graph
= new_dependency_graph
1264 ifacenames
= self
.ifaceobjdict
.keys()
1265 filtered_ifacenames
= [i
for i
in ifacenames
1266 if self
._iface
_whitelisted
(auto
, allow_classes
,
1269 self
.logger
.info('reload: scheduling up on interfaces: %s'
1270 %str
(filtered_ifacenames
))
1272 self
._sched
_ifaces
(filtered_ifacenames
, upops
,
1273 followdependents
=True if self
.WITH_DEPENDS
else False)
1274 except Exception, e
:
1275 self
.logger
.error(str(e
))
1278 self
._process
_delay
_admin
_state
_queue
('up')
1283 def reload(self
, *args
, **kargs
):
1284 """ reload interface config """
1285 self
.logger
.debug('reloading interface config ..')
1286 if kargs
.get('currentlyup', False):
1287 self
._reload
_currentlyup
(*args
, **kargs
)
1289 self
._reload
_default
(*args
, **kargs
)
1291 def _pretty_print_ordered_dict(self
, prefix
, argdict
):
1292 outbuf
= prefix
+ ' {\n'
1293 for k
, vlist
in argdict
.items():
1294 outbuf
+= '\t%s : %s\n' %(k
, str(vlist
))
1295 self
.logger
.debug(outbuf
+ '}')
1297 def print_dependency(self
, ifacenames
, format
):
1298 """ prints iface dependency information """
1301 ifacenames
= self
.ifaceobjdict
.keys()
1302 if format
== 'list':
1303 for k
,v
in self
.dependency_graph
.items():
1304 print '%s : %s' %(k
, str(v
))
1305 elif format
== 'dot':
1307 map(lambda i
: indegrees
.update({i
:
1308 self
.get_iface_refcnt(i
)}),
1309 self
.dependency_graph
.keys())
1310 graph
.generate_dots(self
.dependency_graph
, indegrees
)
1312 def print_ifaceobjs_raw(self
, ifacenames
):
1313 """ prints raw lines for ifaces from config file """
1315 for i
in ifacenames
:
1316 for ifaceobj
in self
.get_ifaceobjs(i
):
1317 if (self
.is_ifaceobj_builtin(ifaceobj
) or
1318 not ifaceobj
.is_config_present()):
1320 ifaceobj
.dump_raw(self
.logger
)
1322 if self
.WITH_DEPENDS
and not self
.ALL
:
1323 dlist
= ifaceobj
.lowerifaces
1324 if not dlist
: continue
1325 self
.print_ifaceobjs_raw(dlist
)
1327 def _get_ifaceobjs_pretty(self
, ifacenames
, ifaceobjs
, running
=False):
1328 """ returns iface obj list """
1330 for i
in ifacenames
:
1331 for ifaceobj
in self
.get_ifaceobjs(i
):
1332 if ((not running
and self
.is_ifaceobj_noconfig(ifaceobj
)) or
1333 (running
and not ifaceobj
.is_config_present())):
1335 ifaceobjs
.append(ifaceobj
)
1336 if self
.WITH_DEPENDS
and not self
.ALL
:
1337 dlist
= ifaceobj
.lowerifaces
1338 if not dlist
: continue
1339 self
._get
_ifaceobjs
_pretty
(dlist
, ifaceobjs
, running
)
1341 def print_ifaceobjs_pretty(self
, ifacenames
, format
='native'):
1342 """ pretty prints iface in format given by keyword arg format """
1345 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
)
1346 if not ifaceobjs
: return
1347 if format
== 'json':
1348 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
,
1349 indent
=4, separators
=(',', ': '))
1351 expand
= int(self
.config
.get('ifquery_ifacename_expand_range', '0'))
1353 if not expand
and (i
.flags
& iface
.IFACERANGE_ENTRY
):
1354 # print only the first one
1355 if i
.flags
& iface
.IFACERANGE_START
:
1356 i
.dump_pretty(use_realname
=True)
1360 def _get_ifaceobjscurr_pretty(self
, ifacenames
, ifaceobjs
):
1362 for i
in ifacenames
:
1363 ifaceobjscurr
= self
.get_ifaceobjcurr(i
)
1364 if not ifaceobjscurr
: continue
1365 for ifaceobj
in ifaceobjscurr
:
1366 if (ifaceobj
.status
== ifaceStatus
.NOTFOUND
or
1367 ifaceobj
.status
== ifaceStatus
.ERROR
):
1369 if self
.is_ifaceobj_noconfig(ifaceobj
):
1371 ifaceobjs
.append(ifaceobj
)
1372 if self
.WITH_DEPENDS
and not self
.ALL
:
1373 dlist
= ifaceobj
.lowerifaces
1374 if not dlist
: continue
1375 dret
= self
._get
_ifaceobjscurr
_pretty
(dlist
, ifaceobjs
)
1379 def print_ifaceobjscurr_pretty(self
, ifacenames
, format
='native'):
1380 """ pretty prints current running state of interfaces with status.
1382 returns 1 if any of the interface has an error,
1387 ret
= self
._get
_ifaceobjscurr
_pretty
(ifacenames
, ifaceobjs
)
1388 if not ifaceobjs
: return
1389 if format
== 'json':
1390 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1391 separators
=(',', ': '))
1393 map(lambda i
: i
.dump_pretty(with_status
=True,
1394 successstr
=self
.config
.get('ifquery_check_success_str',
1396 errorstr
=self
.config
.get('ifquery_check_error_str', _error_sym
),
1397 unknownstr
=self
.config
.get('ifquery_check_unknown_str', '')),
1401 def print_ifaceobjsrunning_pretty(self
, ifacenames
, format
='native'):
1402 """ pretty prints iface running state """
1405 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
, running
=True)
1406 if not ifaceobjs
: return
1407 if format
== 'json':
1408 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1409 separators
=(',', ': '))
1411 map(lambda i
: i
.dump_pretty(), ifaceobjs
)
1414 print 'ifupdown main object dump'
1415 print self
.pp
.pprint(self
.modules
)
1416 print self
.pp
.pprint(self
.ifaceobjdict
)
1418 def _dump_ifaceobjs(self
, ifacenames
):
1419 for i
in ifacenames
:
1420 ifaceobjs
= self
.get_ifaceobjs(i
)