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