]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown/ifupdownmain.py
Fix a few ordering issues + fix following upperiface for bridge device
[mirror_ifupdown2.git] / ifupdown / ifupdownmain.py
CommitLineData
a6f80f0e 1#!/usr/bin/python
3e8ee54f 2#
904908bc 3# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
3e8ee54f 4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6# ifupdownMain --
7# ifupdown main module
8#
a6f80f0e 9
10import os
11import re
3e8ee54f 12import imp
13import pprint
14import logging
15import sys, traceback
cb7cc592 16import copy
2cd06f78 17import json
a6f80f0e 18from statemanager import *
19from networkinterfaces import *
20from iface import *
21from scheduler import *
22from collections import deque
23from collections import OrderedDict
a6f80f0e 24from graph import *
3e8ee54f 25from sets import Set
a6f80f0e 26
2c0ad8b3
RP
27"""
28.. module:: ifupdownmain
29:synopsis: main module for ifupdown package
30
31.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
32
33"""
34
86fc62e2 35_tickmark = u'\u2713'
36_crossmark = u'\u2717'
37_success_sym = _tickmark
38_error_sym = _crossmark
39
be0b20f2 40class ifupdownMain(ifupdownBase):
53b00224 41 """ ifupdown2 main class """
42
a6f80f0e 43 # Flags
cca03c30 44 WITH_DEPENDS = False
a6f80f0e 45 ALL = False
5ee3e1a8 46 IFACE_CLASS = False
6bd7fc74 47 COMPAT_EXEC_SCRIPTS = False
20dd6242 48 STATEMANAGER_ENABLE = True
49 STATEMANAGER_UPDATE = True
50 ADDONS_ENABLE = False
a6f80f0e 51
37c0543d 52 # priv flags to mark iface objects
53 BUILTIN = 0x1
54 NOCONFIG = 0x2
55
56 scripts_dir='/etc/network'
57 addon_modules_dir='/usr/share/ifupdownaddons'
14dc390d 58 addon_modules_configfile='/var/lib/ifupdownaddons/addons.conf'
a6f80f0e 59
60 # iface dictionary in the below format:
61 # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
62 # eg:
53b00224 63 # { 'swp1' : [<iface swp1>, <iface swp2> ..] }
a6f80f0e 64 #
65 # Each ifaceobject corresponds to a configuration block for
66 # that interface
53b00224 67 # The value in the dictionary is a list because the network
68 # interface configuration file supports more than one iface section
69 # in the interfaces file
a6f80f0e 70 ifaceobjdict = OrderedDict()
71
a6f80f0e 72 # iface dictionary representing the curr running state of an iface
73 # in the below format:
74 # {'<ifacename>' : <ifaceobject>}
75 ifaceobjcurrdict = OrderedDict()
76
be0b20f2 77 # Dictionary representing operation and modules
78 # for every operation
79 module_ops = OrderedDict([('pre-up', []),
d08d5f54 80 ('up' , []),
81 ('post-up' , []),
82 ('query-checkcurr', []),
83 ('query-running', []),
84 ('query-dependency', []),
75730152 85 ('query', []),
86 ('query-raw', []),
d08d5f54 87 ('pre-down', []),
88 ('down' , []),
89 ('post-down' , [])])
37c0543d 90
91 # For old style /etc/network/ bash scripts
be0b20f2 92 script_ops = OrderedDict([('pre-up', []),
d08d5f54 93 ('up' , []),
94 ('post-up' , []),
95 ('pre-down', []),
96 ('down' , []),
97 ('post-down' , [])])
a6f80f0e 98
be0b20f2 99 # Handlers for ops that ifupdown2 owns
100 def run_up(self, ifaceobj):
62ddec8b 101 ifacename = ifaceobj.name
be0b20f2 102 if self.link_exists(ifacename):
103 self.link_up(ifacename)
104
105 def run_down(self, ifaceobj):
62ddec8b 106 ifacename = ifaceobj.name
be0b20f2 107 if self.link_exists(ifacename):
108 self.link_down(ifacename)
109
31a5f4c3 110 # ifupdown object interface operation handlers
be0b20f2 111 ops_handlers = OrderedDict([('up', run_up),
112 ('down', run_down)])
a6f80f0e 113
cb7cc592 114 def run_sched_ifaceobj_posthook(self, ifaceobj, op):
5c721925 115 if ((ifaceobj.priv_flags & self.BUILTIN) or
116 (ifaceobj.priv_flags & self.NOCONFIG)):
117 return
20dd6242 118 if self.STATEMANAGER_UPDATE:
cb7cc592 119 self.statemanager.ifaceobj_sync(ifaceobj, op)
31a5f4c3 120
121 # ifupdown object interface scheduler pre and posthooks
122 sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
123
14dc390d 124 def __init__(self, config={},
125 force=False, dryrun=False, nowait=False,
cca03c30 126 perfmode=False, withdepends=False, njobs=1,
14dc390d 127 cache=False, addons_enable=True, statemanager_enable=True,
3dcc1d0e 128 interfacesfile='/etc/network/interfaces',
129 interfacesfileiobuf=None,
130 interfacesfileformat='native'):
2c0ad8b3
RP
131 """This member function initializes the ifupdownmain object.
132
133 Kwargs:
134 config (dict): config dict from /etc/network/ifupdown2/ifupdown2.conf
135 force (bool): force interface configuration
136 dryrun (bool): dryrun interface configuration
137 withdepends (bool): apply interface configuration on all depends
138 interfacesfile (str): interfaces file. default is /etc/network/interfaces
139 interfacesfileformat (str): default is 'native'. Other choices are 'json'
140
141 Raises:
142 AttributeError, KeyError """
143
a6f80f0e 144 self.logger = logging.getLogger('ifupdown')
eab25b7c 145 self.FORCE = force
146 self.DRYRUN = dryrun
147 self.NOWAIT = nowait
148 self.PERFMODE = perfmode
cca03c30 149 self.WITH_DEPENDS = withdepends
20dd6242 150 self.STATEMANAGER_ENABLE = statemanager_enable
739f665b 151 self.CACHE = cache
14dc390d 152 self.interfacesfile = interfacesfile
3dcc1d0e 153 self.interfacesfileiobuf = interfacesfileiobuf
154 self.interfacesfileformat = interfacesfileformat
14dc390d 155 self.config = config
156 self.logger.debug(self.config)
a690dfae 157
158 # Can be used to provide hints for caching
159 self.CACHE_FLAGS = 0x0
37c0543d 160 self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
20dd6242 161 self.ADDONS_ENABLE = addons_enable
eab25b7c 162
a6f80f0e 163 self.ifaces = OrderedDict()
eab25b7c 164 self.njobs = njobs
a6f80f0e 165 self.pp = pprint.PrettyPrinter(indent=4)
37c0543d 166 self.modules = OrderedDict({})
d08d5f54 167 self.module_attrs = {}
20dd6242 168
37c0543d 169 self.load_addon_modules(self.addon_modules_dir)
e938bfec 170 if self.COMPAT_EXEC_SCRIPTS:
171 self.load_scripts(self.scripts_dir)
d08d5f54 172 self.dependency_graph = OrderedDict({})
a6f80f0e 173
20dd6242 174 if self.STATEMANAGER_ENABLE:
175 try:
176 self.statemanager = stateManager()
177 self.statemanager.read_saved_state()
178 except Exception, e:
179 # XXX Maybe we should continue by ignoring old state
180 self.logger.warning('error reading state (%s)' %str(e))
181 raise
182 else:
183 self.STATEMANAGER_UPDATE = False
a6f80f0e 184
31a5f4c3 185 def get_ifaceobjs(self, ifacename):
a6f80f0e 186 return self.ifaceobjdict.get(ifacename)
187
31a5f4c3 188 def get_ifaceobj_first(self, ifacename):
189 ifaceobjs = self.get_ifaceobjs(ifacename)
190 if ifaceobjs:
a6f80f0e 191 return ifaceobjs[0]
192 return None
193
c798b0f4 194 def get_ifacenames(self):
195 return self.ifaceobjdict.keys()
196
a6f80f0e 197 def get_iface_obj_last(self, ifacename):
198 return self.ifaceobjdict.get(ifacename)[-1]
199
8c13865c
RP
200 def must_follow_upperifaces(self, ifacename):
201 #
202 # XXX: This bleeds the knowledge of iface
203 # types in the infrastructure module.
204 # Cant think of a better fix at the moment.
205 # In future maybe the module can set a flag
206 # to indicate if we should follow upperifaces
207 #
208 ifaceobj = self.get_ifaceobj_first(ifacename)
209 if (ifaceobj.type == ifaceType.BRIDGE or
210 ifaceobj.type == ifaceType.BRIDGE_VLAN):
211 return False
212 return True
213
53b00224 214 def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
215 increfcnt=False):
216 """ creates a iface object and adds it to the iface dictionary """
217 ifaceobj = iface()
218 ifaceobj.name = ifacename
219 ifaceobj.priv_flags = priv_flags
220 ifaceobj.auto = True
221 if increfcnt:
222 ifaceobj.inc_refcnt()
223 self.ifaceobjdict[ifacename] = [ifaceobj]
224 return ifaceobj
225
d08d5f54 226 def create_n_save_ifaceobjcurr(self, ifaceobj):
923290bd 227 """ creates a copy of iface object and adds it to the iface
228 dict containing current iface objects
53b00224 229 """
739f665b 230 ifaceobjcurr = iface()
53b00224 231 ifaceobjcurr.name = ifaceobj.name
62ddec8b 232 ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
233 ifaceobjcurr.priv_flags = ifaceobj.priv_flags
2cd06f78 234 ifaceobjcurr.auto = ifaceobj.auto
923290bd 235 self.ifaceobjcurrdict.setdefault(ifaceobj.name,
236 []).append(ifaceobjcurr)
d08d5f54 237 return ifaceobjcurr
a6f80f0e 238
923290bd 239 def get_ifaceobjcurr(self, ifacename, idx=0):
240 ifaceobjlist = self.ifaceobjcurrdict.get(ifacename)
241 if not ifaceobjlist:
242 return None
243 if not idx:
244 return ifaceobjlist
245 else:
246 return ifaceobjlist[idx]
a6f80f0e 247
739f665b 248 def get_ifaceobjrunning(self, ifacename):
249 return self.ifaceobjrunningdict.get(ifacename)
250
a6f80f0e 251 def get_iface_refcnt(self, ifacename):
53b00224 252 """ Return iface ref count """
a6f80f0e 253 max = 0
31a5f4c3 254 ifaceobjs = self.get_ifaceobjs(ifacename)
20dd6242 255 if not ifaceobjs:
256 return 0
a6f80f0e 257 for i in ifaceobjs:
62ddec8b 258 if i.refcnt > max:
259 max = i.refcnt
a6f80f0e 260 return max
261
d08d5f54 262 def is_iface_builtin_byname(self, ifacename):
cca03c30 263 """ Returns true if iface name is a builtin interface.
a6f80f0e 264
cca03c30 265 A builtin interface is an interface which ifupdown understands.
266 The following are currently considered builtin ifaces:
267 - vlan interfaces in the format <ifacename>.<vlanid>
a6f80f0e 268 """
d08d5f54 269 return '.' in ifacename
a6f80f0e 270
37c0543d 271 def is_ifaceobj_builtin(self, ifaceobj):
272 """ Returns true if iface name is a builtin interface.
273
274 A builtin interface is an interface which ifupdown understands.
275 The following are currently considered builtin ifaces:
276 - vlan interfaces in the format <ifacename>.<vlanid>
277 """
d08d5f54 278 return (ifaceobj.priv_flags & self.BUILTIN)
37c0543d 279
280 def is_ifaceobj_noconfig(self, ifaceobj):
53b00224 281 """ Returns true if iface object did not have a user defined config.
37c0543d 282
283 These interfaces appear only when they are dependents of interfaces
284 which have user defined config
285 """
d08d5f54 286 return (ifaceobj.priv_flags & self.NOCONFIG)
37c0543d 287
d08d5f54 288 def is_iface_noconfig(self, ifacename):
289 """ Returns true if iface has no config """
37c0543d 290
31a5f4c3 291 ifaceobj = self.get_ifaceobj_first(ifacename)
d08d5f54 292 if not ifaceobj: return True
d08d5f54 293 return self.is_ifaceobj_noconfig(ifaceobj)
294
f3215127 295 def preprocess_dependency_list(self, upperifacename, dlist, ops):
739f665b 296 """ We go through the dependency list and
297 delete or add interfaces from the interfaces dict by
298 applying the following rules:
299 if flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
300 we only consider devices whose configuration was
301 specified in the network interfaces file. We delete
302 any interface whose config was not specified except
303 for vlan devices. vlan devices get special treatment.
304 Even if they are not present they are created and added
305 to the ifacesdict
306 elif flag _DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
307 we create objects for all dependent devices that are not
308 present in the ifacesdict
309 """
a6f80f0e 310 del_list = []
311
a6f80f0e 312 for d in dlist:
31a5f4c3 313 dilist = self.get_ifaceobjs(d)
d08d5f54 314 if not dilist:
315 if self.is_iface_builtin_byname(d):
f3215127 316 self.create_n_save_ifaceobj(d, self.BUILTIN | self.NOCONFIG,
317 True).add_to_upperifaces(upperifacename)
318 elif not self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
319 self.create_n_save_ifaceobj(d, self.NOCONFIG,
320 True).add_to_upperifaces(upperifacename)
a6f80f0e 321 else:
a6f80f0e 322 del_list.append(d)
323 else:
324 for di in dilist:
325 di.inc_refcnt()
f3215127 326 di.add_to_upperifaces(upperifacename)
a6f80f0e 327
328 for d in del_list:
329 dlist.remove(d)
330
ee3fcf44 331 def query_dependents(self, ifaceobj, ops, ifacenames):
a6f80f0e 332 """ Gets iface dependents by calling into respective modules """
41febf89 333 ret_dlist = []
a6f80f0e 334
20dd6242 335 # Get dependents for interface by querying respective modules
ee3fcf44 336 for module in self.modules.values():
7949b8a5 337 try:
338 if ops[0] == 'query-running':
339 if (not hasattr(module,
340 'get_dependent_ifacenames_running')):
341 continue
342 dlist = module.get_dependent_ifacenames_running(ifaceobj)
343 else:
344 if (not hasattr(module, 'get_dependent_ifacenames')):
345 continue
346 dlist = module.get_dependent_ifacenames(ifaceobj,
ee3fcf44 347 ifacenames)
7949b8a5 348 except Exception, e:
349 self.logger.warn('%s: error getting dependent interfaces (%s)'
350 %(ifaceobj.name, str(e)))
351 dlist = None
352 pass
41febf89
RP
353 if dlist: ret_dlist.extend(dlist)
354 return ret_dlist
20dd6242 355
c798b0f4 356 def populate_dependency_info(self, ops, ifacenames=None):
a6f80f0e 357 """ recursive function to generate iface dependency info """
20dd6242 358
31a5f4c3 359 if not ifacenames:
37c0543d 360 ifacenames = self.ifaceobjdict.keys()
c798b0f4 361
551a3627 362 iqueue = deque(ifacenames)
363 while iqueue:
364 i = iqueue.popleft()
a6f80f0e 365 # Go through all modules and find dependent ifaces
366 dlist = None
31a5f4c3 367 ifaceobj = self.get_ifaceobj_first(i)
368 if not ifaceobj:
a6f80f0e 369 continue
41febf89
RP
370 dlist = self.query_dependents(ifaceobj, ops, ifacenames)
371 if dlist and dlist != ifaceobj.lowerifaces:
62ddec8b 372 self.preprocess_dependency_list(ifaceobj.name,
f3215127 373 dlist, ops)
739f665b 374 [iqueue.append(d) for d in dlist]
41febf89
RP
375 self.dependency_graph.setdefault(i, []).extend(dlist)
376 ifaceobj.lowerifaces = self.dependency_graph.get(i)
377 else:
37c0543d 378 self.dependency_graph[i] = dlist
a6f80f0e 379
41febf89 380 def _add_ifaceobj(self, ifaceobj):
55c86113 381 currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
382 if not currentifaceobjlist:
383 self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
384 return
55c86113 385 if ifaceobj.compare(currentifaceobjlist[0]):
386 self.logger.warn('duplicate interface %s found' %ifaceobj.name)
387 return
923290bd 388 currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS
389 ifaceobj.flags |= iface.HAS_SIBLINGS
55c86113 390 self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
a6f80f0e 391
41febf89 392 def _save_iface(self, ifaceobj):
679e6567
RP
393 currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
394 if not currentifaceobjlist:
395 self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
396 return
397 if ifaceobj.compare(currentifaceobjlist[0]):
398 self.logger.warn('duplicate interface %s found' %ifaceobj.name)
399 return
400 currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS
401 ifaceobj.flags |= iface.HAS_SIBLINGS
402 self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
41febf89 403
3dcc1d0e 404 def _iface_configattr_syntax_checker(self, attrname, attrval):
d08d5f54 405 for m, mdict in self.module_attrs.items():
7949b8a5 406 if not mdict:
407 continue
d08d5f54 408 attrsdict = mdict.get('attrs')
7949b8a5 409 try:
410 if attrsdict.get(attrname):
411 return True
412 except AttributeError:
413 pass
d08d5f54 414 return False
415
3dcc1d0e 416 def _ifaceobj_syntax_checker(self, ifaceobj):
417 err = False
418 for attrname in ifaceobj.config:
419 found = False
420 for k, v in self.module_attrs.items():
421 if v and v.get('attrs', {}).get(attrname):
422 found = True
423 break
424 if not found:
425 err = True
426 self.logger.warn('%s: unsupported attribute \'%s\'' %attrname)
427 continue
428 return err
429
14dc390d 430 def read_iface_config(self):
a6f80f0e 431 """ Reads default network interface config /etc/network/interfaces. """
14dc390d 432 nifaces = networkInterfaces(self.interfacesfile,
3dcc1d0e 433 self.interfacesfileiobuf,
434 self.interfacesfileformat,
14dc390d 435 template_engine=self.config.get('template_engine'),
436 template_lookuppath=self.config.get('template_lookuppath'))
d08d5f54 437 nifaces.subscribe('iface_found', self._save_iface)
3dcc1d0e 438 nifaces.subscribe('validateifaceattr',
439 self._iface_configattr_syntax_checker)
440 nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker)
a6f80f0e 441 nifaces.load()
442
a6f80f0e 443 def read_old_iface_config(self):
14dc390d 444 """ Reads the saved iface config instead of default iface config.
445 And saved iface config is already read by the statemanager """
cb7cc592 446 self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
a6f80f0e 447
53b00224 448 def _load_addon_modules_config(self):
449 """ Load addon modules config file """
a6f80f0e 450
37c0543d 451 with open(self.addon_modules_configfile, 'r') as f:
452 lines = f.readlines()
453 for l in lines:
c0071225 454 litems = l.rstrip(' \n\t\r').split(',')
37c0543d 455 operation = litems[0]
456 mname = litems[1]
be0b20f2 457 self.module_ops[operation].append(mname)
37c0543d 458
459 def load_addon_modules(self, modules_dir):
a6f80f0e 460 """ load python modules from modules_dir
461
462 Default modules_dir is /usr/share/ifupdownmodules
463
464 """
a6f80f0e 465 self.logger.info('loading builtin modules from %s' %modules_dir)
53b00224 466 self._load_addon_modules_config()
a6f80f0e 467 if not modules_dir in sys.path:
37c0543d 468 sys.path.append(modules_dir)
a6f80f0e 469 try:
be0b20f2 470 for op, mlist in self.module_ops.items():
d08d5f54 471 for mname in mlist:
53b00224 472 if self.modules.get(mname):
d08d5f54 473 continue
474 mpath = modules_dir + '/' + mname + '.py'
475 if os.path.exists(mpath):
476 try:
477 m = __import__(mname)
478 mclass = getattr(m, mname)
479 except:
480 raise
481 minstance = mclass(force=self.FORCE,
482 dryrun=self.DRYRUN,
483 nowait=self.NOWAIT,
484 perfmode=self.PERFMODE,
a690dfae 485 cache=self.CACHE,
486 cacheflags=self.CACHE_FLAGS)
d08d5f54 487 self.modules[mname] = minstance
53b00224 488 try:
d08d5f54 489 self.module_attrs[mname] = minstance.get_modinfo()
53b00224 490 except:
491 pass
a6f80f0e 492 except:
493 raise
494
37c0543d 495 # Assign all modules to query operations
be0b20f2 496 self.module_ops['query-checkcurr'] = self.modules.keys()
497 self.module_ops['query-running'] = self.modules.keys()
498 self.module_ops['query-dependency'] = self.modules.keys()
499 self.module_ops['query'] = self.modules.keys()
500 self.module_ops['query-raw'] = self.modules.keys()
d08d5f54 501
14dc390d 502
53b00224 503 def _modules_help(self):
504 """ Prints addon modules supported syntax """
505
d08d5f54 506 indent = ' '
507 for m, mdict in self.module_attrs.items():
508 if not mdict:
509 continue
510 print('%s: %s' %(m, mdict.get('mhelp')))
511 attrdict = mdict.get('attrs')
512 if not attrdict:
513 continue
514 try:
515 for attrname, attrvaldict in attrdict.items():
516 if attrvaldict.get('compat', False):
517 continue
518 print('%s%s' %(indent, attrname))
519 print('%shelp: %s' %(indent + ' ',
520 attrvaldict.get('help', '')))
521 print ('%srequired: %s' %(indent + ' ',
522 attrvaldict.get('required', False)))
523 default = attrvaldict.get('default')
524 if default:
525 print('%sdefault: %s' %(indent + ' ', default))
526
527 validrange = attrvaldict.get('validrange')
528 if validrange:
1b0b81a2 529 print('%svalidrange: %s-%s'
7949b8a5 530 %(indent + ' ', validrange[0], validrange[1]))
d08d5f54 531
532 validvals = attrvaldict.get('validvals')
533 if validvals:
534 print('%svalidvals: %s'
535 %(indent + ' ', ','.join(validvals)))
536
537 examples = attrvaldict.get('example')
538 if not examples:
539 continue
a6f80f0e 540
d08d5f54 541 print '%sexample:' %(indent + ' ')
542 for e in examples:
543 print '%s%s' %(indent + ' ', e)
544 except:
545 pass
546 print ''
547
37c0543d 548 def load_scripts(self, modules_dir):
a6f80f0e 549 """ loading user modules from /etc/network/.
550
551 Note that previously loaded python modules override modules found
552 under /etc/network if any
553
554 """
555
37c0543d 556 self.logger.info('looking for user scripts under %s' %modules_dir)
be0b20f2 557 for op, mlist in self.script_ops.items():
d08d5f54 558 msubdir = modules_dir + '/if-%s.d' %op
559 self.logger.info('loading scripts under %s ...' %msubdir)
560 try:
561 module_list = os.listdir(msubdir)
562 for module in module_list:
563 if self.modules.get(module) is not None:
564 continue
be0b20f2 565 self.script_ops[op].append(
a6f80f0e 566 msubdir + '/' + module)
d08d5f54 567 except:
f802fe3c 568 # continue reading
569 pass
a6f80f0e 570
53b00224 571 def _sched_ifaces(self, ifacenames, ops):
c798b0f4 572 self.logger.debug('scheduling \'%s\' for %s'
d08d5f54 573 %(str(ops), str(ifacenames)))
a6f80f0e 574
7538dc77 575 self._pretty_print_ordered_dict('dependency graph',
576 self.dependency_graph)
c798b0f4 577 return ifaceScheduler.sched_ifaces(self, ifacenames, ops,
578 dependency_graph=self.dependency_graph,
d08d5f54 579 order=ifaceSchedulerFlags.INORDER
580 if 'down' in ops[0]
c798b0f4 581 else ifaceSchedulerFlags.POSTORDER,
582 followdependents=True if self.WITH_DEPENDS else False)
a6f80f0e 583
41febf89
RP
584 def _render_ifacename(self, ifacename):
585 new_ifacenames = []
679e6567 586 vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
41febf89
RP
587 if vlan_match:
588 vlan_groups = vlan_match.groups()
589 if vlan_groups[0] and vlan_groups[1]:
679e6567 590 [new_ifacenames.append('%d' %v)
41febf89
RP
591 for v in range(int(vlan_groups[0]),
592 int(vlan_groups[1])+1)]
593 return new_ifacenames
594
595 def _preprocess_ifacenames(self, ifacenames):
a6f80f0e 596 """ validates interface list for config existance.
597
598 returns -1 if one or more interface not found. else, returns 0
599
600 """
41febf89 601 new_ifacenames = []
a6f80f0e 602 err_iface = ''
603 for i in ifacenames:
31a5f4c3 604 ifaceobjs = self.get_ifaceobjs(i)
605 if not ifaceobjs:
41febf89 606 # if name not available, render interface name and check again
679e6567 607 rendered_ifacenames = utils.expand_iface_range(i)
41febf89
RP
608 if rendered_ifacenames:
609 for ri in rendered_ifacenames:
610 ifaceobjs = self.get_ifaceobjs(ri)
611 if not ifaceobjs:
612 err_iface += ' ' + ri
613 else:
614 new_ifacenames.append(ri)
615 else:
616 err_iface += ' ' + i
617 else:
618 new_ifacenames.append(i)
fe0a57d3 619 if err_iface:
31c58787 620 raise Exception('cannot find interfaces:%s' %err_iface)
41febf89 621 return new_ifacenames
a6f80f0e 622
53b00224 623 def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
a6f80f0e 624 """ Checks if interface is whitelisted depending on set of parameters.
625
a6f80f0e 626 interfaces are checked against the allow_classes and auto lists.
627
628 """
fe0a57d3 629 if excludepats:
3e8ee54f 630 for e in excludepats:
d08d5f54 631 if re.search(e, ifacename):
3e8ee54f 632 return False
31a5f4c3 633 ifaceobjs = self.get_ifaceobjs(ifacename)
634 if not ifaceobjs:
a6f80f0e 635 self.logger.debug('iface %s' %ifacename + ' not found')
636 return False
a6f80f0e 637 # We check classes first
fe0a57d3 638 if allow_classes:
a6f80f0e 639 for i in ifaceobjs:
62ddec8b 640 if i.classes:
3e8ee54f 641 common = Set([allow_classes]).intersection(
62ddec8b 642 Set(i.classes))
fe0a57d3 643 if common:
a6f80f0e 644 return True
645 return False
d08d5f54 646 if auto:
a6f80f0e 647 for i in ifaceobjs:
62ddec8b 648 if i.auto:
a6f80f0e 649 return True
650 return False
a6f80f0e 651 return True
652
53b00224 653 def _compat_conv_op_to_mode(self, op):
654 """ Returns old op name to work with existing scripts """
655 if op == 'pre-up':
656 return 'start'
657 elif op == 'pre-down':
658 return 'stop'
659 else:
660 return op
661
a6f80f0e 662 def generate_running_env(self, ifaceobj, op):
739f665b 663 """ Generates a dictionary with env variables required for
664 an interface. Used to support script execution for interfaces.
a6f80f0e 665 """
666
667 cenv = None
62ddec8b 668 iface_env = ifaceobj.env
669 if iface_env:
a6f80f0e 670 cenv = os.environ
d08d5f54 671 if cenv:
a6f80f0e 672 cenv.update(iface_env)
673 else:
674 cenv = iface_env
53b00224 675 cenv['MODE'] = self._compat_conv_op_to_mode(op)
a6f80f0e 676 return cenv
677
53b00224 678 def _save_state(self):
20dd6242 679 if not self.STATEMANAGER_ENABLE or not self.STATEMANAGER_UPDATE:
680 return
681 try:
682 # Update persistant iface states
683 self.statemanager.save_state()
684 except Exception, e:
685 if self.logger.isEnabledFor(logging.DEBUG):
686 t = sys.exc_info()[2]
687 traceback.print_tb(t)
688 self.logger.warning('error saving state (%s)' %str(e))
689
d08d5f54 690 def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
9dce3561 691 excludepats=None, printdependency=None, syntaxcheck=False):
2c0ad8b3
RP
692 """This brings the interface(s) up
693
694 Args:
41febf89
RP
695 ops (list): list of ops to perform on the interface(s).
696 Eg: ['pre-up', 'up', 'post-up'
2c0ad8b3
RP
697
698 Kwargs:
699 auto (bool): act on interfaces marked auto
700 allow_classes (list): act on interfaces belonging to classes in the list
701 ifacenames (list): act on interfaces specified in this list
702 excludepats (list): list of patterns of interfaces to exclude
703 syntaxcheck (bool): only perform syntax check
704 """
53b00224 705
5ee3e1a8
RP
706 if allow_classes:
707 self.IFACE_CLASS = True
7538dc77 708 if not self.ADDONS_ENABLE: self.STATEMANAGER_UPDATE = False
d08d5f54 709 if auto:
a6f80f0e 710 self.ALL = True
cca03c30 711 self.WITH_DEPENDS = True
d08d5f54 712 try:
713 self.read_iface_config()
62ddec8b 714 except Exception:
d08d5f54 715 raise
a6f80f0e 716
9dce3561 717 # If only syntax check was requested, return here
718 if syntaxcheck:
719 return
720
31a5f4c3 721 if ifacenames:
41febf89 722 ifacenames = self._preprocess_ifacenames(ifacenames)
a6f80f0e 723
724 # if iface list not given by user, assume all from config file
31a5f4c3 725 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
a6f80f0e 726
727 # filter interfaces based on auto and allow classes
cca03c30 728 filtered_ifacenames = [i for i in ifacenames
53b00224 729 if self._iface_whitelisted(auto, allow_classes,
d08d5f54 730 excludepats, i)]
fe0a57d3 731 if not filtered_ifacenames:
d08d5f54 732 raise Exception('no ifaces found matching given allow lists')
a6f80f0e 733
20dd6242 734 if printdependency:
c798b0f4 735 self.populate_dependency_info(ops, filtered_ifacenames)
d08d5f54 736 self.print_dependency(filtered_ifacenames, printdependency)
739f665b 737 return
cca03c30 738 else:
c798b0f4 739 self.populate_dependency_info(ops)
740
525f0a30 741 try:
742 self._sched_ifaces(filtered_ifacenames, ops)
743 finally:
744 if not self.DRYRUN and self.ADDONS_ENABLE:
745 self._save_state()
a6f80f0e 746
d08d5f54 747 def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
5c721925 748 excludepats=None, printdependency=None, usecurrentconfig=False):
53b00224 749 """ down an interface """
750
5ee3e1a8
RP
751 if allow_classes:
752 self.IFACE_CLASS = True
7538dc77 753 if not self.ADDONS_ENABLE: self.STATEMANAGER_UPDATE = False
d08d5f54 754 if auto:
755 self.ALL = True
756 self.WITH_DEPENDS = True
5c721925 757 # For down we need to look at old state, unless usecurrentconfig
758 # is set
759 if (not usecurrentconfig and self.STATEMANAGER_ENABLE and
53b00224 760 self.statemanager.ifaceobjdict):
31a5f4c3 761 # Since we are using state manager objects,
762 # skip the updating of state manager objects
5c721925 763 self.logger.debug('Looking at old state ..')
d08d5f54 764 self.read_old_iface_config()
fe0a57d3 765 else:
d08d5f54 766 # If no old state available
d08d5f54 767 try:
768 self.read_iface_config()
769 except Exception, e:
770 raise Exception('error reading iface config (%s)' %str(e))
d08d5f54 771 if ifacenames:
772 # If iface list is given by the caller, always check if iface
773 # is present
31c58787 774 try:
41febf89 775 ifacenames = self._preprocess_ifacenames(ifacenames)
31c58787 776 except Exception, e:
777 raise Exception('%s' %str(e) +
778 ' (interface was probably never up ?)')
779
d08d5f54 780 # if iface list not given by user, assume all from config file
fe0a57d3 781 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
53b00224 782
d08d5f54 783 # filter interfaces based on auto and allow classes
784 filtered_ifacenames = [i for i in ifacenames
53b00224 785 if self._iface_whitelisted(auto, allow_classes,
d08d5f54 786 excludepats, i)]
fe0a57d3 787 if not filtered_ifacenames:
c0071225 788 raise Exception('no ifaces found matching given allow lists ' +
41febf89 789 '(or interfaces were probably never up ?)')
14dc390d 790
d08d5f54 791 if printdependency:
99b212b0 792 self.populate_dependency_info(ops, filtered_ifacenames)
d08d5f54 793 self.print_dependency(filtered_ifacenames, printdependency)
794 return
99b212b0 795 else:
796 self.populate_dependency_info(ops)
525f0a30 797
798 try:
799 self._sched_ifaces(filtered_ifacenames, ops)
800 finally:
801 if not self.DRYRUN and self.ADDONS_ENABLE:
802 self._save_state()
d08d5f54 803
804 def query(self, ops, auto=False, allow_classes=None, ifacenames=None,
739f665b 805 excludepats=None, printdependency=None,
d08d5f54 806 format='native'):
53b00224 807 """ query an interface """
808
5ee3e1a8
RP
809 if allow_classes:
810 self.IFACE_CLASS = True
5c721925 811 if self.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
812 return self.statemanager.dump_pretty(ifacenames)
20dd6242 813 self.STATEMANAGER_UPDATE = False
d08d5f54 814 if auto:
739f665b 815 self.logger.debug('setting flag ALL')
816 self.ALL = True
37c0543d 817 self.WITH_DEPENDS = True
739f665b 818
d08d5f54 819 if ops[0] == 'query-syntax':
53b00224 820 self._modules_help()
d08d5f54 821 return
822 elif ops[0] == 'query-running':
739f665b 823 # create fake devices to all dependents that dont have config
37c0543d 824 map(lambda i: self.create_n_save_ifaceobj(i, self.NOCONFIG),
825 ifacenames)
739f665b 826 else:
827 try:
828 self.read_iface_config()
829 except Exception:
830 raise
831
53b00224 832 if ifacenames and ops[0] != 'query-running':
41febf89
RP
833 # If iface list is given, always check if iface is present
834 ifacenames = self._preprocess_ifacenames(ifacenames)
739f665b 835
836 # if iface list not given by user, assume all from config file
31a5f4c3 837 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
739f665b 838
839 # filter interfaces based on auto and allow classes
d08d5f54 840 if ops[0] == 'query-running':
739f665b 841 filtered_ifacenames = ifacenames
842 else:
843 filtered_ifacenames = [i for i in ifacenames
53b00224 844 if self._iface_whitelisted(auto, allow_classes,
d08d5f54 845 excludepats, i)]
fe0a57d3 846 if not filtered_ifacenames:
739f665b 847 raise Exception('no ifaces found matching ' +
848 'given allow lists')
37c0543d 849
14dc390d 850 self.populate_dependency_info(ops, filtered_ifacenames)
d08d5f54 851 if ops[0] == 'query-dependency' and printdependency:
852 self.print_dependency(filtered_ifacenames, printdependency)
853 return
739f665b 854
75730152 855 if ops[0] == 'query':
856 return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
857 elif ops[0] == 'query-raw':
858 return self.print_ifaceobjs_raw(filtered_ifacenames)
859
53b00224 860 self._sched_ifaces(filtered_ifacenames, ops)
739f665b 861
d08d5f54 862 if ops[0] == 'query-checkcurr':
863 ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames, format)
739f665b 864 if ret != 0:
865 # if any of the object has an error, signal that silently
866 raise Exception('')
d08d5f54 867 elif ops[0] == 'query-running':
868 self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
739f665b 869 return
870
20dd6242 871 def reload(self, upops, downops, auto=False, allow=None,
14dc390d 872 ifacenames=None, excludepats=None, usecurrentconfig=False):
53b00224 873 """ reload interface config """
739f665b 874 allow_classes = []
c0071225 875 new_ifaceobjdict = {}
739f665b 876
877 self.logger.debug('reloading interface config ..')
d08d5f54 878 if auto:
739f665b 879 self.ALL = True
37c0543d 880 self.WITH_DEPENDS = True
739f665b 881
882 try:
883 self.read_iface_config()
20dd6242 884 except:
739f665b 885 raise
886
c0071225
RP
887 if not self.ifaceobjdict:
888 self.logger.warn("nothing to reload ..exiting.")
889 return
890
37c0543d 891 # generate dependency graph of interfaces
c798b0f4 892 self.populate_dependency_info(upops)
14dc390d 893 if (not usecurrentconfig and self.STATEMANAGER_ENABLE
894 and self.statemanager.ifaceobjdict):
895 # Save a copy of new iface objects and dependency_graph
896 new_ifaceobjdict = dict(self.ifaceobjdict)
897 new_dependency_graph = dict(self.dependency_graph)
37c0543d 898
739f665b 899 # if old state is present, read old state and mark op for 'down'
900 # followed by 'up' aka: reload
37c0543d 901 # old interface config is read into self.ifaceobjdict
739f665b 902 self.read_old_iface_config()
903 op = 'reload'
904 else:
905 # oldconfig not available, continue with 'up' with new config
906 op = 'up'
907
20dd6242 908 if not ifacenames: ifacenames = self.ifaceobjdict.keys()
fe0a57d3 909 if op == 'reload' and ifacenames:
739f665b 910 filtered_ifacenames = [i for i in ifacenames
53b00224 911 if self._iface_whitelisted(auto, allow_classes,
d08d5f54 912 excludepats, i)]
37c0543d 913 # Generate the interface down list
914 # Interfaces that go into the down list:
915 # - interfaces that were present in last config and are not
916 # present in the new config
917 # - interfaces that were changed between the last and current
918 # config
919 #
37c0543d 920 ifacedownlist = []
53b00224 921 for ifname, lastifaceobjlist in self.ifaceobjdict.items():
37c0543d 922 objidx = 0
37c0543d 923 # If interface is not present in the new file
924 # append it to the down list
53b00224 925 newifaceobjlist = new_ifaceobjdict.get(ifname)
926 if not newifaceobjlist:
37c0543d 927 ifacedownlist.append(ifname)
928 continue
37c0543d 929 # If interface has changed between the current file
930 # and the last installed append it to the down list
53b00224 931 if len(newifaceobjlist) != len(lastifaceobjlist):
37c0543d 932 ifacedownlist.append(ifname)
933 continue
37c0543d 934 # compare object list
53b00224 935 for objidx in range(0, len(lastifaceobjlist)):
936 oldobj = lastifaceobjlist[objidx]
937 newobj = newifaceobjlist[objidx]
ca3f4fc7 938 if not newobj.compare(oldobj):
37c0543d 939 ifacedownlist.append(ifname)
940 continue
941
fe0a57d3 942 if ifacedownlist:
37c0543d 943 self.logger.info('Executing down on interfaces: %s'
739f665b 944 %str(ifacedownlist))
20dd6242 945 # reinitialize dependency graph
946 self.dependency_graph = OrderedDict({})
37c0543d 947 # Generate dependency info for old config
14dc390d 948 self.populate_dependency_info(downops, ifacedownlist)
53b00224 949 self._sched_ifaces(ifacedownlist, downops)
37c0543d 950 else:
951 self.logger.debug('no interfaces to down ..')
739f665b 952
20dd6242 953 # Now, run 'up' with new config dict
954 # reset statemanager update flag to default
c0071225
RP
955 if not new_ifaceobjdict:
956 return
53b00224 957 self.ifaceobjdict = new_ifaceobjdict
958 self.dependency_graph = new_dependency_graph
739f665b 959 ifacenames = self.ifaceobjdict.keys()
960 filtered_ifacenames = [i for i in ifacenames
53b00224 961 if self._iface_whitelisted(auto, allow_classes,
d08d5f54 962 excludepats, i)]
c798b0f4 963
53b00224 964 self.logger.info('Scheduling up on interfaces: %s'
965 %str(filtered_ifacenames))
966 self._sched_ifaces(filtered_ifacenames, upops)
e37ad4a6 967 if self.DRYRUN:
968 return
53b00224 969 self._save_state()
a6f80f0e 970
7538dc77 971 def _pretty_print_ordered_dict(self, prefix, argdict):
525f0a30 972 outbuf = prefix + ' {\n'
53b00224 973 for k, vlist in argdict.items():
525f0a30 974 outbuf += '\t%s : %s\n' %(k, str(vlist))
7538dc77 975 self.logger.debug(outbuf + '}')
a6f80f0e 976
53b00224 977 def print_dependency(self, ifacenames, format):
978 """ prints iface dependency information """
a6f80f0e 979
53b00224 980 if not ifacenames:
981 ifacenames = self.ifaceobjdict.keys()
982 if format == 'list':
983 for k,v in self.dependency_graph.items():
984 print '%s : %s' %(k, str(v))
985 elif format == 'dot':
986 indegrees = {}
987 map(lambda i: indegrees.update({i :
988 self.get_iface_refcnt(i)}),
989 self.dependency_graph.keys())
990 graph.generate_dots(self.dependency_graph, indegrees)
a6f80f0e 991
739f665b 992 def print_ifaceobjs_raw(self, ifacenames):
53b00224 993 """ prints raw lines for ifaces from config file """
994
739f665b 995 for i in ifacenames:
31a5f4c3 996 for ifaceobj in self.get_ifaceobjs(i):
75730152 997 if (self.is_ifaceobj_builtin(ifaceobj) or
998 not ifaceobj.is_config_present()):
999 continue
1000 ifaceobj.dump_raw(self.logger)
739f665b 1001 print '\n'
339026c8 1002 if self.WITH_DEPENDS and not self.ALL:
62ddec8b 1003 dlist = ifaceobj.lowerifaces
fe0a57d3 1004 if not dlist: continue
53b00224 1005 self.print_ifaceobjs_raw(dlist)
739f665b 1006
2cd06f78 1007 def _get_ifaceobjs_pretty(self, ifacenames, ifaceobjs, running=False):
1008 """ returns iface obj list """
53b00224 1009
a6f80f0e 1010 for i in ifacenames:
31a5f4c3 1011 for ifaceobj in self.get_ifaceobjs(i):
2cd06f78 1012 if ((not running and self.is_ifaceobj_noconfig(ifaceobj)) or
1013 (running and not ifaceobj.is_config_present())):
75730152 1014 continue
2cd06f78 1015 ifaceobjs.append(ifaceobj)
339026c8 1016 if self.WITH_DEPENDS and not self.ALL:
62ddec8b 1017 dlist = ifaceobj.lowerifaces
fe0a57d3 1018 if not dlist: continue
2cd06f78 1019 self._get_ifaceobjs_pretty(dlist, ifaceobjs, running)
739f665b 1020
2cd06f78 1021 def print_ifaceobjs_pretty(self, ifacenames, format='native'):
1022 """ pretty prints iface in format given by keyword arg format """
eab25b7c 1023
2cd06f78 1024 ifaceobjs = []
1025 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs)
1026 if not ifaceobjs: return
1027 if format == 'json':
3dcc1d0e 1028 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
1029 indent=4, separators=(',', ': '))
2cd06f78 1030 else:
1031 map(lambda i: i.dump_pretty(), ifaceobjs)
53b00224 1032
2cd06f78 1033 def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
eab25b7c 1034 ret = 0
a6f80f0e 1035 for i in ifacenames:
923290bd 1036 ifaceobjscurr = self.get_ifaceobjcurr(i)
1037 if not ifaceobjscurr: continue
1038 for ifaceobj in ifaceobjscurr:
1039 if (ifaceobj.status == ifaceStatus.NOTFOUND or
1040 ifaceobj.status == ifaceStatus.ERROR):
1041 ret = 1
1042 if self.is_ifaceobj_noconfig(ifaceobj):
1043 continue
1044 ifaceobjs.append(ifaceobj)
1045 if self.WITH_DEPENDS and not self.ALL:
1046 dlist = ifaceobj.lowerifaces
1047 if not dlist: continue
1048 dret = self._get_ifaceobjscurr_pretty(dlist, ifaceobjs)
1049 if dret: ret = 1
2cd06f78 1050 return ret
1051
1052 def print_ifaceobjscurr_pretty(self, ifacenames, format='native'):
1053 """ pretty prints current running state of interfaces with status.
1054
1055 returns 1 if any of the interface has an error,
1056 else returns 0
1057 """
1058
1059 ifaceobjs = []
1060 ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
1061 if not ifaceobjs: return
1062 self.logger.debug(ifaceobjs)
1063 if format == 'json':
1064 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
1065 separators=(',', ': '))
1066 else:
86fc62e2 1067 map(lambda i: i.dump_pretty(with_status=True,
1068 successstr=self.config.get('check_success_str',
1069 _success_sym),
1070 errorstr=self.config.get('check_error_str', _error_sym)),
1071 ifaceobjs)
eab25b7c 1072 return ret
a6f80f0e 1073
d08d5f54 1074 def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
53b00224 1075 """ pretty prints iface running state """
1076
2cd06f78 1077 ifaceobjs = []
1078 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs, running=True)
1079 if not ifaceobjs: return
1080 if format == 'json':
1081 print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
1082 separators=(',', ': '))
1083 else:
1084 map(lambda i: i.dump_pretty(), ifaceobjs)
53b00224 1085
1086 def _dump(self):
1087 print 'ifupdown main object dump'
1088 print self.pp.pprint(self.modules)
1089 print self.pp.pprint(self.ifaceobjdict)
1090
1091 def _dump_ifaceobjs(self, ifacenames):
1092 for i in ifacenames:
1093 ifaceobjs = self.get_ifaceobjs(i)
1094 for i in ifaceobjs:
1095 i.dump(self.logger)
1096 print '\n'