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 if upperifaceobj
.link_kind
& \
443 (ifaceLinkKind
.BOND | ifaceLinkKind
.BRIDGE
):
444 ni
.role |
= ifaceRole
.SLAVE
445 ni
.add_to_upperifaces(upperifaceobj
.name
)
446 if upperifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
447 ni
.link_type
= ifaceLinkType
.LINK_SLAVE
451 di
.add_to_upperifaces(upperifaceobj
.name
)
452 if upperifaceobj
.link_kind
& \
453 (ifaceLinkKind
.BOND | ifaceLinkKind
.BRIDGE
):
454 di
.role |
= ifaceRole
.SLAVE
455 if upperifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
456 di
.link_type
= ifaceLinkType
.LINK_SLAVE
460 def query_dependents(self
, ifaceobj
, ops
, ifacenames
, type=None):
461 """ Gets iface dependents by calling into respective modules """
464 # Get dependents for interface by querying respective modules
465 for module
in self
.modules
.values():
467 if ops
[0] == 'query-running':
468 if (not hasattr(module
,
469 'get_dependent_ifacenames_running')):
471 dlist
= module
.get_dependent_ifacenames_running(ifaceobj
)
473 if (not hasattr(module
, 'get_dependent_ifacenames')):
475 dlist
= module
.get_dependent_ifacenames(ifaceobj
,
478 self
.logger
.warn('%s: error getting dependent interfaces (%s)'
479 %(ifaceobj
.name
, str(e
)))
482 if dlist
: ret_dlist
.extend(dlist
)
483 return list(set(ret_dlist
))
486 def populate_dependency_info(self
, ops
, ifacenames
=None):
487 """ recursive function to generate iface dependency info """
490 ifacenames
= self
.ifaceobjdict
.keys()
492 iqueue
= deque(ifacenames
)
495 # Go through all modules and find dependent ifaces
497 ifaceobj
= self
.get_ifaceobj_first(i
)
500 dlist
= ifaceobj
.lowerifaces
502 dlist
= self
.query_dependents(ifaceobj
, ops
, ifacenames
)
506 self
.preprocess_dependency_list(ifaceobj
,
508 ifaceobj
.lowerifaces
= dlist
509 [iqueue
.append(d
) for d
in dlist
]
510 if not self
.dependency_graph
.get(i
):
511 self
.dependency_graph
[i
] = dlist
513 def _check_config_no_repeats(self
, ifaceobj
):
514 """ check if object has an attribute that is
515 restricted to a single object in the system.
516 if yes, warn and return """
517 for k
,v
in self
._cache
_no
_repeats
.items():
518 iv
= ifaceobj
.config
.get(k
)
519 if iv
and iv
[0] == v
:
520 self
.logger
.error('ignoring interface %s. ' %ifaceobj
.name
+
521 'Only one object with attribute ' +
522 '\'%s %s\' allowed.' %(k
, v
))
524 for k
, v
in self
.config
.get('no_repeats', {}).items():
525 iv
= ifaceobj
.config
.get(k
)
526 if iv
and iv
[0] == v
:
527 self
._cache
_no
_repeats
[k
] = v
530 def _save_iface(self
, ifaceobj
):
531 if self
._check
_config
_no
_repeats
(ifaceobj
):
533 if not self
._link
_master
_slave
:
534 ifaceobj
.link_type
= ifaceLinkType
.LINK_NA
535 currentifaceobjlist
= self
.ifaceobjdict
.get(ifaceobj
.name
)
536 if not currentifaceobjlist
:
537 self
.ifaceobjdict
[ifaceobj
.name
]= [ifaceobj
]
539 if ifaceobj
.compare(currentifaceobjlist
[0]):
540 self
.logger
.warn('duplicate interface %s found' %ifaceobj
.name
)
542 if currentifaceobjlist
[0].type == ifaceobj
.type:
543 currentifaceobjlist
[0].flags |
= iface
.HAS_SIBLINGS
544 ifaceobj
.flags |
= iface
.HAS_SIBLINGS
545 self
.ifaceobjdict
[ifaceobj
.name
].append(ifaceobj
)
547 def _iface_configattr_syntax_checker(self
, attrname
, attrval
):
548 for m
, mdict
in self
.module_attrs
.items():
551 attrsdict
= mdict
.get('attrs')
553 if attrsdict
.get(attrname
):
555 except AttributeError:
559 def _ifaceobj_syntax_checker(self
, ifaceobj
):
561 for attrname
, attrvalue
in ifaceobj
.config
.items():
563 for k
, v
in self
.module_attrs
.items():
564 if v
and v
.get('attrs', {}).get(attrname
):
569 self
.logger
.warn('%s: unsupported attribute \'%s\'' \
570 % (ifaceobj
.name
, attrname
))
574 def read_iface_config(self
):
575 """ Reads default network interface config /etc/network/interfaces. """
576 nifaces
= networkInterfaces(self
.interfacesfile
,
577 self
.interfacesfileiobuf
,
578 self
.interfacesfileformat
,
579 template_engine
=self
.config
.get('template_engine'),
580 template_lookuppath
=self
.config
.get('template_lookuppath'))
581 nifaces
.subscribe('iface_found', self
._save
_iface
)
582 nifaces
.subscribe('validateifaceattr',
583 self
._iface
_configattr
_syntax
_checker
)
584 nifaces
.subscribe('validateifaceobj', self
._ifaceobj
_syntax
_checker
)
587 def read_old_iface_config(self
):
588 """ Reads the saved iface config instead of default iface config.
589 And saved iface config is already read by the statemanager """
590 self
.ifaceobjdict
= copy
.deepcopy(self
.statemanager
.ifaceobjdict
)
592 def _load_addon_modules_config(self
):
593 """ Load addon modules config file """
595 with
open(self
.addon_modules_configfile
, 'r') as f
:
596 lines
= f
.readlines()
599 litems
= l
.strip(' \n\t\r').split(',')
600 if not litems
or len(litems
) < 2:
602 operation
= litems
[0]
604 self
.module_ops
[operation
].append(mname
)
606 self
.logger
.warn('error reading line \'%s\'' %(l
, str(e
)))
609 def load_addon_modules(self
, modules_dir
):
610 """ load python modules from modules_dir
612 Default modules_dir is /usr/share/ifupdownmodules
615 self
.logger
.info('loading builtin modules from %s' %modules_dir
)
616 self
._load
_addon
_modules
_config
()
617 if not modules_dir
in sys
.path
:
618 sys
.path
.append(modules_dir
)
620 for op
, mlist
in self
.module_ops
.items():
622 if self
.modules
.get(mname
):
624 mpath
= modules_dir
+ '/' + mname
+ '.py'
625 if os
.path
.exists(mpath
):
627 m
= __import__(mname
)
628 mclass
= getattr(m
, mname
)
631 minstance
= mclass(force
=self
.FORCE
,
634 perfmode
=self
.PERFMODE
,
636 cacheflags
=self
.CACHE_FLAGS
)
637 self
.modules
[mname
] = minstance
639 self
.module_attrs
[mname
] = minstance
.get_modinfo()
645 # Assign all modules to query operations
646 self
.module_ops
['query-checkcurr'] = self
.modules
.keys()
647 self
.module_ops
['query-running'] = self
.modules
.keys()
648 self
.module_ops
['query-dependency'] = self
.modules
.keys()
649 self
.module_ops
['query'] = self
.modules
.keys()
650 self
.module_ops
['query-raw'] = self
.modules
.keys()
653 def _modules_help(self
):
654 """ Prints addon modules supported syntax """
657 for m
, mdict
in self
.module_attrs
.items():
660 print('%s: %s' %(m
, mdict
.get('mhelp')))
661 attrdict
= mdict
.get('attrs')
665 for attrname
, attrvaldict
in attrdict
.items():
666 if attrvaldict
.get('compat', False):
668 print('%s%s' %(indent
, attrname
))
669 print('%shelp: %s' %(indent
+ ' ',
670 attrvaldict
.get('help', '')))
671 print ('%srequired: %s' %(indent
+ ' ',
672 attrvaldict
.get('required', False)))
673 default
= attrvaldict
.get('default')
675 print('%sdefault: %s' %(indent
+ ' ', default
))
677 validrange
= attrvaldict
.get('validrange')
679 print('%svalidrange: %s-%s'
680 %(indent
+ ' ', validrange
[0], validrange
[1]))
682 validvals
= attrvaldict
.get('validvals')
684 print('%svalidvals: %s'
685 %(indent
+ ' ', ','.join(validvals
)))
687 examples
= attrvaldict
.get('example')
691 print '%sexample:' %(indent
+ ' ')
693 print '%s%s' %(indent
+ ' ', e
)
698 def load_scripts(self
, modules_dir
):
699 """ loading user modules from /etc/network/.
701 Note that previously loaded python modules override modules found
702 under /etc/network if any
706 self
.logger
.info('looking for user scripts under %s' %modules_dir
)
707 for op
, mlist
in self
.script_ops
.items():
708 msubdir
= modules_dir
+ '/if-%s.d' %op
709 self
.logger
.info('loading scripts under %s ...' %msubdir
)
711 module_list
= os
.listdir(msubdir
)
712 for module
in module_list
:
713 if self
.modules
.get(module
) is not None:
715 self
.script_ops
[op
].append(
716 msubdir
+ '/' + module
)
721 def _sched_ifaces(self
, ifacenames
, ops
, skipupperifaces
=False,
722 followdependents
=True, sort
=False):
723 self
.logger
.debug('scheduling \'%s\' for %s'
724 %(str(ops
), str(ifacenames
)))
725 self
._pretty
_print
_ordered
_dict
('dependency graph',
726 self
.dependency_graph
)
727 return ifaceScheduler
.sched_ifaces(self
, ifacenames
, ops
,
728 dependency_graph
=self
.dependency_graph
,
729 order
=ifaceSchedulerFlags
.INORDER
731 else ifaceSchedulerFlags
.POSTORDER
,
732 followdependents
=followdependents
,
733 skipupperifaces
=skipupperifaces
,
734 sort
=True if (sort
or self
.IFACE_CLASS
) else False)
736 def _render_ifacename(self
, ifacename
):
738 vlan_match
= re
.match("^([\d]+)-([\d]+)", ifacename
)
740 vlan_groups
= vlan_match
.groups()
741 if vlan_groups
[0] and vlan_groups
[1]:
742 [new_ifacenames
.append('%d' %v
)
743 for v
in range(int(vlan_groups
[0]),
744 int(vlan_groups
[1])+1)]
745 return new_ifacenames
747 def _preprocess_ifacenames(self
, ifacenames
):
748 """ validates interface list for config existance.
750 returns -1 if one or more interface not found. else, returns 0
756 ifaceobjs
= self
.get_ifaceobjs(i
)
758 # if name not available, render interface name and check again
759 rendered_ifacenames
= utils
.expand_iface_range(i
)
760 if rendered_ifacenames
:
761 for ri
in rendered_ifacenames
:
762 ifaceobjs
= self
.get_ifaceobjs(ri
)
764 err_iface
+= ' ' + ri
766 new_ifacenames
.append(ri
)
770 new_ifacenames
.append(i
)
772 raise Exception('cannot find interfaces:%s' %err_iface
)
773 return new_ifacenames
775 def _iface_whitelisted(self
, auto
, allow_classes
, excludepats
, ifacename
):
776 """ Checks if interface is whitelisted depending on set of parameters.
778 interfaces are checked against the allow_classes and auto lists.
782 for e
in excludepats
:
783 if re
.search(e
, ifacename
):
785 ifaceobjs
= self
.get_ifaceobjs(ifacename
)
787 self
.logger
.debug('iface %s' %ifacename
+ ' not found')
789 # We check classes first
793 common
= Set([allow_classes
]).intersection(
805 def _compat_conv_op_to_mode(self
, op
):
806 """ Returns old op name to work with existing scripts """
809 elif op
== 'pre-down':
814 def generate_running_env(self
, ifaceobj
, op
):
815 """ Generates a dictionary with env variables required for
816 an interface. Used to support script execution for interfaces.
820 iface_env
= ifaceobj
.env
824 cenv
.update(iface_env
)
827 cenv
['MODE'] = self
._compat
_conv
_op
_to
_mode
(op
)
830 def _save_state(self
):
831 if not self
.STATEMANAGER_ENABLE
or not self
.STATEMANAGER_UPDATE
:
834 # Update persistant iface states
835 self
.statemanager
.save_state()
837 if self
.logger
.isEnabledFor(logging
.DEBUG
):
838 t
= sys
.exc_info()[2]
839 traceback
.print_tb(t
)
840 self
.logger
.warning('error saving state (%s)' %str
(e
))
842 def set_type(self
, type):
844 self
.type = ifaceType
.IFACE
846 self
.type = ifaceType
.BRIDGE_VLAN
848 self
.type = ifaceType
.UNKNOWN
850 def _process_delay_admin_state_queue(self
, op
):
851 if not self
._delay
_admin
_state
_iface
_queue
:
856 func
= self
.link_down
859 for i
in self
._delay
_admin
_state
_iface
_queue
:
861 if self
.link_exists(i
):
864 self
.logger
.warn(str(e
))
867 def up(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
868 excludepats
=None, printdependency
=None, syntaxcheck
=False,
869 type=None, skipupperifaces
=False):
870 """This brings the interface(s) up
873 ops (list): list of ops to perform on the interface(s).
874 Eg: ['pre-up', 'up', 'post-up'
877 auto (bool): act on interfaces marked auto
878 allow_classes (list): act on interfaces belonging to classes in the list
879 ifacenames (list): act on interfaces specified in this list
880 excludepats (list): list of patterns of interfaces to exclude
881 syntaxcheck (bool): only perform syntax check
887 self
.IFACE_CLASS
= True
888 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
891 self
.WITH_DEPENDS
= True
893 self
.read_iface_config()
897 # If only syntax check was requested, return here
902 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
904 # if iface list not given by user, assume all from config file
905 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
907 # filter interfaces based on auto and allow classes
908 filtered_ifacenames
= [i
for i
in ifacenames
909 if self
._iface
_whitelisted
(auto
, allow_classes
,
911 if not filtered_ifacenames
:
912 raise Exception('no ifaces found matching given allow lists')
915 self
.populate_dependency_info(ops
, filtered_ifacenames
)
916 self
.print_dependency(filtered_ifacenames
, printdependency
)
919 self
.populate_dependency_info(ops
)
922 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
923 skipupperifaces
=skipupperifaces
,
924 followdependents
=True if self
.WITH_DEPENDS
else False)
926 self
._process
_delay
_admin
_state
_queue
('up')
927 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
930 def down(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
931 excludepats
=None, printdependency
=None, usecurrentconfig
=False,
933 """ down an interface """
938 self
.IFACE_CLASS
= True
939 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
942 self
.WITH_DEPENDS
= True
943 # For down we need to look at old state, unless usecurrentconfig
945 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
and
946 self
.statemanager
.ifaceobjdict
):
947 # Since we are using state manager objects,
948 # skip the updating of state manager objects
949 self
.logger
.debug('Looking at old state ..')
950 self
.read_old_iface_config()
952 # If no old state available
954 self
.read_iface_config()
956 raise Exception('error reading iface config (%s)' %str
(e
))
958 # If iface list is given by the caller, always check if iface
961 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
963 raise Exception('%s' %str
(e
) +
964 ' (interface was probably never up ?)')
966 # if iface list not given by user, assume all from config file
967 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
969 # filter interfaces based on auto and allow classes
970 filtered_ifacenames
= [i
for i
in ifacenames
971 if self
._iface
_whitelisted
(auto
, allow_classes
,
973 if not filtered_ifacenames
:
974 raise Exception('no ifaces found matching given allow lists ' +
975 '(or interfaces were probably never up ?)')
978 self
.populate_dependency_info(ops
, filtered_ifacenames
)
979 self
.print_dependency(filtered_ifacenames
, printdependency
)
982 self
.populate_dependency_info(ops
)
985 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
986 followdependents
=True if self
.WITH_DEPENDS
else False)
988 self
._process
_delay
_admin
_state
_queue
('down')
989 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
992 def query(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
993 excludepats
=None, printdependency
=None,
994 format
='native', type=None):
995 """ query an interface """
1000 self
.IFACE_CLASS
= True
1001 if self
.STATEMANAGER_ENABLE
and ops
[0] == 'query-savedstate':
1002 return self
.statemanager
.dump_pretty(ifacenames
)
1003 self
.STATEMANAGER_UPDATE
= False
1005 self
.logger
.debug('setting flag ALL')
1007 self
.WITH_DEPENDS
= True
1009 if ops
[0] == 'query-syntax':
1010 self
._modules
_help
()
1012 elif ops
[0] == 'query-running':
1013 # create fake devices to all dependents that dont have config
1014 map(lambda i
: self
.create_n_save_ifaceobj(i
, self
.NOCONFIG
),
1018 self
.read_iface_config()
1022 if ifacenames
and ops
[0] != 'query-running':
1023 # If iface list is given, always check if iface is present
1024 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
1026 # if iface list not given by user, assume all from config file
1027 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1029 # filter interfaces based on auto and allow classes
1030 if ops
[0] == 'query-running':
1031 filtered_ifacenames
= ifacenames
1033 filtered_ifacenames
= [i
for i
in ifacenames
1034 if self
._iface
_whitelisted
(auto
, allow_classes
,
1036 if not filtered_ifacenames
:
1037 raise Exception('no ifaces found matching ' +
1038 'given allow lists')
1040 self
.populate_dependency_info(ops
)
1041 if ops
[0] == 'query-dependency' and printdependency
:
1042 self
.print_dependency(filtered_ifacenames
, printdependency
)
1045 if ops
[0] == 'query':
1046 return self
.print_ifaceobjs_pretty(filtered_ifacenames
, format
)
1047 elif ops
[0] == 'query-raw':
1048 return self
.print_ifaceobjs_raw(filtered_ifacenames
)
1050 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
1051 followdependents
=True if self
.WITH_DEPENDS
else False)
1053 if ops
[0] == 'query-checkcurr':
1054 ret
= self
.print_ifaceobjscurr_pretty(filtered_ifacenames
, format
)
1056 # if any of the object has an error, signal that silently
1058 elif ops
[0] == 'query-running':
1059 self
.print_ifaceobjsrunning_pretty(filtered_ifacenames
, format
)
1062 def _reload_currentlyup(self
, upops
, downops
, auto
=True, allow
=None,
1063 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1065 """ reload currently up interfaces """
1067 new_ifaceobjdict
= {}
1069 # Override auto to true
1072 self
.read_iface_config()
1075 if not self
.ifaceobjdict
:
1076 self
.logger
.warn("nothing to reload ..exiting.")
1078 already_up_ifacenames
= []
1079 # generate dependency graph of interfaces
1080 self
.populate_dependency_info(upops
)
1081 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1082 and self
.statemanager
.ifaceobjdict
):
1083 already_up_ifacenames
= self
.statemanager
.ifaceobjdict
.keys()
1085 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1086 filtered_ifacenames
= [i
for i
in ifacenames
1087 if self
._iface
_whitelisted
(auto
, allow_classes
,
1090 # Get already up interfaces that still exist in the interfaces file
1091 already_up_ifacenames_not_present
= Set(
1092 already_up_ifacenames
).difference(ifacenames
)
1093 already_up_ifacenames_still_present
= Set(
1094 already_up_ifacenames
).difference(
1095 already_up_ifacenames_not_present
)
1096 interfaces_to_up
= Set(already_up_ifacenames_still_present
).union(
1097 filtered_ifacenames
)
1099 if (already_up_ifacenames_not_present
and
1100 self
.config
.get('ifreload_currentlyup_down_notpresent') == '1'):
1101 self
.logger
.info('reload: schedule down on interfaces: %s'
1102 %str
(already_up_ifacenames_not_present
))
1104 # Save a copy of new iface objects and dependency_graph
1105 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1106 new_dependency_graph
= dict(self
.dependency_graph
)
1108 # old interface config is read into self.ifaceobjdict
1109 self
.read_old_iface_config()
1111 # reinitialize dependency graph
1112 self
.dependency_graph
= OrderedDict({})
1113 self
.populate_dependency_info(downops
,
1114 already_up_ifacenames_not_present
)
1115 self
._sched
_ifaces
(already_up_ifacenames_not_present
, downops
,
1116 followdependents
=False, sort
=True)
1118 self
.logger
.debug('no interfaces to down ..')
1120 # Now, run 'up' with new config dict
1121 # reset statemanager update flag to default
1124 self
.WITH_DEPENDS
= True
1125 if new_ifaceobjdict
:
1126 # and now, ifaceobjdict is back to current config
1127 self
.ifaceobjdict
= new_ifaceobjdict
1128 self
.dependency_graph
= new_dependency_graph
1130 if not self
.ifaceobjdict
:
1132 self
.logger
.info('reload: scheduling up on interfaces: %s'
1133 %str
(interfaces_to_up
))
1134 self
._sched
_ifaces
(interfaces_to_up
, upops
,
1135 followdependents
=True if self
.WITH_DEPENDS
else False)
1140 def _reload_default(self
, upops
, downops
, auto
=False, allow
=None,
1141 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1143 """ reload interface config """
1145 new_ifaceobjdict
= {}
1148 self
.read_iface_config()
1152 if not self
.ifaceobjdict
:
1153 self
.logger
.warn("nothing to reload ..exiting.")
1155 # generate dependency graph of interfaces
1156 self
.populate_dependency_info(upops
)
1157 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1158 and self
.statemanager
.ifaceobjdict
):
1159 # Save a copy of new iface objects and dependency_graph
1160 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1161 new_dependency_graph
= dict(self
.dependency_graph
)
1163 # if old state is present, read old state and mark op for 'down'
1164 # followed by 'up' aka: reload
1165 # old interface config is read into self.ifaceobjdict
1166 self
.read_old_iface_config()
1169 # oldconfig not available, continue with 'up' with new config
1172 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1173 if op
== 'reload' and ifacenames
:
1174 filtered_ifacenames
= [i
for i
in ifacenames
1175 if self
._iface
_whitelisted
(auto
, allow_classes
,
1178 # if config file had 'ifreload_down_changed' variable
1179 # set, also look for interfaces that changed to down them
1180 down_changed
= int(self
.config
.get('ifreload_down_changed', '1'))
1182 # Generate the interface down list
1183 # Interfaces that go into the down list:
1184 # - interfaces that were present in last config and are not
1185 # present in the new config
1186 # - interfaces that were changed between the last and current
1189 for ifname
in filtered_ifacenames
:
1190 lastifaceobjlist
= self
.ifaceobjdict
.get(ifname
)
1192 # If interface is not present in the new file
1193 # append it to the down list
1194 newifaceobjlist
= new_ifaceobjdict
.get(ifname
)
1195 if not newifaceobjlist
:
1196 ifacedownlist
.append(ifname
)
1198 if not down_changed
:
1200 if len(newifaceobjlist
) != len(lastifaceobjlist
):
1201 ifacedownlist
.append(ifname
)
1204 # If interface has changed between the current file
1205 # and the last installed append it to the down list
1206 # compare object list
1207 for objidx
in range(0, len(lastifaceobjlist
)):
1208 oldobj
= lastifaceobjlist
[objidx
]
1209 newobj
= newifaceobjlist
[objidx
]
1210 if not newobj
.compare(oldobj
):
1211 ifacedownlist
.append(ifname
)
1215 self
.logger
.info('reload: scheduling down on interfaces: %s'
1216 %str
(ifacedownlist
))
1217 # reinitialize dependency graph
1218 self
.dependency_graph
= OrderedDict({})
1219 # Generate dependency info for old config
1220 self
.populate_dependency_info(downops
, ifacedownlist
)
1222 self
._sched
_ifaces
(ifacedownlist
, downops
,
1223 followdependents
=False,
1225 except Exception, e
:
1226 self
.logger
.error(str(e
))
1229 self
._process
_delay
_admin
_state
_queue
('down')
1231 self
.logger
.debug('no interfaces to down ..')
1233 # Now, run 'up' with new config dict
1234 # reset statemanager update flag to default
1235 if not new_ifaceobjdict
:
1240 self
.WITH_DEPENDS
= True
1241 # and now, we are back to the current config in ifaceobjdict
1242 self
.ifaceobjdict
= new_ifaceobjdict
1243 self
.dependency_graph
= new_dependency_graph
1244 ifacenames
= self
.ifaceobjdict
.keys()
1245 filtered_ifacenames
= [i
for i
in ifacenames
1246 if self
._iface
_whitelisted
(auto
, allow_classes
,
1249 self
.logger
.info('reload: scheduling up on interfaces: %s'
1250 %str
(filtered_ifacenames
))
1252 self
._sched
_ifaces
(filtered_ifacenames
, upops
,
1253 followdependents
=True if self
.WITH_DEPENDS
else False)
1254 except Exception, e
:
1255 self
.logger
.error(str(e
))
1258 self
._process
_delay
_admin
_state
_queue
('up')
1263 def reload(self
, *args
, **kargs
):
1264 """ reload interface config """
1265 self
.logger
.debug('reloading interface config ..')
1266 if kargs
.get('currentlyup', False):
1267 self
._reload
_currentlyup
(*args
, **kargs
)
1269 self
._reload
_default
(*args
, **kargs
)
1271 def _pretty_print_ordered_dict(self
, prefix
, argdict
):
1272 outbuf
= prefix
+ ' {\n'
1273 for k
, vlist
in argdict
.items():
1274 outbuf
+= '\t%s : %s\n' %(k
, str(vlist
))
1275 self
.logger
.debug(outbuf
+ '}')
1277 def print_dependency(self
, ifacenames
, format
):
1278 """ prints iface dependency information """
1281 ifacenames
= self
.ifaceobjdict
.keys()
1282 if format
== 'list':
1283 for k
,v
in self
.dependency_graph
.items():
1284 print '%s : %s' %(k
, str(v
))
1285 elif format
== 'dot':
1287 map(lambda i
: indegrees
.update({i
:
1288 self
.get_iface_refcnt(i
)}),
1289 self
.dependency_graph
.keys())
1290 graph
.generate_dots(self
.dependency_graph
, indegrees
)
1292 def print_ifaceobjs_raw(self
, ifacenames
):
1293 """ prints raw lines for ifaces from config file """
1295 for i
in ifacenames
:
1296 for ifaceobj
in self
.get_ifaceobjs(i
):
1297 if (self
.is_ifaceobj_builtin(ifaceobj
) or
1298 not ifaceobj
.is_config_present()):
1300 ifaceobj
.dump_raw(self
.logger
)
1302 if self
.WITH_DEPENDS
and not self
.ALL
:
1303 dlist
= ifaceobj
.lowerifaces
1304 if not dlist
: continue
1305 self
.print_ifaceobjs_raw(dlist
)
1307 def _get_ifaceobjs_pretty(self
, ifacenames
, ifaceobjs
, running
=False):
1308 """ returns iface obj list """
1310 for i
in ifacenames
:
1311 for ifaceobj
in self
.get_ifaceobjs(i
):
1312 if ((not running
and self
.is_ifaceobj_noconfig(ifaceobj
)) or
1313 (running
and not ifaceobj
.is_config_present())):
1315 ifaceobjs
.append(ifaceobj
)
1316 if self
.WITH_DEPENDS
and not self
.ALL
:
1317 dlist
= ifaceobj
.lowerifaces
1318 if not dlist
: continue
1319 self
._get
_ifaceobjs
_pretty
(dlist
, ifaceobjs
, running
)
1321 def print_ifaceobjs_pretty(self
, ifacenames
, format
='native'):
1322 """ pretty prints iface in format given by keyword arg format """
1325 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
)
1326 if not ifaceobjs
: return
1327 if format
== 'json':
1328 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
,
1329 indent
=4, separators
=(',', ': '))
1331 expand
= int(self
.config
.get('ifquery_ifacename_expand_range', '0'))
1333 if not expand
and (i
.flags
& iface
.IFACERANGE_ENTRY
):
1334 # print only the first one
1335 if i
.flags
& iface
.IFACERANGE_START
:
1336 i
.dump_pretty(use_realname
=True)
1340 def _get_ifaceobjscurr_pretty(self
, ifacenames
, ifaceobjs
):
1342 for i
in ifacenames
:
1343 ifaceobjscurr
= self
.get_ifaceobjcurr(i
)
1344 if not ifaceobjscurr
: continue
1345 for ifaceobj
in ifaceobjscurr
:
1346 if (ifaceobj
.status
== ifaceStatus
.NOTFOUND
or
1347 ifaceobj
.status
== ifaceStatus
.ERROR
):
1349 if self
.is_ifaceobj_noconfig(ifaceobj
):
1351 ifaceobjs
.append(ifaceobj
)
1352 if self
.WITH_DEPENDS
and not self
.ALL
:
1353 dlist
= ifaceobj
.lowerifaces
1354 if not dlist
: continue
1355 dret
= self
._get
_ifaceobjscurr
_pretty
(dlist
, ifaceobjs
)
1359 def print_ifaceobjscurr_pretty(self
, ifacenames
, format
='native'):
1360 """ pretty prints current running state of interfaces with status.
1362 returns 1 if any of the interface has an error,
1367 ret
= self
._get
_ifaceobjscurr
_pretty
(ifacenames
, ifaceobjs
)
1368 if not ifaceobjs
: return
1369 if format
== 'json':
1370 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1371 separators
=(',', ': '))
1373 map(lambda i
: i
.dump_pretty(with_status
=True,
1374 successstr
=self
.config
.get('ifquery_check_success_str',
1376 errorstr
=self
.config
.get('ifquery_check_error_str', _error_sym
),
1377 unknownstr
=self
.config
.get('ifquery_check_unknown_str', '')),
1381 def print_ifaceobjsrunning_pretty(self
, ifacenames
, format
='native'):
1382 """ pretty prints iface running state """
1385 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
, running
=True)
1386 if not ifaceobjs
: return
1387 if format
== 'json':
1388 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1389 separators
=(',', ': '))
1391 map(lambda i
: i
.dump_pretty(), ifaceobjs
)
1394 print 'ifupdown main object dump'
1395 print self
.pp
.pprint(self
.modules
)
1396 print self
.pp
.pprint(self
.ifaceobjdict
)
1398 def _dump_ifaceobjs(self
, ifacenames
):
1399 for i
in ifacenames
:
1400 ifaceobjs
= self
.get_ifaceobjs(i
)