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 if ifaceobj
.blacklisted
:
502 dlist
= ifaceobj
.lowerifaces
504 dlist
= self
.query_dependents(ifaceobj
, ops
, ifacenames
)
508 self
.preprocess_dependency_list(ifaceobj
,
510 ifaceobj
.lowerifaces
= dlist
511 [iqueue
.append(d
) for d
in dlist
]
512 if not self
.dependency_graph
.get(i
):
513 self
.dependency_graph
[i
] = dlist
515 def _check_config_no_repeats(self
, ifaceobj
):
516 """ check if object has an attribute that is
517 restricted to a single object in the system.
518 if yes, warn and return """
519 for k
,v
in self
._cache
_no
_repeats
.items():
520 iv
= ifaceobj
.config
.get(k
)
521 if iv
and iv
[0] == v
:
522 self
.logger
.error('ignoring interface %s. ' %ifaceobj
.name
+
523 'Only one object with attribute ' +
524 '\'%s %s\' allowed.' %(k
, v
))
526 for k
, v
in self
.config
.get('no_repeats', {}).items():
527 iv
= ifaceobj
.config
.get(k
)
528 if iv
and iv
[0] == v
:
529 self
._cache
_no
_repeats
[k
] = v
532 def _save_iface(self
, ifaceobj
):
533 if self
._check
_config
_no
_repeats
(ifaceobj
):
535 if not self
._link
_master
_slave
:
536 ifaceobj
.link_type
= ifaceLinkType
.LINK_NA
537 currentifaceobjlist
= self
.ifaceobjdict
.get(ifaceobj
.name
)
538 if not currentifaceobjlist
:
539 self
.ifaceobjdict
[ifaceobj
.name
]= [ifaceobj
]
541 if ifaceobj
.compare(currentifaceobjlist
[0]):
542 self
.logger
.warn('duplicate interface %s found' %ifaceobj
.name
)
544 if currentifaceobjlist
[0].type == ifaceobj
.type:
545 currentifaceobjlist
[0].flags |
= iface
.HAS_SIBLINGS
546 ifaceobj
.flags |
= iface
.HAS_SIBLINGS
547 self
.ifaceobjdict
[ifaceobj
.name
].append(ifaceobj
)
549 def _iface_configattr_syntax_checker(self
, attrname
, attrval
):
550 for m
, mdict
in self
.module_attrs
.items():
553 attrsdict
= mdict
.get('attrs')
555 if attrsdict
.get(attrname
):
557 except AttributeError:
561 def _ifaceobj_syntax_checker(self
, ifaceobj
):
563 for attrname
, attrvalue
in ifaceobj
.config
.items():
565 for k
, v
in self
.module_attrs
.items():
566 if v
and v
.get('attrs', {}).get(attrname
):
571 self
.logger
.warn('%s: unsupported attribute \'%s\'' \
572 % (ifaceobj
.name
, attrname
))
576 def read_iface_config(self
):
577 """ Reads default network interface config /etc/network/interfaces. """
578 nifaces
= networkInterfaces(self
.interfacesfile
,
579 self
.interfacesfileiobuf
,
580 self
.interfacesfileformat
,
581 template_engine
=self
.config
.get('template_engine'),
582 template_lookuppath
=self
.config
.get('template_lookuppath'))
583 nifaces
.subscribe('iface_found', self
._save
_iface
)
584 nifaces
.subscribe('validateifaceattr',
585 self
._iface
_configattr
_syntax
_checker
)
586 nifaces
.subscribe('validateifaceobj', self
._ifaceobj
_syntax
_checker
)
589 def read_old_iface_config(self
):
590 """ Reads the saved iface config instead of default iface config.
591 And saved iface config is already read by the statemanager """
592 self
.ifaceobjdict
= copy
.deepcopy(self
.statemanager
.ifaceobjdict
)
594 def _load_addon_modules_config(self
):
595 """ Load addon modules config file """
597 with
open(self
.addon_modules_configfile
, 'r') as f
:
598 lines
= f
.readlines()
601 litems
= l
.strip(' \n\t\r').split(',')
602 if not litems
or len(litems
) < 2:
604 operation
= litems
[0]
606 self
.module_ops
[operation
].append(mname
)
608 self
.logger
.warn('error reading line \'%s\'' %(l
, str(e
)))
611 def load_addon_modules(self
, modules_dir
):
612 """ load python modules from modules_dir
614 Default modules_dir is /usr/share/ifupdownmodules
617 self
.logger
.info('loading builtin modules from %s' %modules_dir
)
618 self
._load
_addon
_modules
_config
()
619 if not modules_dir
in sys
.path
:
620 sys
.path
.append(modules_dir
)
622 for op
, mlist
in self
.module_ops
.items():
624 if self
.modules
.get(mname
):
626 mpath
= modules_dir
+ '/' + mname
+ '.py'
627 if os
.path
.exists(mpath
):
629 m
= __import__(mname
)
630 mclass
= getattr(m
, mname
)
633 minstance
= mclass(force
=self
.FORCE
,
636 perfmode
=self
.PERFMODE
,
638 cacheflags
=self
.CACHE_FLAGS
)
639 self
.modules
[mname
] = minstance
641 self
.module_attrs
[mname
] = minstance
.get_modinfo()
647 # Assign all modules to query operations
648 self
.module_ops
['query-checkcurr'] = self
.modules
.keys()
649 self
.module_ops
['query-running'] = self
.modules
.keys()
650 self
.module_ops
['query-dependency'] = self
.modules
.keys()
651 self
.module_ops
['query'] = self
.modules
.keys()
652 self
.module_ops
['query-raw'] = self
.modules
.keys()
655 def _modules_help(self
):
656 """ Prints addon modules supported syntax """
659 for m
, mdict
in self
.module_attrs
.items():
662 print('%s: %s' %(m
, mdict
.get('mhelp')))
663 attrdict
= mdict
.get('attrs')
667 for attrname
, attrvaldict
in attrdict
.items():
668 if attrvaldict
.get('compat', False):
670 print('%s%s' %(indent
, attrname
))
671 print('%shelp: %s' %(indent
+ ' ',
672 attrvaldict
.get('help', '')))
673 print ('%srequired: %s' %(indent
+ ' ',
674 attrvaldict
.get('required', False)))
675 default
= attrvaldict
.get('default')
677 print('%sdefault: %s' %(indent
+ ' ', default
))
679 validrange
= attrvaldict
.get('validrange')
681 print('%svalidrange: %s-%s'
682 %(indent
+ ' ', validrange
[0], validrange
[1]))
684 validvals
= attrvaldict
.get('validvals')
686 print('%svalidvals: %s'
687 %(indent
+ ' ', ','.join(validvals
)))
689 examples
= attrvaldict
.get('example')
693 print '%sexample:' %(indent
+ ' ')
695 print '%s%s' %(indent
+ ' ', e
)
700 def load_scripts(self
, modules_dir
):
701 """ loading user modules from /etc/network/.
703 Note that previously loaded python modules override modules found
704 under /etc/network if any
708 self
.logger
.info('looking for user scripts under %s' %modules_dir
)
709 for op
, mlist
in self
.script_ops
.items():
710 msubdir
= modules_dir
+ '/if-%s.d' %op
711 self
.logger
.info('loading scripts under %s ...' %msubdir
)
713 module_list
= os
.listdir(msubdir
)
714 for module
in module_list
:
715 if self
.modules
.get(module
) is not None:
717 self
.script_ops
[op
].append(
718 msubdir
+ '/' + module
)
723 def _sched_ifaces(self
, ifacenames
, ops
, skipupperifaces
=False,
724 followdependents
=True, sort
=False):
725 self
.logger
.debug('scheduling \'%s\' for %s'
726 %(str(ops
), str(ifacenames
)))
727 self
._pretty
_print
_ordered
_dict
('dependency graph',
728 self
.dependency_graph
)
729 return ifaceScheduler
.sched_ifaces(self
, ifacenames
, ops
,
730 dependency_graph
=self
.dependency_graph
,
731 order
=ifaceSchedulerFlags
.INORDER
733 else ifaceSchedulerFlags
.POSTORDER
,
734 followdependents
=followdependents
,
735 skipupperifaces
=skipupperifaces
,
736 sort
=True if (sort
or self
.IFACE_CLASS
) else False)
738 def _render_ifacename(self
, ifacename
):
740 vlan_match
= re
.match("^([\d]+)-([\d]+)", ifacename
)
742 vlan_groups
= vlan_match
.groups()
743 if vlan_groups
[0] and vlan_groups
[1]:
744 [new_ifacenames
.append('%d' %v
)
745 for v
in range(int(vlan_groups
[0]),
746 int(vlan_groups
[1])+1)]
747 return new_ifacenames
749 def _preprocess_ifacenames(self
, ifacenames
):
750 """ validates interface list for config existance.
752 returns -1 if one or more interface not found. else, returns 0
758 ifaceobjs
= self
.get_ifaceobjs(i
)
760 # if name not available, render interface name and check again
761 rendered_ifacenames
= utils
.expand_iface_range(i
)
762 if rendered_ifacenames
:
763 for ri
in rendered_ifacenames
:
764 ifaceobjs
= self
.get_ifaceobjs(ri
)
766 err_iface
+= ' ' + ri
768 new_ifacenames
.append(ri
)
772 new_ifacenames
.append(i
)
774 raise Exception('cannot find interfaces:%s' %err_iface
)
775 return new_ifacenames
777 def _iface_whitelisted(self
, auto
, allow_classes
, excludepats
, ifacename
):
778 """ Checks if interface is whitelisted depending on set of parameters.
780 interfaces are checked against the allow_classes and auto lists.
785 # Check if interface matches the exclude patter
787 for e
in excludepats
:
788 if re
.search(e
, ifacename
):
790 ifaceobjs
= self
.get_ifaceobjs(ifacename
)
793 self
.logger
.debug('iface %s' %ifacename
+ ' not found')
795 # If matched exclude pattern, return false
800 # Check if interface belongs to the class
801 # the user is interested in, if not return false
805 common
= Set([allow_classes
]).intersection(
813 # If the user has requested auto class, check if the interface
823 def _compat_conv_op_to_mode(self
, op
):
824 """ Returns old op name to work with existing scripts """
827 elif op
== 'pre-down':
832 def generate_running_env(self
, ifaceobj
, op
):
833 """ Generates a dictionary with env variables required for
834 an interface. Used to support script execution for interfaces.
838 iface_env
= ifaceobj
.env
842 cenv
.update(iface_env
)
845 cenv
['MODE'] = self
._compat
_conv
_op
_to
_mode
(op
)
848 def _save_state(self
):
849 if not self
.STATEMANAGER_ENABLE
or not self
.STATEMANAGER_UPDATE
:
852 # Update persistant iface states
853 self
.statemanager
.save_state()
855 if self
.logger
.isEnabledFor(logging
.DEBUG
):
856 t
= sys
.exc_info()[2]
857 traceback
.print_tb(t
)
858 self
.logger
.warning('error saving state (%s)' %str
(e
))
860 def set_type(self
, type):
862 self
.type = ifaceType
.IFACE
864 self
.type = ifaceType
.BRIDGE_VLAN
866 self
.type = ifaceType
.UNKNOWN
868 def _process_delay_admin_state_queue(self
, op
):
869 if not self
._delay
_admin
_state
_iface
_queue
:
874 func
= self
.link_down
877 for i
in self
._delay
_admin
_state
_iface
_queue
:
879 if self
.link_exists(i
):
882 self
.logger
.warn(str(e
))
885 def up(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
886 excludepats
=None, printdependency
=None, syntaxcheck
=False,
887 type=None, skipupperifaces
=False):
888 """This brings the interface(s) up
891 ops (list): list of ops to perform on the interface(s).
892 Eg: ['pre-up', 'up', 'post-up'
895 auto (bool): act on interfaces marked auto
896 allow_classes (list): act on interfaces belonging to classes in the list
897 ifacenames (list): act on interfaces specified in this list
898 excludepats (list): list of patterns of interfaces to exclude
899 syntaxcheck (bool): only perform syntax check
905 self
.IFACE_CLASS
= True
906 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
909 self
.WITH_DEPENDS
= True
911 self
.read_iface_config()
915 # If only syntax check was requested, return here
920 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
922 # if iface list not given by user, assume all from config file
923 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
925 # filter interfaces based on auto and allow classes
926 filtered_ifacenames
= [i
for i
in ifacenames
927 if self
._iface
_whitelisted
(auto
, allow_classes
,
929 if not filtered_ifacenames
:
930 raise Exception('no ifaces found matching given allow lists')
933 self
.populate_dependency_info(ops
, filtered_ifacenames
)
934 self
.print_dependency(filtered_ifacenames
, printdependency
)
937 self
.populate_dependency_info(ops
)
940 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
941 skipupperifaces
=skipupperifaces
,
942 followdependents
=True if self
.WITH_DEPENDS
else False)
944 self
._process
_delay
_admin
_state
_queue
('up')
945 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
948 def down(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
949 excludepats
=None, printdependency
=None, usecurrentconfig
=False,
951 """ down an interface """
956 self
.IFACE_CLASS
= True
957 if not self
.ADDONS_ENABLE
: self
.STATEMANAGER_UPDATE
= False
960 self
.WITH_DEPENDS
= True
961 # For down we need to look at old state, unless usecurrentconfig
963 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
and
964 self
.statemanager
.ifaceobjdict
):
965 # Since we are using state manager objects,
966 # skip the updating of state manager objects
967 self
.logger
.debug('Looking at old state ..')
968 self
.read_old_iface_config()
970 # If no old state available
972 self
.read_iface_config()
974 raise Exception('error reading iface config (%s)' %str
(e
))
976 # If iface list is given by the caller, always check if iface
979 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
981 raise Exception('%s' %str
(e
) +
982 ' (interface was probably never up ?)')
984 # if iface list not given by user, assume all from config file
985 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
987 # filter interfaces based on auto and allow classes
988 filtered_ifacenames
= [i
for i
in ifacenames
989 if self
._iface
_whitelisted
(auto
, allow_classes
,
991 if not filtered_ifacenames
:
992 raise Exception('no ifaces found matching given allow lists ' +
993 '(or interfaces were probably never up ?)')
996 self
.populate_dependency_info(ops
, filtered_ifacenames
)
997 self
.print_dependency(filtered_ifacenames
, printdependency
)
1000 self
.populate_dependency_info(ops
)
1003 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
1004 followdependents
=True if self
.WITH_DEPENDS
else False)
1006 self
._process
_delay
_admin
_state
_queue
('down')
1007 if not self
.DRYRUN
and self
.ADDONS_ENABLE
:
1010 def query(self
, ops
, auto
=False, allow_classes
=None, ifacenames
=None,
1011 excludepats
=None, printdependency
=None,
1012 format
='native', type=None):
1013 """ query an interface """
1018 self
.IFACE_CLASS
= True
1019 if self
.STATEMANAGER_ENABLE
and ops
[0] == 'query-savedstate':
1020 return self
.statemanager
.dump_pretty(ifacenames
)
1021 self
.STATEMANAGER_UPDATE
= False
1023 self
.logger
.debug('setting flag ALL')
1025 self
.WITH_DEPENDS
= True
1027 if ops
[0] == 'query-syntax':
1028 self
._modules
_help
()
1030 elif ops
[0] == 'query-running':
1031 # create fake devices to all dependents that dont have config
1032 map(lambda i
: self
.create_n_save_ifaceobj(i
, self
.NOCONFIG
),
1036 self
.read_iface_config()
1040 if ifacenames
and ops
[0] != 'query-running':
1041 # If iface list is given, always check if iface is present
1042 ifacenames
= self
._preprocess
_ifacenames
(ifacenames
)
1044 # if iface list not given by user, assume all from config file
1045 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1047 # filter interfaces based on auto and allow classes
1048 if ops
[0] == 'query-running':
1049 filtered_ifacenames
= ifacenames
1051 filtered_ifacenames
= [i
for i
in ifacenames
1052 if self
._iface
_whitelisted
(auto
, allow_classes
,
1054 if not filtered_ifacenames
:
1055 raise Exception('no ifaces found matching ' +
1056 'given allow lists')
1058 self
.populate_dependency_info(ops
)
1059 if ops
[0] == 'query-dependency' and printdependency
:
1060 self
.print_dependency(filtered_ifacenames
, printdependency
)
1063 if ops
[0] == 'query':
1064 return self
.print_ifaceobjs_pretty(filtered_ifacenames
, format
)
1065 elif ops
[0] == 'query-raw':
1066 return self
.print_ifaceobjs_raw(filtered_ifacenames
)
1068 self
._sched
_ifaces
(filtered_ifacenames
, ops
,
1069 followdependents
=True if self
.WITH_DEPENDS
else False)
1071 if ops
[0] == 'query-checkcurr':
1072 ret
= self
.print_ifaceobjscurr_pretty(filtered_ifacenames
, format
)
1074 # if any of the object has an error, signal that silently
1076 elif ops
[0] == 'query-running':
1077 self
.print_ifaceobjsrunning_pretty(filtered_ifacenames
, format
)
1080 def _reload_currentlyup(self
, upops
, downops
, auto
=True, allow
=None,
1081 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1083 """ reload currently up interfaces """
1085 new_ifaceobjdict
= {}
1087 # Override auto to true
1090 self
.read_iface_config()
1093 if not self
.ifaceobjdict
:
1094 self
.logger
.warn("nothing to reload ..exiting.")
1096 already_up_ifacenames
= []
1097 # generate dependency graph of interfaces
1098 self
.populate_dependency_info(upops
)
1099 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1100 and self
.statemanager
.ifaceobjdict
):
1101 already_up_ifacenames
= self
.statemanager
.ifaceobjdict
.keys()
1103 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1104 filtered_ifacenames
= [i
for i
in ifacenames
1105 if self
._iface
_whitelisted
(auto
, allow_classes
,
1108 # Get already up interfaces that still exist in the interfaces file
1109 already_up_ifacenames_not_present
= Set(
1110 already_up_ifacenames
).difference(ifacenames
)
1111 already_up_ifacenames_still_present
= Set(
1112 already_up_ifacenames
).difference(
1113 already_up_ifacenames_not_present
)
1114 interfaces_to_up
= Set(already_up_ifacenames_still_present
).union(
1115 filtered_ifacenames
)
1117 if (already_up_ifacenames_not_present
and
1118 self
.config
.get('ifreload_currentlyup_down_notpresent') == '1'):
1119 self
.logger
.info('reload: schedule down on interfaces: %s'
1120 %str
(already_up_ifacenames_not_present
))
1122 # Save a copy of new iface objects and dependency_graph
1123 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1124 new_dependency_graph
= dict(self
.dependency_graph
)
1126 # old interface config is read into self.ifaceobjdict
1127 self
.read_old_iface_config()
1129 # reinitialize dependency graph
1130 self
.dependency_graph
= OrderedDict({})
1131 self
.populate_dependency_info(downops
,
1132 already_up_ifacenames_not_present
)
1133 self
._sched
_ifaces
(already_up_ifacenames_not_present
, downops
,
1134 followdependents
=False, sort
=True)
1136 self
.logger
.debug('no interfaces to down ..')
1138 # Now, run 'up' with new config dict
1139 # reset statemanager update flag to default
1142 self
.WITH_DEPENDS
= True
1143 if new_ifaceobjdict
:
1144 # and now, ifaceobjdict is back to current config
1145 self
.ifaceobjdict
= new_ifaceobjdict
1146 self
.dependency_graph
= new_dependency_graph
1148 if not self
.ifaceobjdict
:
1150 self
.logger
.info('reload: scheduling up on interfaces: %s'
1151 %str
(interfaces_to_up
))
1152 self
._sched
_ifaces
(interfaces_to_up
, upops
,
1153 followdependents
=True if self
.WITH_DEPENDS
else False)
1158 def _reload_default(self
, upops
, downops
, auto
=False, allow
=None,
1159 ifacenames
=None, excludepats
=None, usecurrentconfig
=False,
1161 """ reload interface config """
1163 new_ifaceobjdict
= {}
1166 self
.read_iface_config()
1170 if not self
.ifaceobjdict
:
1171 self
.logger
.warn("nothing to reload ..exiting.")
1173 # generate dependency graph of interfaces
1174 self
.populate_dependency_info(upops
)
1175 if (not usecurrentconfig
and self
.STATEMANAGER_ENABLE
1176 and self
.statemanager
.ifaceobjdict
):
1177 # Save a copy of new iface objects and dependency_graph
1178 new_ifaceobjdict
= dict(self
.ifaceobjdict
)
1179 new_dependency_graph
= dict(self
.dependency_graph
)
1181 # if old state is present, read old state and mark op for 'down'
1182 # followed by 'up' aka: reload
1183 # old interface config is read into self.ifaceobjdict
1184 self
.read_old_iface_config()
1187 # oldconfig not available, continue with 'up' with new config
1190 if not ifacenames
: ifacenames
= self
.ifaceobjdict
.keys()
1191 if op
== 'reload' and ifacenames
:
1192 filtered_ifacenames
= [i
for i
in ifacenames
1193 if self
._iface
_whitelisted
(auto
, allow_classes
,
1196 # if config file had 'ifreload_down_changed' variable
1197 # set, also look for interfaces that changed to down them
1198 down_changed
= int(self
.config
.get('ifreload_down_changed', '1'))
1200 # Generate the interface down list
1201 # Interfaces that go into the down list:
1202 # - interfaces that were present in last config and are not
1203 # present in the new config
1204 # - interfaces that were changed between the last and current
1207 for ifname
in filtered_ifacenames
:
1208 lastifaceobjlist
= self
.ifaceobjdict
.get(ifname
)
1210 # If interface is not present in the new file
1211 # append it to the down list
1212 newifaceobjlist
= new_ifaceobjdict
.get(ifname
)
1213 if not newifaceobjlist
:
1214 ifacedownlist
.append(ifname
)
1216 if not down_changed
:
1218 if len(newifaceobjlist
) != len(lastifaceobjlist
):
1219 ifacedownlist
.append(ifname
)
1222 # If interface has changed between the current file
1223 # and the last installed append it to the down list
1224 # compare object list
1225 for objidx
in range(0, len(lastifaceobjlist
)):
1226 oldobj
= lastifaceobjlist
[objidx
]
1227 newobj
= newifaceobjlist
[objidx
]
1228 if not newobj
.compare(oldobj
):
1229 ifacedownlist
.append(ifname
)
1233 self
.logger
.info('reload: scheduling down on interfaces: %s'
1234 %str
(ifacedownlist
))
1235 # reinitialize dependency graph
1236 self
.dependency_graph
= OrderedDict({})
1237 # Generate dependency info for old config
1238 self
.populate_dependency_info(downops
, ifacedownlist
)
1240 self
._sched
_ifaces
(ifacedownlist
, downops
,
1241 followdependents
=False,
1243 except Exception, e
:
1244 self
.logger
.error(str(e
))
1247 self
._process
_delay
_admin
_state
_queue
('down')
1249 self
.logger
.debug('no interfaces to down ..')
1251 # Now, run 'up' with new config dict
1252 # reset statemanager update flag to default
1253 if not new_ifaceobjdict
:
1258 self
.WITH_DEPENDS
= True
1259 # and now, we are back to the current config in ifaceobjdict
1260 self
.ifaceobjdict
= new_ifaceobjdict
1261 self
.dependency_graph
= new_dependency_graph
1262 ifacenames
= self
.ifaceobjdict
.keys()
1263 filtered_ifacenames
= [i
for i
in ifacenames
1264 if self
._iface
_whitelisted
(auto
, allow_classes
,
1267 self
.logger
.info('reload: scheduling up on interfaces: %s'
1268 %str
(filtered_ifacenames
))
1270 self
._sched
_ifaces
(filtered_ifacenames
, upops
,
1271 followdependents
=True if self
.WITH_DEPENDS
else False)
1272 except Exception, e
:
1273 self
.logger
.error(str(e
))
1276 self
._process
_delay
_admin
_state
_queue
('up')
1281 def reload(self
, *args
, **kargs
):
1282 """ reload interface config """
1283 self
.logger
.debug('reloading interface config ..')
1284 if kargs
.get('currentlyup', False):
1285 self
._reload
_currentlyup
(*args
, **kargs
)
1287 self
._reload
_default
(*args
, **kargs
)
1289 def _pretty_print_ordered_dict(self
, prefix
, argdict
):
1290 outbuf
= prefix
+ ' {\n'
1291 for k
, vlist
in argdict
.items():
1292 outbuf
+= '\t%s : %s\n' %(k
, str(vlist
))
1293 self
.logger
.debug(outbuf
+ '}')
1295 def print_dependency(self
, ifacenames
, format
):
1296 """ prints iface dependency information """
1299 ifacenames
= self
.ifaceobjdict
.keys()
1300 if format
== 'list':
1301 for k
,v
in self
.dependency_graph
.items():
1302 print '%s : %s' %(k
, str(v
))
1303 elif format
== 'dot':
1305 map(lambda i
: indegrees
.update({i
:
1306 self
.get_iface_refcnt(i
)}),
1307 self
.dependency_graph
.keys())
1308 graph
.generate_dots(self
.dependency_graph
, indegrees
)
1310 def print_ifaceobjs_raw(self
, ifacenames
):
1311 """ prints raw lines for ifaces from config file """
1313 for i
in ifacenames
:
1314 for ifaceobj
in self
.get_ifaceobjs(i
):
1315 if (self
.is_ifaceobj_builtin(ifaceobj
) or
1316 not ifaceobj
.is_config_present()):
1318 ifaceobj
.dump_raw(self
.logger
)
1320 if self
.WITH_DEPENDS
and not self
.ALL
:
1321 dlist
= ifaceobj
.lowerifaces
1322 if not dlist
: continue
1323 self
.print_ifaceobjs_raw(dlist
)
1325 def _get_ifaceobjs_pretty(self
, ifacenames
, ifaceobjs
, running
=False):
1326 """ returns iface obj list """
1328 for i
in ifacenames
:
1329 for ifaceobj
in self
.get_ifaceobjs(i
):
1330 if ((not running
and self
.is_ifaceobj_noconfig(ifaceobj
)) or
1331 (running
and not ifaceobj
.is_config_present())):
1333 ifaceobjs
.append(ifaceobj
)
1334 if self
.WITH_DEPENDS
and not self
.ALL
:
1335 dlist
= ifaceobj
.lowerifaces
1336 if not dlist
: continue
1337 self
._get
_ifaceobjs
_pretty
(dlist
, ifaceobjs
, running
)
1339 def print_ifaceobjs_pretty(self
, ifacenames
, format
='native'):
1340 """ pretty prints iface in format given by keyword arg format """
1343 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
)
1344 if not ifaceobjs
: return
1345 if format
== 'json':
1346 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
,
1347 indent
=4, separators
=(',', ': '))
1349 expand
= int(self
.config
.get('ifquery_ifacename_expand_range', '0'))
1351 if not expand
and (i
.flags
& iface
.IFACERANGE_ENTRY
):
1352 # print only the first one
1353 if i
.flags
& iface
.IFACERANGE_START
:
1354 i
.dump_pretty(use_realname
=True)
1358 def _get_ifaceobjscurr_pretty(self
, ifacenames
, ifaceobjs
):
1360 for i
in ifacenames
:
1361 ifaceobjscurr
= self
.get_ifaceobjcurr(i
)
1362 if not ifaceobjscurr
: continue
1363 for ifaceobj
in ifaceobjscurr
:
1364 if (ifaceobj
.status
== ifaceStatus
.NOTFOUND
or
1365 ifaceobj
.status
== ifaceStatus
.ERROR
):
1367 if self
.is_ifaceobj_noconfig(ifaceobj
):
1369 ifaceobjs
.append(ifaceobj
)
1370 if self
.WITH_DEPENDS
and not self
.ALL
:
1371 dlist
= ifaceobj
.lowerifaces
1372 if not dlist
: continue
1373 dret
= self
._get
_ifaceobjscurr
_pretty
(dlist
, ifaceobjs
)
1377 def print_ifaceobjscurr_pretty(self
, ifacenames
, format
='native'):
1378 """ pretty prints current running state of interfaces with status.
1380 returns 1 if any of the interface has an error,
1385 ret
= self
._get
_ifaceobjscurr
_pretty
(ifacenames
, ifaceobjs
)
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(with_status
=True,
1392 successstr
=self
.config
.get('ifquery_check_success_str',
1394 errorstr
=self
.config
.get('ifquery_check_error_str', _error_sym
),
1395 unknownstr
=self
.config
.get('ifquery_check_unknown_str', '')),
1399 def print_ifaceobjsrunning_pretty(self
, ifacenames
, format
='native'):
1400 """ pretty prints iface running state """
1403 self
._get
_ifaceobjs
_pretty
(ifacenames
, ifaceobjs
, running
=True)
1404 if not ifaceobjs
: return
1405 if format
== 'json':
1406 print json
.dumps(ifaceobjs
, cls
=ifaceJsonEncoder
, indent
=2,
1407 separators
=(',', ': '))
1409 map(lambda i
: i
.dump_pretty(), ifaceobjs
)
1412 print 'ifupdown main object dump'
1413 print self
.pp
.pprint(self
.modules
)
1414 print self
.pp
.pprint(self
.ifaceobjdict
)
1416 def _dump_ifaceobjs(self
, ifacenames
):
1417 for i
in ifacenames
:
1418 ifaceobjs
= self
.get_ifaceobjs(i
)