]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdown/ifupdownmain.py
python3: replace python2 map with loops
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / ifupdownmain.py
CommitLineData
35681c06 1#!/usr/bin/env python3
3e8ee54f 2#
d486dd0d 3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
3e8ee54f 4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6# ifupdownMain --
7# ifupdown main module
8#
a6f80f0e 9
223ba5af
JF
10import re
11import os
12import logging
13import traceback
3e8ee54f 14import pprint
d486dd0d 15
a6f80f0e 16from collections import OrderedDict
a6f80f0e 17
482b2fab
JF
18from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPAddress, IPv4Address, IPv6Address
19
d486dd0d 20try:
223ba5af
JF
21 import ifupdown2.lib.nlcache as nlcache
22
d486dd0d
JF
23 import ifupdown2.ifupdownaddons.mstpctlutil
24
25 import ifupdown2.ifupdown.policymanager
d486dd0d
JF
26 import ifupdown2.ifupdown.statemanager as statemanager
27 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
28 import ifupdown2.ifupdown.ifupdownconfig as ifupdownConfig
29
30 from ifupdown2.ifupdown.graph import *
31 from ifupdown2.ifupdown.iface import *
32 from ifupdown2.ifupdown.scheduler import *
33 from ifupdown2.ifupdown.exceptions import *
34 from ifupdown2.ifupdown.networkinterfaces import *
35 from ifupdown2.ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
36except ImportError:
223ba5af
JF
37 import lib.nlcache as nlcache
38
d486dd0d
JF
39 import ifupdownaddons.mstpctlutil
40
41 import ifupdown.ifupdownflags
42 import ifupdown.policymanager
43 import ifupdown.statemanager as statemanager
44 import ifupdown.ifupdownflags as ifupdownflags
45 import ifupdown.ifupdownconfig as ifupdownConfig
46
47 from ifupdown.graph import *
48 from ifupdown.iface import *
49 from ifupdown.scheduler import *
50 from ifupdown.exceptions import *
51 from ifupdown.networkinterfaces import *
52 from ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
53
54
2c0ad8b3
RP
55"""
56.. module:: ifupdownmain
57:synopsis: main module for ifupdown package
58
59.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
60
61"""
62
3b01ed76
JF
63_tickmark = '\u2713'
64_crossmark = '\u2717'
e74d01e1
RP
65_success_sym = '(%s)' %_tickmark
66_error_sym = '(%s)' %_crossmark
86fc62e2 67
dbc018d3 68class ifupdownMainFlags():
6bd7fc74 69 COMPAT_EXEC_SCRIPTS = False
20dd6242 70 STATEMANAGER_ENABLE = True
71 STATEMANAGER_UPDATE = True
72 ADDONS_ENABLE = False
dbc018d3
RP
73 DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
74 SCHED_SKIP_CHECK_UPPERIFACES = False
75 CHECK_SHARED_DEPENDENTS = True
a6f80f0e 76
dbc018d3 77class ifacePrivFlags():
37c0543d 78 # priv flags to mark iface objects
dbc018d3
RP
79 BUILTIN = False
80 NOCONFIG = False
81
82 def __init__(self, builtin=False, noconfig=False):
83 self.BUILTIN = builtin
84 self.NOCONFIG = noconfig
d486dd0d 85
223ba5af 86class ifupdownMain:
dbc018d3 87 """ ifupdown2 main class """
37c0543d 88
d486dd0d
JF
89 scripts_dir = '/etc/network'
90 addon_modules_dir = ADDON_MODULES_DIR
91 addon_modules_configfile = ADDONS_CONF_PATH
a6f80f0e 92
be0b20f2 93 # Handlers for ops that ifupdown2 owns
94 def run_up(self, ifaceobj):
496745cd
RP
95 # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs).
96 # there is no real interface behind it
97 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
98 return
d2b35716
RP
99 if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
100 (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
d486dd0d 101 self._keep_link_down(ifaceobj)
c8a3b44e 102 return
a070c90e
RP
103 if self._delay_admin_state:
104 self._delay_admin_state_iface_queue.append(ifaceobj.name)
105 return
7f045fd8
RP
106 # If this object is a link slave, ie its link is controlled
107 # by its link master interface, then dont set the link state.
108 # But do allow user to change state of the link if the interface
109 # is already with its link master (hence the master check).
7e2e64fb 110 if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
7f045fd8 111 return
a070c90e 112 if not self.link_exists(ifaceobj.name):
d486dd0d
JF
113 return
114 if self._keep_link_down(ifaceobj):
115 return
05a49550 116 try:
223ba5af 117 self.netlink.link_up(ifaceobj.name)
05a49550
JF
118 except:
119 if ifaceobj.addr_method == 'manual':
120 pass
121 else:
122 raise
d486dd0d
JF
123
124 def _keep_link_down(self, ifaceobj):
9858b0a6
RP
125 if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
126 # user has asked to explicitly keep the link down,
127 # so, force link down
128 self.logger.info('%s: keeping link down due to user config' %ifaceobj.name)
223ba5af 129 self.netlink.link_down(ifaceobj.name)
d486dd0d
JF
130 return True
131 return False
be0b20f2 132
133 def run_down(self, ifaceobj):
d2b35716
RP
134 if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
135 (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
9219cef3 136 return
0ba04b38
RP
137 # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs)
138 # there is no real interface behind it
496745cd
RP
139 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
140 return
a070c90e
RP
141 if self._delay_admin_state:
142 self._delay_admin_state_iface_queue.append(ifaceobj.name)
143 return
7f045fd8
RP
144 # If this object is a link slave, ie its link is controlled
145 # by its link master interface, then dont set the link state.
146 # But do allow user to change state of the link if the interface
147 # is already with its link master (hence the master check).
7e2e64fb
RP
148 if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
149 return
a070c90e 150 if not self.link_exists(ifaceobj.name):
7e2e64fb 151 return
05a49550 152 try:
223ba5af
JF
153 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK:
154 # set intf down (except loopback)
155 self.netlink.link_down(ifaceobj.name)
156 else:
157 self.logger.info("%s: ifupdown2 cannot bring loopback interface down" % ifaceobj.name)
05a49550
JF
158 except:
159 if ifaceobj.addr_method == 'manual':
160 pass
161 else:
162 raise
be0b20f2 163
31a5f4c3 164 # ifupdown object interface operation handlers
be0b20f2 165 ops_handlers = OrderedDict([('up', run_up),
166 ('down', run_down)])
a6f80f0e 167
cb7cc592 168 def run_sched_ifaceobj_posthook(self, ifaceobj, op):
dbc018d3
RP
169 if (ifaceobj.priv_flags and (ifaceobj.priv_flags.BUILTIN or
170 ifaceobj.priv_flags.NOCONFIG)):
5c721925 171 return
dbc018d3 172 if self.flags.STATEMANAGER_UPDATE:
cb7cc592 173 self.statemanager.ifaceobj_sync(ifaceobj, op)
31a5f4c3 174
175 # ifupdown object interface scheduler pre and posthooks
176 sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
177
d486dd0d 178 def reset_ifupdown2(self):
223ba5af
JF
179 self.modules = OrderedDict({})
180 self.module_attrs = {}
181
d486dd0d 182 ifaceScheduler.reset()
223ba5af
JF
183 try:
184 ifupdown2.ifupdown.statemanager.reset()
185 ifupdown2.ifupdown.policymanager.reset()
186 ifupdown2.ifupdown.ifupdownflags.reset()
187 ifupdownConfig.reset()
188 ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
189 except:
190 try:
191 ifupdown.statemanager.reset()
192 ifupdown.policymanager.reset()
193 ifupdown.ifupdownflags.reset()
194 ifupdownConfig.reset()
195 ifupdownaddons.mstpctlutil.mstpctlutil.reset()
196 except:
197 pass
198
199 def ignore_error(self, errmsg):
200 if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
201 re.IGNORECASE | re.MULTILINE) is not None):
202 return True
203 return False
d486dd0d 204
223ba5af
JF
205 def log_warn(self, str):
206 if self.ignore_error(str) == False:
207 if self.logger.getEffectiveLevel() == logging.DEBUG:
208 traceback.print_stack()
209 traceback.print_exc()
210 self.logger.warn(str)
211 pass
212
213 def log_error(self, str):
214 if self.ignore_error(str) == False:
215 raise Exception(str)
216 else:
217 pass
d486dd0d 218
223ba5af
JF
219 def link_exists(self, ifacename):
220 return os.path.exists('/sys/class/net/%s' %ifacename)
d486dd0d 221
14dc390d 222 def __init__(self, config={},
d486dd0d 223 daemon=False, force=False, dryrun=False, nowait=False,
cca03c30 224 perfmode=False, withdepends=False, njobs=1,
14dc390d 225 cache=False, addons_enable=True, statemanager_enable=True,
3dcc1d0e 226 interfacesfile='/etc/network/interfaces',
227 interfacesfileiobuf=None,
6e16e5ae
N
228 interfacesfileformat='native',
229 withdefaults=False):
2c0ad8b3
RP
230 """This member function initializes the ifupdownmain object.
231
232 Kwargs:
233 config (dict): config dict from /etc/network/ifupdown2/ifupdown2.conf
234 force (bool): force interface configuration
235 dryrun (bool): dryrun interface configuration
236 withdepends (bool): apply interface configuration on all depends
237 interfacesfile (str): interfaces file. default is /etc/network/interfaces
238 interfacesfileformat (str): default is 'native'. Other choices are 'json'
239
240 Raises:
241 AttributeError, KeyError """
242
223ba5af
JF
243 modulename = self.__class__.__name__
244 self.logger = logging.getLogger('ifupdown.' + modulename)
245
d486dd0d
JF
246 if daemon:
247 self.reset_ifupdown2()
223ba5af
JF
248 else:
249 # init nlcache with appropriate log level
250 nlcache.NetlinkListenerWithCache.init(logging.root.level)
251
252 # start netlink listener and cache link/addr/netconf dumps
253 nlcache.NetlinkListenerWithCache.get_instance().start()
254
255 # save reference to nlcache
256 self.netlink = nlcache.NetlinkListenerWithCache.get_instance()
257 self.netlink.reset_errorq()
d486dd0d
JF
258
259 # iface dictionary in the below format:
260 # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
261 # eg:
262 # { 'swp1' : [<iface swp1>, <iface swp2> ..] }
263 #
264 # Each ifaceobject corresponds to a configuration block for
265 # that interface
266 # The value in the dictionary is a list because the network
267 # interface configuration file supports more than one iface section
268 # in the interfaces file
269 self.ifaceobjdict = OrderedDict()
270
271 # iface dictionary representing the curr running state of an iface
272 # in the below format:
273 # {'<ifacename>' : <ifaceobject>}
274 self.ifaceobjcurrdict = OrderedDict()
275
276 # Dictionary representing operation and modules
277 # for every operation
278 self.module_ops = OrderedDict([('pre-up', []),
279 ('up', []),
280 ('post-up', []),
281 ('query-checkcurr', []),
282 ('query-running', []),
283 ('query-dependency', []),
284 ('query', []),
285 ('query-raw', []),
286 ('pre-down', []),
287 ('down', []),
288 ('post-down', [])])
289
290 # For old style /etc/network/ bash scripts
291 self.script_ops = OrderedDict([('pre-up', []),
292 ('up', []),
293 ('post-up', []),
294 ('pre-down', []),
295 ('down', []),
296 ('post-down', [])])
297
298
a6f80f0e 299 self.logger = logging.getLogger('ifupdown')
fc5e1735
RP
300 ifupdownflags.flags.FORCE = force
301 ifupdownflags.flags.DRYRUN = dryrun
6e16e5ae 302 ifupdownflags.flags.WITHDEFAULTS = withdefaults
fc5e1735
RP
303 ifupdownflags.flags.NOWAIT = nowait
304 ifupdownflags.flags.PERFMODE = perfmode
305 ifupdownflags.flags.CACHE = cache
d2b35716 306 ifupdownflags.flags.WITH_DEPENDS = withdepends
fc5e1735 307
dbc018d3 308 # Can be used to provide hints for caching
fc5e1735 309 ifupdownflags.flags.CACHE_FLAGS = 0x0
dbc018d3
RP
310
311 self.flags = ifupdownMainFlags()
312
dbc018d3 313 self.flags.STATEMANAGER_ENABLE = statemanager_enable
14dc390d 314 self.interfacesfile = interfacesfile
3dcc1d0e 315 self.interfacesfileiobuf = interfacesfileiobuf
316 self.interfacesfileformat = interfacesfileformat
14dc390d 317 self.config = config
318 self.logger.debug(self.config)
67cfaeb1 319 self.blacklisted_ifaces_present = False
a690dfae 320
2da58137
RP
321 self.type = ifaceType.UNKNOWN
322
dbc018d3
RP
323 self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
324 self.flags.ADDONS_ENABLE = addons_enable
eab25b7c 325
a6f80f0e 326 self.ifaces = OrderedDict()
eab25b7c 327 self.njobs = njobs
a6f80f0e 328 self.pp = pprint.PrettyPrinter(indent=4)
37c0543d 329 self.modules = OrderedDict({})
d08d5f54 330 self.module_attrs = {}
b6b8bd2b
JF
331 self.overridden_ifupdown_scripts = []
332
60dfcbdf
RP
333 if self.config.get('addon_python_modules_support', '1') == '1':
334 self.load_addon_modules(self.addon_modules_dir)
335 if self.config.get('addon_scripts_support', '0') == '1':
e938bfec 336 self.load_scripts(self.scripts_dir)
d08d5f54 337 self.dependency_graph = OrderedDict({})
a6f80f0e 338
8e113d63
RP
339 self._cache_no_repeats = {}
340
9f98f360
JF
341 # initialize global config object with config passed by the user
342 # This makes config available to addon modules
343 ifupdownConfig.config = self.config
344 statemanager.statemanager_api.init()
345
dbc018d3 346 if self.flags.STATEMANAGER_ENABLE:
d486dd0d 347 self.statemanager = statemanager.statemanager_api
20dd6242 348 try:
20dd6242 349 self.statemanager.read_saved_state()
3b01ed76 350 except Exception as e:
d486dd0d
JF
351 # if read_saved_state fails, state file might be corrupt.
352 # Ignore old state and continue
20dd6242 353 self.logger.warning('error reading state (%s)' %str(e))
20dd6242 354 else:
dbc018d3 355 self.flags.STATEMANAGER_UPDATE = False
a070c90e
RP
356 self._delay_admin_state = True if self.config.get(
357 'delay_admin_state_change', '0') == '1' else False
358 self._delay_admin_state_iface_queue = []
cebe79c9
RP
359 if self._delay_admin_state:
360 self.logger.info('\'delay_admin_state_change\' is set. admin ' +
361 'state changes will be delayed till the end.')
362
363 self._link_master_slave = True if self.config.get(
a070c90e 364 'link_master_slave', '0') == '1' else False
cebe79c9
RP
365 if self._link_master_slave:
366 self.logger.info('\'link_master_slave\' is set. slave admin ' +
367 'state changes will be delayed till the ' +
368 'masters admin state change.')
a6f80f0e 369
2ddd65c5
RP
370 # squash iface objects for same interface both internal and
371 # external representation. It is off by default.
99ce6894
RP
372 self._ifaceobj_squash = True if self.config.get(
373 'ifaceobj_squash', '0') == '1' else False
374
2ddd65c5
RP
375 # squash iface objects for same interface internal
376 # representation only. External representation as seen by ifquery
377 # will continue to see multiple iface stanzas if it was specified
378 # that way by the user. It is on by default.
379 self._ifaceobj_squash_internal = True if self.config.get(
380 'ifaceobj_squash_internal', '1') == '1' else False
381
482b2fab
JF
382 self.validate_keywords = {
383 '<mac>': self._keyword_mac,
384 '<text>': self._keyword_text,
385 '<ipv4>': self._keyword_ipv4,
386 '<ipv6>': self._keyword_ipv6,
2c592263 387 '<ip>': self._keyword_ip,
482b2fab
JF
388 '<number>': self._keyword_number,
389 '<interface>': self._keyword_interface,
390 '<ipv4-vrf-text>': self._keyword_ipv4_vrf_text,
391 '<number-ipv4-list>': self._keyword_number_ipv4_list,
392 '<interface-list>': self._keyword_interface_list,
393 '<ipv4/prefixlen>': self._keyword_ipv4_prefixlen,
394 '<ipv6/prefixlen>': self._keyword_ipv6_prefixlen,
2c592263 395 '<ip/prefixlen>': self._keyword_ip_prefixlen,
482b2fab 396 '<number-range-list>': self._keyword_number_range_list,
d486dd0d 397 '<number-comma-range-list>': self._keyword_number_comma_range_list,
482b2fab 398 '<interface-range-list>': self._keyword_interface_range_list,
d486dd0d 399 '<interface-range-list-multiple-of-16>': self._keyword_interface_range_list_multiple_of_16,
2c592263 400 '<mac-ip/prefixlen-list>': self._keyword_mac_ip_prefixlen_list,
482b2fab
JF
401 '<number-interface-list>': self._keyword_number_interface_list,
402 '<interface-yes-no-list>': self._keyword_interface_yes_no_list,
d486dd0d 403 '<interface-on-off-list>': self._keyword_interface_on_off_list,
482b2fab 404 '<interface-yes-no-0-1-list>': self._keyword_interface_yes_no_0_1_list,
d486dd0d 405 '<interface-disabled-automatic-enabled>': self._keyword_interface_disabled_automatic_enabled_list,
482b2fab 406 '<interface-yes-no-auto-list>': self._keyword_interface_yes_no_auto_list,
d486dd0d 407 '<interface-l2protocol-tunnel-list>': self._keyword_interface_l2protocol_tunnel_list
482b2fab
JF
408 }
409
61c4d724 410 def link_master_slave_ignore_error(self, errorstr):
d486dd0d 411 # If link master slave flag is set,
61c4d724
RP
412 # there may be cases where the lowerdev may not be
413 # up resulting in 'Network is down' error
414 # This can happen if the lowerdev is a LINK_SLAVE
415 # of another interface which is not up yet
416 # example of such a case:
417 # bringing up a vlan on a bond interface and the bond
418 # is a LINK_SLAVE of a bridge (in other words the bond is
419 # part of a bridge) which is not up yet
420 if self._link_master_slave:
a5bd56f2 421 if 'Network is down' in errorstr:
61c4d724
RP
422 return True
423 return False
424
31a5f4c3 425 def get_ifaceobjs(self, ifacename):
a6f80f0e 426 return self.ifaceobjdict.get(ifacename)
427
a9ab1b4f
RP
428 def get_ifaceobjs_saved(self, ifacename):
429 """ Return ifaceobjects from statemanager """
dbc018d3 430 if self.flags.STATEMANAGER_ENABLE:
a9ab1b4f
RP
431 return self.statemanager.get_ifaceobjs(ifacename)
432 else:
a5bd56f2 433 return None
a9ab1b4f 434
31a5f4c3 435 def get_ifaceobj_first(self, ifacename):
436 ifaceobjs = self.get_ifaceobjs(ifacename)
437 if ifaceobjs:
a6f80f0e 438 return ifaceobjs[0]
439 return None
440
c798b0f4 441 def get_ifacenames(self):
3b01ed76 442 return list(self.ifaceobjdict.keys())
c798b0f4 443
a6f80f0e 444 def get_iface_obj_last(self, ifacename):
445 return self.ifaceobjdict.get(ifacename)[-1]
446
a9ab1b4f 447
8c13865c
RP
448 def must_follow_upperifaces(self, ifacename):
449 #
450 # XXX: This bleeds the knowledge of iface
451 # types in the infrastructure module.
452 # Cant think of a better fix at the moment.
453 # In future maybe the module can set a flag
454 # to indicate if we should follow upperifaces
455 #
456 ifaceobj = self.get_ifaceobj_first(ifacename)
cb46a208 457 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
8c13865c
RP
458 return False
459 return True
460
53b00224 461 def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
462 increfcnt=False):
463 """ creates a iface object and adds it to the iface dictionary """
464 ifaceobj = iface()
465 ifaceobj.name = ifacename
466 ifaceobj.priv_flags = priv_flags
467 ifaceobj.auto = True
f3b69969
RP
468 if not self._link_master_slave:
469 ifaceobj.link_type = ifaceLinkType.LINK_NA
53b00224 470 if increfcnt:
471 ifaceobj.inc_refcnt()
472 self.ifaceobjdict[ifacename] = [ifaceobj]
473 return ifaceobj
474
d08d5f54 475 def create_n_save_ifaceobjcurr(self, ifaceobj):
923290bd 476 """ creates a copy of iface object and adds it to the iface
d486dd0d 477 dict containing current iface objects
53b00224 478 """
739f665b 479 ifaceobjcurr = iface()
53b00224 480 ifaceobjcurr.name = ifaceobj.name
cb46a208 481 ifaceobjcurr.type = ifaceobj.type
62ddec8b 482 ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
dbc018d3 483 ifaceobjcurr.priv_flags = copy.deepcopy(ifaceobj.priv_flags)
2cd06f78 484 ifaceobjcurr.auto = ifaceobj.auto
923290bd 485 self.ifaceobjcurrdict.setdefault(ifaceobj.name,
486 []).append(ifaceobjcurr)
d08d5f54 487 return ifaceobjcurr
a6f80f0e 488
923290bd 489 def get_ifaceobjcurr(self, ifacename, idx=0):
490 ifaceobjlist = self.ifaceobjcurrdict.get(ifacename)
491 if not ifaceobjlist:
492 return None
493 if not idx:
494 return ifaceobjlist
495 else:
496 return ifaceobjlist[idx]
a6f80f0e 497
a6f80f0e 498 def get_iface_refcnt(self, ifacename):
53b00224 499 """ Return iface ref count """
a6f80f0e 500 max = 0
31a5f4c3 501 ifaceobjs = self.get_ifaceobjs(ifacename)
20dd6242 502 if not ifaceobjs:
503 return 0
a6f80f0e 504 for i in ifaceobjs:
62ddec8b 505 if i.refcnt > max:
506 max = i.refcnt
a6f80f0e 507 return max
508
d08d5f54 509 def is_iface_builtin_byname(self, ifacename):
cca03c30 510 """ Returns true if iface name is a builtin interface.
d486dd0d 511
cca03c30 512 A builtin interface is an interface which ifupdown understands.
513 The following are currently considered builtin ifaces:
514 - vlan interfaces in the format <ifacename>.<vlanid>
a6f80f0e 515 """
d08d5f54 516 return '.' in ifacename
a6f80f0e 517
37c0543d 518 def is_ifaceobj_builtin(self, ifaceobj):
519 """ Returns true if iface name is a builtin interface.
d486dd0d 520
37c0543d 521 A builtin interface is an interface which ifupdown understands.
522 The following are currently considered builtin ifaces:
523 - vlan interfaces in the format <ifacename>.<vlanid>
524 """
7ef04d1b
RP
525 if (ifaceobj.priv_flags and ifaceobj.priv_flags.BUILTIN):
526 return True
527 return False
37c0543d 528
529 def is_ifaceobj_noconfig(self, ifaceobj):
53b00224 530 """ Returns true if iface object did not have a user defined config.
d486dd0d 531
37c0543d 532 These interfaces appear only when they are dependents of interfaces
533 which have user defined config
534 """
dbc018d3 535 return (ifaceobj.priv_flags and ifaceobj.priv_flags.NOCONFIG)
37c0543d 536
d08d5f54 537 def is_iface_noconfig(self, ifacename):
538 """ Returns true if iface has no config """
37c0543d 539
31a5f4c3 540 ifaceobj = self.get_ifaceobj_first(ifacename)
d08d5f54 541 if not ifaceobj: return True
d08d5f54 542 return self.is_ifaceobj_noconfig(ifaceobj)
543
45ca0b6d 544 def check_shared_dependents(self, ifaceobj, dlist):
858a230f 545 """ ABSOLETE: Check if dlist intersects with any other
45ca0b6d
RP
546 interface with slave dependents.
547 example: bond and bridges.
548 This function logs such errors """
549 setdlist = Set(dlist)
3b01ed76 550 for ifacename, ifacedlist in list(self.dependency_graph.items()):
45ca0b6d
RP
551 if not ifacedlist:
552 continue
553 check_depends = False
554 iobjs = self.get_ifaceobjs(ifacename)
dbc018d3
RP
555 if not iobjs:
556 continue
45ca0b6d
RP
557 for i in iobjs:
558 if (i.dependency_type == ifaceDependencyType.MASTER_SLAVE):
559 check_depends = True
560 if check_depends:
561 common = Set(ifacedlist).intersection(setdlist)
562 if common:
1cda1e43 563 self.logger.error('misconfig..?. iface %s and %s '
45ca0b6d 564 %(ifaceobj.name, ifacename) +
1cda1e43 565 'seem to share dependents/ports %s' %str(list(common)))
45ca0b6d 566
f7551dcb 567 def _set_iface_role(self, ifaceobj, role, upperifaceobj):
858a230f 568 if (self.flags.CHECK_SHARED_DEPENDENTS and
f7551dcb 569 (ifaceobj.role & ifaceRole.SLAVE) and
d486dd0d 570 (role == ifaceRole.SLAVE) and (upperifaceobj.role & ifaceRole.MASTER)):
3b01ed76
JF
571 self.logger.error("misconfig..? %s %s is enslaved to multiple interfaces %s"
572 % (ifaceobj.name,
573 ifaceLinkPrivFlags.get_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
574 ifaceobj.set_status(ifaceStatus.ERROR)
575 return
858a230f
RP
576 ifaceobj.role = role
577
65e0c276
RP
578 def _set_iface_role_n_kind(self, ifaceobj, upperifaceobj):
579 if (upperifaceobj.link_kind & ifaceLinkKind.BOND):
f7551dcb 580 self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
858a230f
RP
581 ifaceobj.link_privflags |= ifaceLinkPrivFlags.BOND_SLAVE
582
65e0c276 583 if (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
f7551dcb 584 self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
858a230f
RP
585 ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_PORT
586
ea9e3c0f
JF
587 if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
588 and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
589 upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
590
858a230f
RP
591 # vrf masters get processed after slaves, which means
592 # check both link_kind vrf and vrf slave
593 if ((upperifaceobj.link_kind & ifaceLinkKind.VRF) or
594 (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
f7551dcb 595 self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
858a230f 596 ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
768b4ec5
RP
597 if self._link_master_slave:
598 if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
599 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
600 else:
601 upperifaceobj.link_type = ifaceLinkType.LINK_NA
602 ifaceobj.link_type = ifaceLinkType.LINK_NA
65e0c276 603
ccbeedcd 604 def dump_iface_dependency_info(self):
d486dd0d 605 """ debug funtion to print raw dependency
ccbeedcd
RP
606 info - lower and upper devices"""
607
3b01ed76 608 for ifacename, ifaceobjs in self.ifaceobjdict.items():
ccbeedcd
RP
609 iobj = ifaceobjs[0]
610 self.logger.info("%s: refcnt: %d, lower: %s, upper: %s" %(ifacename,
611 self.get_iface_refcnt(ifacename),
612 str(iobj.lowerifaces) if iobj.lowerifaces else [],
613 str(iobj.upperifaces) if iobj.upperifaces else []))
614
615
7f045fd8 616 def preprocess_dependency_list(self, upperifaceobj, dlist, ops):
739f665b 617 """ We go through the dependency list and
618 delete or add interfaces from the interfaces dict by
619 applying the following rules:
dbc018d3 620 if flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
739f665b 621 we only consider devices whose configuration was
622 specified in the network interfaces file. We delete
623 any interface whose config was not specified except
624 for vlan devices. vlan devices get special treatment.
625 Even if they are not present they are created and added
626 to the ifacesdict
dbc018d3 627 elif flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
739f665b 628 we create objects for all dependent devices that are not
629 present in the ifacesdict
630 """
a6f80f0e 631 del_list = []
632
a6f80f0e 633 for d in dlist:
31a5f4c3 634 dilist = self.get_ifaceobjs(d)
d08d5f54 635 if not dilist:
7f045fd8 636 ni = None
d08d5f54 637 if self.is_iface_builtin_byname(d):
a070c90e 638 ni = self.create_n_save_ifaceobj(d,
dbc018d3
RP
639 ifacePrivFlags(True, True), True)
640 elif not self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
641 ni = self.create_n_save_ifaceobj(d,
642 ifacePrivFlags(False, True), True)
a6f80f0e 643 else:
a6f80f0e 644 del_list.append(d)
7f045fd8
RP
645 if ni:
646 ni.add_to_upperifaces(upperifaceobj.name)
21289e4a 647 self._set_iface_role_n_kind(ni, upperifaceobj)
a6f80f0e 648 else:
649 for di in dilist:
650 di.inc_refcnt()
7f045fd8 651 di.add_to_upperifaces(upperifaceobj.name)
21289e4a 652 self._set_iface_role_n_kind(di, upperifaceobj)
a6f80f0e 653 for d in del_list:
654 dlist.remove(d)
655
4cc2df04 656 def preprocess_upperiface(self, lowerifaceobj, ulist, ops):
ccbeedcd
RP
657 for u in ulist:
658 if (lowerifaceobj.upperifaces and
659 u in lowerifaceobj.upperifaces):
660 continue
661 lowerifaceobj.add_to_upperifaces(u)
662 uifacelist = self.get_ifaceobjs(u)
663 if uifacelist:
664 for ui in uifacelist:
665 lowerifaceobj.inc_refcnt()
666 self._set_iface_role_n_kind(lowerifaceobj, ui)
667 ui.add_to_lowerifaces(lowerifaceobj.name)
4cc2df04
RP
668
669 def query_lowerifaces(self, ifaceobj, ops, ifacenames, type=None):
a6f80f0e 670 """ Gets iface dependents by calling into respective modules """
41febf89 671 ret_dlist = []
a6f80f0e 672
20dd6242 673 # Get dependents for interface by querying respective modules
3b01ed76 674 for module in list(self.modules.values()):
7949b8a5 675 try:
676 if ops[0] == 'query-running':
677 if (not hasattr(module,
678 'get_dependent_ifacenames_running')):
679 continue
680 dlist = module.get_dependent_ifacenames_running(ifaceobj)
681 else:
682 if (not hasattr(module, 'get_dependent_ifacenames')):
683 continue
684 dlist = module.get_dependent_ifacenames(ifaceobj,
ee3fcf44 685 ifacenames)
3b01ed76 686 except Exception as e:
7949b8a5 687 self.logger.warn('%s: error getting dependent interfaces (%s)'
688 %(ifaceobj.name, str(e)))
689 dlist = None
690 pass
41febf89 691 if dlist: ret_dlist.extend(dlist)
84ca006f 692 return list(set(ret_dlist))
20dd6242 693
4cc2df04
RP
694 def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None):
695 """ Gets iface upperifaces by calling into respective modules """
696 ret_ulist = []
697
698 # Get upperifaces for interface by querying respective modules
3b01ed76 699 for module in list(self.modules.values()):
4cc2df04
RP
700 try:
701 if ops[0] == 'query-running':
702 if (not hasattr(module,
703 'get_upper_ifacenames_running')):
704 continue
705 ulist = module.get_upper_ifacenames_running(ifaceobj)
706 else:
707 if (not hasattr(module, 'get_upper_ifacenames')):
708 continue
709 ulist = module.get_upper_ifacenames(ifaceobj, ifacenames)
3b01ed76 710 except Exception as e:
4cc2df04
RP
711 self.logger.warn('%s: error getting upper interfaces (%s)'
712 %(ifaceobj.name, str(e)))
713 ulist = None
714 pass
715 if ulist: ret_ulist.extend(ulist)
716 return list(set(ret_ulist))
45ca0b6d 717
e1601369
RP
718 def populate_dependency_info(self, ops, ifacenames=None):
719 """ recursive function to generate iface dependency info """
720
721 if not ifacenames:
3b01ed76 722 ifacenames = list(self.ifaceobjdict.keys())
e1601369
RP
723
724 iqueue = deque(ifacenames)
725 while iqueue:
726 i = iqueue.popleft()
727 # Go through all modules and find dependent ifaces
728 dlist = None
4cc2df04 729 ulist = None
2d8b307b
RP
730 ifaceobjs = self.get_ifaceobjs(i)
731 if not ifaceobjs:
e1601369 732 continue
4cc2df04 733 dependents_processed = False
2d8b307b
RP
734
735 # Store all dependency info in the first ifaceobj
736 # but get dependency info from all ifaceobjs
737 ifaceobj = ifaceobjs[0]
738 for iobj in ifaceobjs:
4cc2df04 739 ulist = self.query_upperifaces(iobj, ops, ifacenames)
2d8b307b 740 if iobj.lowerifaces:
4cc2df04 741 dependents_processed = True
2d8b307b 742 break
4cc2df04 743 dlist = self.query_lowerifaces(iobj, ops, ifacenames)
2d8b307b
RP
744 if dlist:
745 break
4cc2df04
RP
746 if ulist:
747 self.preprocess_upperiface(ifaceobj, ulist, ops)
4cc2df04 748 if dependents_processed:
e1601369
RP
749 continue
750 if dlist:
7f045fd8 751 self.preprocess_dependency_list(ifaceobj,
e1601369
RP
752 dlist, ops)
753 ifaceobj.lowerifaces = dlist
754 [iqueue.append(d) for d in dlist]
4cc2df04
RP
755 #if not self.dependency_graph.get(i):
756 # self.dependency_graph[i] = dlist
757
3b01ed76 758 for i in list(self.ifaceobjdict.keys()):
4cc2df04 759 iobj = self.get_ifaceobj_first(i)
736e4b0d
RP
760 if (not iobj.link_kind and
761 not (iobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
762 iobj.name == 'lo'):
763 iobj.link_privflags |= ifaceLinkPrivFlags.LOOPBACK
4cc2df04
RP
764 if iobj.lowerifaces:
765 self.dependency_graph[i] = iobj.lowerifaces
766 else:
767 self.dependency_graph[i] = []
e1601369 768
67cfaeb1
RP
769 if not self.blacklisted_ifaces_present:
770 return
771
772 # Walk through the dependency graph and remove blacklisted
773 # interfaces that were picked up as dependents
3b01ed76 774 for i in list(self.dependency_graph.keys()):
67cfaeb1
RP
775 ifaceobj = self.get_ifaceobj_first(i)
776 if not ifaceobj:
777 continue
858a230f 778
67cfaeb1
RP
779 if ifaceobj.blacklisted and not ifaceobj.upperifaces:
780 # if blacklisted and was not picked up as a
781 # dependent of a upper interface, delete the
782 # interface from the dependency graph
783 dlist = ifaceobj.lowerifaces
784 if dlist:
785 for d in dlist:
397214a5
RP
786 difaceobjs = self.get_ifaceobjs(d)
787 if not difaceobjs:
67cfaeb1 788 continue
67cfaeb1 789 try:
397214a5
RP
790 for d in difaceobjs:
791 d.dec_refcnt()
792 d.upperifaces.remove(i)
67cfaeb1
RP
793 except:
794 self.logger.debug('error removing %s from %s upperifaces' %(i, d))
795 pass
796 self.logger.debug("populate_dependency_info: deleting blacklisted interface %s" %i)
797 del self.dependency_graph[i]
858a230f 798 continue
67cfaeb1 799
8e113d63
RP
800 def _check_config_no_repeats(self, ifaceobj):
801 """ check if object has an attribute that is
802 restricted to a single object in the system.
803 if yes, warn and return """
3b01ed76 804 for k,v in list(self._cache_no_repeats.items()):
8e113d63
RP
805 iv = ifaceobj.config.get(k)
806 if iv and iv[0] == v:
807 self.logger.error('ignoring interface %s. ' %ifaceobj.name +
808 'Only one object with attribute ' +
809 '\'%s %s\' allowed.' %(k, v))
810 return True
3b01ed76 811 for k, v in list(self.config.get('no_repeats', {}).items()):
8e113d63
RP
812 iv = ifaceobj.config.get(k)
813 if iv and iv[0] == v:
814 self._cache_no_repeats[k] = v
815 return False
a6f80f0e 816
2ddd65c5
RP
817 def _save_iface_squash(self, ifaceobj):
818 """ squash ifaceobjects belonging to same iface
819 into a single object """
820 if self._check_config_no_repeats(ifaceobj):
821 return
822 ifaceobj.priv_flags = ifacePrivFlags()
823 if not self._link_master_slave:
824 ifaceobj.link_type = ifaceLinkType.LINK_NA
825 currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
826 if not currentifaceobjlist:
827 self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
828 return
829 if ifaceobj.compare(currentifaceobjlist[0]):
830 self.logger.warn('duplicate interface %s found' %ifaceobj.name)
831 return
7b98c26f
JF
832 for obj in self.ifaceobjdict[ifaceobj.name]:
833 if obj.type == ifaceobj.type:
834 obj.squash(ifaceobj)
835 return
836 self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
2ddd65c5 837
41febf89 838 def _save_iface(self, ifaceobj):
8e113d63
RP
839 if self._check_config_no_repeats(ifaceobj):
840 return
dbc018d3 841 ifaceobj.priv_flags = ifacePrivFlags()
7e2e64fb
RP
842 if not self._link_master_slave:
843 ifaceobj.link_type = ifaceLinkType.LINK_NA
679e6567
RP
844 currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
845 if not currentifaceobjlist:
99ce6894
RP
846 self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
847 if not self._ifaceobj_squash:
848 ifaceobj.flags |= ifaceobj.YOUNGEST_SIBLING
849 return
679e6567
RP
850 if ifaceobj.compare(currentifaceobjlist[0]):
851 self.logger.warn('duplicate interface %s found' %ifaceobj.name)
852 return
8e113d63 853 if currentifaceobjlist[0].type == ifaceobj.type:
7444feea
ST
854 currentifaceobjlist[0].flags |= ifaceobj.HAS_SIBLINGS
855 ifaceobj.flags |= ifaceobj.HAS_SIBLINGS
856 # clear the OLDEST_SIBLING from all the siblings
857 for iface in self.ifaceobjdict[ifaceobj.name]:
858 iface.flags &= ~ifaceobj.OLDEST_SIBLING
859 # current sibling is the oldest
860 ifaceobj.flags |= ifaceobj.OLDEST_SIBLING
679e6567 861 self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
41febf89 862
482b2fab
JF
863 def _keyword_text(self, value, validrange=None):
864 return isinstance(value, str) and len(value) > 0
865
866 def _keyword_mac(self, value, validrange=None):
867 if value.strip().startswith('ether'):
868 value = value.strip()[6:]
869 return re.match('[0-9a-f]{1,2}([-:])[0-9a-f]{1,2}(\\1[0-9a-f]{1,2}){4}$',
870 value.lower())
871
872 def _keyword_check_list(self, _list, obj, limit=None):
873 try:
874 if limit and limit > 0:
3b01ed76 875 for i in range(0, limit):
482b2fab
JF
876 obj(_list[i])
877 return len(_list) == limit
878 else:
879 for elem in _list:
880 obj(elem)
881 return True
882 except Exception as e:
883 self.logger.debug('keyword: check list: %s' % str(e))
884 return False
885
886 def _keyword_ipv4(self, value, validrange=None):
887 return self._keyword_check_list(value.split(), IPv4Address, limit=1)
888
889 def _keyword_ipv4_prefixlen(self, value, validrange=None):
890 return self._keyword_check_list(value.split(), IPv4Network, limit=1)
891
892 def _keyword_ipv6(self, value, validrange=None):
893 return self._keyword_check_list(value.split(), IPv6Address, limit=1)
894
895 def _keyword_ipv6_prefixlen(self, value, validrange=None):
896 return self._keyword_check_list(value.split(), IPv6Network, limit=1)
897
2c592263 898 def _keyword_ip(self, value, validrange=None):
482b2fab
JF
899 return self._keyword_check_list(value.split(), IPAddress, limit=1)
900
2c592263 901 def _keyword_ip_prefixlen(self, value, validrange=None):
482b2fab
JF
902 return self._keyword_check_list(value.split(), IPNetwork, limit=1)
903
2c592263 904 def _keyword_mac_ip_prefixlen_list(self, value, validrange=None):
482b2fab 905 """
223ba5af
JF
906 MAC address followed by optional list of ip addresses
907 <mac> [<ip> <ip> ...]
482b2fab
JF
908 ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
909 """
910 try:
911 res = value.split()
482b2fab
JF
912 if not self._keyword_mac(res[0]):
913 return False
914 for ip in res[1:]:
2c592263 915 if not self._keyword_ip_prefixlen(ip):
482b2fab
JF
916 return False
917 return True
918 except Exception as e:
919 self.logger.debug('keyword: mac ipaddr prefixlen: %s' % str(e))
920 return False
921
922 def _keyword_number_ipv4_list(self, value, validrange=None):
923 """
924 <number>=<ipv4> [<number>=<ipv4> ...]
925 ex: bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1
926 """
927 try:
928 elements = value.split(' ')
929 if not elements:
930 return False
931 for elem in elements:
932 v = elem.split('=')
933 int(v[0])
934 IPv4Address(v[1])
935 return True
936 except Exception as e:
937 self.logger.debug('keyword: number ipv4: %s' % str(e))
938 return False
939
940 def _keyword_interface(self, ifacename, validrange=None):
941 return self.get_ifaceobjs(ifacename)
942
943 def _keyword_ipv4_vrf_text(self, value, validrange=None):
944 """
945 <ipv4> "vrf" <text>
946 ex: clagd-backup-ip 10.10.10.42 vrf blue
947 """
948 values = value.split()
949 size = len(values)
950
951 if size > 3 or size < 1:
952 return False
953 try:
954 IPv4Address(values[0])
955 if size > 1:
956 if values[1] != 'vrf':
957 return False
958 if size > 2:
959 if not self._keyword_text(values[2]):
960 return False
961 return True
962 except Exception as e:
963 self.logger.debug('keyword: ipv4 vrf text: %s' % str(e))
964 return False
965
966 def _keyword_interface_list_with_value(self, value, validvals):
967 values = value.split()
968 try:
969 if len(values) == 1:
970 if values[0] in validvals:
971 return True
972 for v in values:
973 iface_value = v.split('=')
974 size = len(iface_value)
975 if size != 2:
976 if iface_value[0] == 'glob' or iface_value[0] == 'regex':
977 continue
978 return False
979 if not iface_value[1] in validvals:
980 return False
981 return True
982 except Exception as e:
983 self.logger.debug('keyword: interface list with value: %s' % str(e))
984 return False
985
d486dd0d
JF
986 def _keyword_interface_on_off_list(self, value, validrange=None):
987 """
988 <yes|no> | ( <interface>=<on|off> [<interface>=<on|off> ...] )
989 ex: bridge-learning swp1=on swp2=off
990 """
991 return self._keyword_interface_list_with_value(value, ['on', 'off'])
992
482b2fab
JF
993 def _keyword_interface_yes_no_list(self, value, validrange=None):
994 """
995 <yes|no> | ( <interface>=<yes|no> [<interface>=<yes|no> ...] )
996 ex: mstpctl-portrestrrole swp1=yes swp2=no
997 """
998 return self._keyword_interface_list_with_value(value, ['yes', 'no'])
999
1000 def _keyword_interface_yes_no_auto_list(self, value, validrange=None):
1001 """
1002 <yes|no|auto> |
1003 ( <interface>=<yes|no|auto> [<interface>=<yes|no|auto> ...] )
1004 ex: mstpctl-portp2p swp1=yes swp2=no swp3=auto
1005 """
1006 return self._keyword_interface_list_with_value(value,
1007 ['yes', 'no', 'auto'])
1008
d486dd0d
JF
1009 def _keyword_interface_l2protocol_tunnel_list(self, value, validrange=None):
1010 """
1011 bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all
1012 bridge-l2protocol-tunnel lacp stp,lldp,cdp
1013 bridge-l2protocol-tunnel stp lacp cdp
1014 bridge-l2protocol-tunnel lldp pvst
1015 bridge-l2protocol-tunnel stp
1016 bridge-l2protocol-tunnel all
1017 """
1018 try:
1019 if '=' in value:
1020 for intf_arg in value.split():
1021 intf_arg_split = intf_arg.split('=')
1022 for arg in re.split(',|\s*', intf_arg_split[1]):
1023 if arg not in ['all', 'stp', 'lldp', 'lacp', 'cdp', 'pvst']:
1024 return False
1025 else:
1026 for arg in re.split(',|\s*', value):
1027 if arg not in ['all', 'stp', 'lldp', 'lacp', 'cdp', 'pvst']:
1028 return False
1029 except:
1030 return False
1031 return True
1032
482b2fab
JF
1033 def _keyword_interface_yes_no_0_1_list(self, value, validrange=None):
1034 """
1035 <yes|no|0|1> |
1036 ( <interface>=<yes|no|0|1> [<interface>=<yes|no|0|1> ...] )
1037 ex: bridge-portmcrouter swp1=yes swp2=yes swp3=1
1038 """
1039 return self._keyword_interface_list_with_value(value,
d486dd0d
JF
1040 ['yes', 'no', '1', '0', '2'])
1041
1042 def _keyword_interface_disabled_automatic_enabled_list(self, value, validrange=None):
1043 return self._keyword_interface_list_with_value(value, [
1044 '0', 'disabled', 'no',
1045 '1', 'automatic', 'yes',
1046 '2', 'enabled'])
482b2fab 1047
d486dd0d
JF
1048 def _keyword_interface_range_list_multiple_of_16(self, value, validrange):
1049 return self._keyword_interface_range_list(value, validrange, multiple=16)
1050
1051 def _keyword_interface_range_list(self, value, validrange, multiple=None):
482b2fab
JF
1052 """
1053 <number> | ( <interface>=<number> [ <interface>=number> ...] )
1054 ex: mstpctl-portpathcost swp1=0 swp2=1
1055 """
1056 values = value.split()
1057 try:
d486dd0d 1058 if len(values) == 1 and '=' not in values[0]:
482b2fab
JF
1059 try:
1060 n = int(values[0])
1061 if n < int(validrange[0]) or n > int(
1062 validrange[1]):
1063 raise invalidValueError('value of out range "%s":'
1064 ' valid attribute range: %s'
1065 % (values[0],
1066 '-'.join(validrange)))
d486dd0d
JF
1067
1068 if multiple is not None:
1069 if not (n % multiple == 0):
1070 raise invalidValueError('invalid value %s: must be a multiple of %s' % (n, multiple))
1071
482b2fab
JF
1072 return True
1073 except invalidValueError as e:
1074 raise e
1075 except Exception as e:
1076 self.logger.debug('keyword: interface range list: %s'
1077 % str(e))
1078 return False
1079 for v in values:
1080 iface_value = v.split('=')
1081 size = len(iface_value)
1082 if size != 2:
1083 return False
1084 number = int(iface_value[1])
1085 if number < int(validrange[0]) or number > int(
1086 validrange[1]):
1087 raise invalidValueError(
1088 'value of out range "%s" for iface "%s":'
1089 ' valid attribute range: %s'
1090 % (iface_value[1],
1091 iface_value[0],
1092 '-'.join(validrange)))
d486dd0d
JF
1093
1094 if multiple is not None:
1095 if not (number % multiple == 0):
1096 raise invalidValueError('invalid value %s: must be a multiple of %s' % (number, multiple))
1097
482b2fab
JF
1098 return True
1099 except invalidValueError as e:
1100 raise e
1101 except Exception as e:
1102 self.logger.debug('keyword: interface range list: %s' % str(e))
1103 return False
1104
1105 def _keyword_interface_list(self, value, validrange=None):
1106 """
1107 [glob|regex] <interface> [ [glob|regex] <interface> ...]
1108 ex: bridge-ports swp1 swp2 glob swp3-5.100 regex (swp[6|7|8].100)
1109 """
1110 interface_list = value.split()
1111 size = len(interface_list)
1112 i = 0
1113 while i < size:
1114 if interface_list[i] == 'glob' or interface_list[i] == 'regex':
1115 i += 1
1116 else:
1117 if not self._keyword_interface(interface_list[i]):
1118 return False
1119 i += 1
1120 return True
1121
1122 def _keyword_number_range_list(self, value, validrange=None):
1123 """
1124 <number> [<number>-<number>]
1125 ex: bridge-vids 42 100-200
1126 """
1127 number_list = value.split()
1128 try:
1129 i = 0
1130 while i < len(number_list):
1131 if '-' in number_list[i]:
1132 range = number_list[i].split('-')
1133 a = int(range[0])
1134 b = int(range[1])
1135 if a > b:
1136 return False
1137 else:
1138 int(number_list[i])
1139 i += 1
1140 return True
1141 except Exception as e:
1142 self.logger.debug('keyword: number range list: %s' % str(e))
1143 return False
1144
1145 def _keyword_number_interface_list(self, value, validrange=None):
1146 """
1147 <number> <interface> [<interface>... [<number> <interface> ... ]]
1148 bridge-waitport 42 swp1 swp2 swp3 9 swp4
1149 """
1150 interface_list = value.split()
1151 if not interface_list:
1152 return False
1153 try:
1154 int(interface_list[0])
1155 prev = True
1156 for elem in interface_list[1:]:
1157 try:
1158 int(elem)
1159 if prev:
1160 return False
1161 prev = True
1162 except:
1163 prev = False
1164 return not prev
1165 except Exception as e:
1166 self.logger.debug('keyword: number interface list: %s' % str(e))
1167 return False
1168
482b2fab
JF
1169 def _keyword_number(self, value, validrange=None):
1170 try:
1171 int(value)
1172 return True
1173 except Exception as e:
1174 self.logger.debug('keyword: number: %s' % str(e))
1175 return False
1176
1177 def _is_keyword(self, value):
1178 if isinstance(value, tuple):
1179 return True
1180 keyword_found = value in self.validate_keywords
1181 if value.startswith('<') and value.endswith('>') and not keyword_found:
1182 raise Exception('%s: invalid keyword, please make sure to use'
1183 ' a valid keyword see `ifquery -s`' % value)
1184 return keyword_found
1185
1186 def _check_validvals_value(self, attrname, value, validvals, validrange):
1187 if validvals and value not in validvals:
1188 is_valid = False
1189 for keyword in validvals:
1190 if self._is_keyword(keyword):
1191 if validrange:
1192 if self.validate_keywords[keyword](value, validrange):
1193 return {'result': True}
1194 else:
1195 if self.validate_keywords[keyword](value):
1196 return {'result': True}
1197 if not is_valid:
1198 return {
1199 'result': False,
1200 'message': 'invalid value "%s": valid attribute values: %s'
1201 % (value, validvals)
1202 }
d486dd0d
JF
1203 elif validvals and value in validvals:
1204 pass
482b2fab
JF
1205 elif validrange:
1206 if len(validrange) != 2:
1207 raise Exception('%s: invalid range in addon configuration'
1208 % '-'.join(validrange))
1209 _value = int(value)
1210 if _value < int(validrange[0]) or _value > int(validrange[1]):
1211 return {
1212 'result': False,
1213 'message': 'value of out range "%s": '
1214 'valid attribute range: %s'
1215 % (value, '-'.join(validrange))
1216 }
1217 return {'result': True}
1218
1219 def _check_validvals(self, ifacename, module_name, attrs):
1220 ifaceobj = self.get_ifaceobjs(ifacename)
1221 if not ifaceobj:
1222 return
1223 success = True
3b01ed76 1224 for attrname, attrvalue in list(ifaceobj[0].config.items()):
482b2fab
JF
1225 try:
1226 attrname_dict = attrs.get(attrname, {})
1227 validvals = attrname_dict.get('validvals', [])
1228 validrange = attrname_dict.get('validrange', [])
1229 for value in attrvalue:
1230 res = self._check_validvals_value(attrname,
1231 value,
1232 validvals,
1233 validrange)
1234 if not res['result']:
1235 self.logger.warn('%s: %s: %s' %
1236 (ifacename, attrname, res['message']))
1237 success = False
1238 except Exception as e:
1239 self.logger.warn('addon \'%s\': %s: %s' % (module_name,
1240 attrname,
1241 str(e)))
1242 success = False
1243 return success
1244
eb377c6c 1245 def _module_syntax_check(self, filtered_ifacenames):
482b2fab 1246 result = True
eb377c6c 1247 for ifacename in filtered_ifacenames:
3b01ed76 1248 for module in list(self.modules.values()):
eb377c6c 1249 try:
482b2fab
JF
1250 if hasattr(module, '_modinfo'):
1251 if not self._check_validvals(ifacename,
1252 module.__class__.__name__,
1253 module._modinfo.get('attrs', {})):
1254 result = False
1255 if hasattr(module, 'syntax_check') and callable(module.syntax_check):
8e9fc178
JF
1256 if not module.syntax_check(self.get_ifaceobjs(ifacename)[0],
1257 self.get_ifaceobjs):
482b2fab 1258 result = False
3b01ed76 1259 except Exception as e:
482b2fab
JF
1260 self.logger.warn('%s: %s' % (ifacename, str(e)))
1261 result = False
1262 return result
eb377c6c 1263
3dcc1d0e 1264 def _iface_configattr_syntax_checker(self, attrname, attrval):
3b01ed76 1265 for m, mdict in list(self.module_attrs.items()):
7949b8a5 1266 if not mdict:
1267 continue
d08d5f54 1268 attrsdict = mdict.get('attrs')
7949b8a5 1269 try:
1553a881
RP
1270 a = attrsdict.get(attrname)
1271 if a:
1272 if a.get('deprecated'):
1273 newa = a.get('new-attribute')
1274 if newa:
f57b9804 1275 self.logger.warn('attribute %s is deprecated. use %s instead.' %(attrname, newa))
1553a881 1276 else:
f57b9804 1277 self.logger.warn('attribute %s is deprecated.'
1553a881 1278 %attrname)
7949b8a5 1279 return True
a9633d05
JF
1280 else:
1281 for key in attrsdict:
1282 if 'aliases' in attrsdict[key]:
1283 if attrname in attrsdict[key]['aliases']:
1284 return True
7949b8a5 1285 except AttributeError:
1286 pass
d08d5f54 1287 return False
1288
3dcc1d0e 1289 def _ifaceobj_syntax_checker(self, ifaceobj):
cfa06db6 1290 ret = True
3b01ed76 1291 for attrname, attrvalue in list(ifaceobj.config.items()):
3dcc1d0e 1292 found = False
3b01ed76 1293 for k, v in list(self.module_attrs.items()):
3dcc1d0e 1294 if v and v.get('attrs', {}).get(attrname):
1295 found = True
1296 break
1297 if not found:
cfa06db6 1298 ret = False
fa714fa2
ST
1299 self.logger.warn('%s: unsupported attribute \'%s\'' \
1300 % (ifaceobj.name, attrname))
3dcc1d0e 1301 continue
cfa06db6 1302 return ret
3dcc1d0e 1303
223ba5af 1304 def read_iface_config(self, raw=False):
a6f80f0e 1305 """ Reads default network interface config /etc/network/interfaces. """
cfa06db6 1306 ret = True
14dc390d 1307 nifaces = networkInterfaces(self.interfacesfile,
3dcc1d0e 1308 self.interfacesfileiobuf,
1309 self.interfacesfileformat,
f27710fe 1310 template_enable=self.config.get('template_enable', 0),
14dc390d 1311 template_engine=self.config.get('template_engine'),
223ba5af
JF
1312 template_lookuppath=self.config.get('template_lookuppath'),
1313 raw=raw)
2ddd65c5
RP
1314 if self._ifaceobj_squash or self._ifaceobj_squash_internal:
1315 nifaces.subscribe('iface_found', self._save_iface_squash)
1316 else:
1317 nifaces.subscribe('iface_found', self._save_iface)
60dfcbdf
RP
1318 if self.config.get('addon_syntax_check', '1') == '1':
1319 nifaces.subscribe('validateifaceattr',
1320 self._iface_configattr_syntax_checker)
1321 nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker)
a6f80f0e 1322 nifaces.load()
cfa06db6
RP
1323 if nifaces.errors or nifaces.warns:
1324 ret = False
1325 return ret
a6f80f0e 1326
a6f80f0e 1327 def read_old_iface_config(self):
14dc390d 1328 """ Reads the saved iface config instead of default iface config.
1329 And saved iface config is already read by the statemanager """
cb7cc592 1330 self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
a6f80f0e 1331
53b00224 1332 def _load_addon_modules_config(self):
1333 """ Load addon modules config file """
a6f80f0e 1334
37c0543d 1335 with open(self.addon_modules_configfile, 'r') as f:
1336 lines = f.readlines()
1337 for l in lines:
7bbc9340
RP
1338 try:
1339 litems = l.strip(' \n\t\r').split(',')
1340 if not litems or len(litems) < 2:
1341 continue
1342 operation = litems[0]
1343 mname = litems[1]
1344 self.module_ops[operation].append(mname)
3b01ed76 1345 except Exception as e:
55d9fae1 1346 self.logger.warn('error reading line \'%s\' %s:' %(l, str(e)))
7bbc9340 1347 continue
37c0543d 1348
d486dd0d 1349 def load_addon_modules(self, modules_dir_list):
a6f80f0e 1350 """ load python modules from modules_dir
1351
1352 Default modules_dir is /usr/share/ifupdownmodules
1353
1354 """
d486dd0d
JF
1355 failed_import = list()
1356
1357 self.logger.info('loading builtin modules from %s' % str(modules_dir_list))
53b00224 1358 self._load_addon_modules_config()
d486dd0d
JF
1359
1360 for modules_dir in modules_dir_list:
1361 if not modules_dir in sys.path:
1362 sys.path.insert(1, modules_dir)
1363 try:
3b01ed76 1364 for op, mlist in list(self.module_ops.items()):
d486dd0d
JF
1365 for mname in mlist:
1366 if self.modules.get(mname):
b54179d2 1367 continue
d486dd0d
JF
1368 mpath = modules_dir + '/' + mname + '.py'
1369 if os.path.exists(mpath) and mpath not in failed_import:
1370 try:
1371 m = __import__(mname)
1372 mclass = getattr(m, mname)
1373 except Exception as e:
1374 self.logger.warning('cannot load "%s" module: %s' % (mname, str(e)))
1375 failed_import.append(mpath)
1376 continue
1377 try:
1378 minstance = mclass()
1379 script_override = minstance.get_overrides_ifupdown_scripts()
1380 self.overridden_ifupdown_scripts.extend(script_override)
3b01ed76 1381 except moduleNotSupported as e:
223ba5af 1382 self.logger.info('module %s not loaded (%s)'
d486dd0d
JF
1383 %(mname, str(e)))
1384 continue
1385 except:
1386 raise
1387 self.modules[mname] = minstance
1388 try:
1389 self.module_attrs[mname] = minstance.get_modinfo()
1390 except:
1391 pass
1392 except:
1393 raise
a6f80f0e 1394
37c0543d 1395 # Assign all modules to query operations
3b01ed76
JF
1396 self.module_ops['query-checkcurr'] = list(self.modules.keys())
1397 self.module_ops['query-running'] = list(self.modules.keys())
1398 self.module_ops['query-dependency'] = list(self.modules.keys())
1399 self.module_ops['query'] = list(self.modules.keys())
1400 self.module_ops['query-raw'] = list(self.modules.keys())
d08d5f54 1401
d486dd0d
JF
1402 def _keyword_number_comma_range_list(self, value, validrange=None):
1403 return self._keyword_number_range_list(value.replace(',', ' '), validrange=validrange)
14dc390d 1404
53b00224 1405
d486dd0d
JF
1406 def _modules_help(self, fmt):
1407 """ Prints addon modules supported syntax """
d08d5f54 1408
d486dd0d
JF
1409 if fmt == 'json':
1410 modinfos = {}
3b01ed76 1411 for key, value in list(self.modules.items()):
d486dd0d
JF
1412 if hasattr(value, '_modinfo'):
1413 modinfos[key] = {
1414 'mhelp': value._modinfo['mhelp'],
1415 'attrs': value.merge_modinfo_with_policy_files()
1416 }
3b01ed76 1417 print(json.dumps(modinfos))
d486dd0d
JF
1418 else:
1419 indent = ' '
3b01ed76 1420 for m, mdict in list(self.module_attrs.items()):
d486dd0d
JF
1421 if not mdict:
1422 continue
3b01ed76 1423 print(('%s: %s' %(m, mdict.get('mhelp'))))
d486dd0d
JF
1424 attrdict = self.modules[m].merge_modinfo_with_policy_files()
1425 if not attrdict:
1426 continue
1427 try:
3b01ed76 1428 for attrname, attrvaldict in list(attrdict.items()):
d486dd0d
JF
1429 if attrvaldict.get('compat', False):
1430 continue
3b01ed76
JF
1431 print(('%s%s' %(indent, attrname)))
1432 print(('%shelp: %s' %(indent + ' ',
1433 attrvaldict.get('help', ''))))
1434 print(('%srequired: %s' %(indent + ' ',
1435 attrvaldict.get('required', False))))
d486dd0d
JF
1436 default = attrvaldict.get('default')
1437 if default:
3b01ed76 1438 print(('%sdefault: %s' %(indent + ' ', default)))
d486dd0d
JF
1439
1440 validrange = attrvaldict.get('validrange')
1441 if validrange:
3b01ed76
JF
1442 print(('%svalidrange: %s-%s'
1443 %(indent + ' ', validrange[0], validrange[1])))
d486dd0d
JF
1444
1445 validvals = attrvaldict.get('validvals')
1446 if validvals:
3b01ed76
JF
1447 print(('%svalidvals: %s'
1448 %(indent + ' ', ','.join(validvals))))
d486dd0d
JF
1449
1450 examples = attrvaldict.get('example')
1451 if not examples:
1452 continue
d08d5f54 1453
3b01ed76 1454 print('%sexample:' %(indent + ' '))
d486dd0d 1455 for e in examples:
3b01ed76 1456 print('%s%s' %(indent + ' ', e))
d486dd0d
JF
1457 except:
1458 pass
3b01ed76 1459 print('')
a6f80f0e 1460
37c0543d 1461 def load_scripts(self, modules_dir):
a6f80f0e 1462 """ loading user modules from /etc/network/.
1463
1464 Note that previously loaded python modules override modules found
1465 under /etc/network if any
1466
1467 """
1468
37c0543d 1469 self.logger.info('looking for user scripts under %s' %modules_dir)
3b01ed76 1470 for op, mlist in list(self.script_ops.items()):
d08d5f54 1471 msubdir = modules_dir + '/if-%s.d' %op
1472 self.logger.info('loading scripts under %s ...' %msubdir)
1473 try:
1474 module_list = os.listdir(msubdir)
1475 for module in module_list:
b6b8bd2b 1476 if self.modules.get(module) or module in self.overridden_ifupdown_scripts:
d08d5f54 1477 continue
b6b8bd2b 1478 self.script_ops[op].append(msubdir + '/' + module)
d486dd0d 1479 except:
f802fe3c 1480 # continue reading
1481 pass
a6f80f0e 1482
6e16e5ae
N
1483 def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False,
1484 followdependents=True, sort=False):
c798b0f4 1485 self.logger.debug('scheduling \'%s\' for %s'
d08d5f54 1486 %(str(ops), str(ifacenames)))
7538dc77 1487 self._pretty_print_ordered_dict('dependency graph',
1488 self.dependency_graph)
6e16e5ae 1489 ifaceScheduler.sched_ifaces(self, ifacenames, ops,
d486dd0d
JF
1490 dependency_graph=self.dependency_graph,
1491 order=ifaceSchedulerFlags.INORDER
d08d5f54 1492 if 'down' in ops[0]
c798b0f4 1493 else ifaceSchedulerFlags.POSTORDER,
d486dd0d
JF
1494 followdependents=followdependents,
1495 skipupperifaces=skipupperifaces,
1496 sort=True if (sort or ifupdownflags.flags.CLASS) else False)
2009513f 1497 return ifaceScheduler.get_sched_status()
a6f80f0e 1498
41febf89
RP
1499 def _render_ifacename(self, ifacename):
1500 new_ifacenames = []
679e6567 1501 vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
41febf89
RP
1502 if vlan_match:
1503 vlan_groups = vlan_match.groups()
1504 if vlan_groups[0] and vlan_groups[1]:
679e6567 1505 [new_ifacenames.append('%d' %v)
41febf89
RP
1506 for v in range(int(vlan_groups[0]),
1507 int(vlan_groups[1])+1)]
1508 return new_ifacenames
1509
1510 def _preprocess_ifacenames(self, ifacenames):
a6f80f0e 1511 """ validates interface list for config existance.
d486dd0d 1512
a6f80f0e 1513 returns -1 if one or more interface not found. else, returns 0
1514
1515 """
41febf89 1516 new_ifacenames = []
a6f80f0e 1517 err_iface = ''
1518 for i in ifacenames:
31a5f4c3 1519 ifaceobjs = self.get_ifaceobjs(i)
1520 if not ifaceobjs:
41febf89 1521 # if name not available, render interface name and check again
679e6567 1522 rendered_ifacenames = utils.expand_iface_range(i)
41febf89
RP
1523 if rendered_ifacenames:
1524 for ri in rendered_ifacenames:
1525 ifaceobjs = self.get_ifaceobjs(ri)
1526 if not ifaceobjs:
1527 err_iface += ' ' + ri
1528 else:
1529 new_ifacenames.append(ri)
1530 else:
1531 err_iface += ' ' + i
1532 else:
1533 new_ifacenames.append(i)
fe0a57d3 1534 if err_iface:
31c58787 1535 raise Exception('cannot find interfaces:%s' %err_iface)
d486dd0d 1536 return new_ifacenames
a6f80f0e 1537
53b00224 1538 def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
a6f80f0e 1539 """ Checks if interface is whitelisted depending on set of parameters.
1540
a6f80f0e 1541 interfaces are checked against the allow_classes and auto lists.
1542
1543 """
1042b709 1544
0532f0a3
RP
1545 ret = True
1546
19e2bf8c 1547 # Check if interface matches the exclude patter
fe0a57d3 1548 if excludepats:
3e8ee54f 1549 for e in excludepats:
d08d5f54 1550 if re.search(e, ifacename):
0532f0a3 1551 ret = False
31a5f4c3 1552 ifaceobjs = self.get_ifaceobjs(ifacename)
1553 if not ifaceobjs:
0532f0a3
RP
1554 if ret:
1555 self.logger.debug('iface %s' %ifacename + ' not found')
1556 return ret
67cfaeb1 1557 # If matched exclude pattern, return false
0532f0a3
RP
1558 if not ret:
1559 for i in ifaceobjs:
1560 i.blacklisted = True
67cfaeb1 1561 self.blacklisted_ifaces_present = True
0532f0a3
RP
1562 return ret
1563 # Check if interface belongs to the class
67cfaeb1 1564 # the user is interested in, if not return false
fe0a57d3 1565 if allow_classes:
19e2bf8c 1566 ret = False
a6f80f0e 1567 for i in ifaceobjs:
62ddec8b 1568 if i.classes:
d486dd0d 1569 common = Set(allow_classes).intersection(
62ddec8b 1570 Set(i.classes))
fe0a57d3 1571 if common:
0532f0a3 1572 ret = True
0532f0a3 1573 if not ret:
19e2bf8c
RP
1574 # If a class was requested and interface does not belong
1575 # to the class, only then mark the ifaceobjs as blacklisted
1576 self.blacklisted_ifaces_present = True
1577 for i in ifaceobjs:
1578 i.blacklisted = True
1579 return ret
67cfaeb1
RP
1580 # If the user has requested auto class, check if the interface
1581 # is marked auto
d08d5f54 1582 if auto:
19e2bf8c 1583 ret = False
a6f80f0e 1584 for i in ifaceobjs:
62ddec8b 1585 if i.auto:
0532f0a3 1586 ret = True
19e2bf8c
RP
1587 if not ret:
1588 # If auto was requested and interface was not marked auto,
1589 # only then mark all of them as blacklisted
1590 self.blacklisted_ifaces_present = True
1591 for i in ifaceobjs:
0532f0a3
RP
1592 i.blacklisted = True
1593 return ret
a6f80f0e 1594
53b00224 1595 def _compat_conv_op_to_mode(self, op):
1596 """ Returns old op name to work with existing scripts """
60dfcbdf 1597 if 'up' in op:
53b00224 1598 return 'start'
60dfcbdf 1599 elif 'down' in op:
53b00224 1600 return 'stop'
1601 else:
1602 return op
1603
a6f80f0e 1604 def generate_running_env(self, ifaceobj, op):
739f665b 1605 """ Generates a dictionary with env variables required for
1606 an interface. Used to support script execution for interfaces.
a6f80f0e 1607 """
1608
1609 cenv = None
60dfcbdf 1610 iface_env = ifaceobj.get_env()
62ddec8b 1611 if iface_env:
cad735c5 1612 cenv = dict(os.environ)
d08d5f54 1613 if cenv:
a6f80f0e 1614 cenv.update(iface_env)
1615 else:
1616 cenv = iface_env
60dfcbdf
RP
1617 else:
1618 cenv = {}
1619 cenv['MODE'] = self._compat_conv_op_to_mode(op)
1620 cenv['PHASE'] = op
1621
a6f80f0e 1622 return cenv
1623
53b00224 1624 def _save_state(self):
dbc018d3
RP
1625 if (not self.flags.STATEMANAGER_ENABLE or
1626 not self.flags.STATEMANAGER_UPDATE):
20dd6242 1627 return
1628 try:
1629 # Update persistant iface states
1630 self.statemanager.save_state()
3b01ed76 1631 except Exception as e:
20dd6242 1632 if self.logger.isEnabledFor(logging.DEBUG):
1633 t = sys.exc_info()[2]
1634 traceback.print_tb(t)
1635 self.logger.warning('error saving state (%s)' %str(e))
1636
2da58137
RP
1637 def set_type(self, type):
1638 if type == 'iface':
1639 self.type = ifaceType.IFACE
1640 elif type == 'vlan':
1641 self.type = ifaceType.BRIDGE_VLAN
1642 else:
1643 self.type = ifaceType.UNKNOWN
1644
a070c90e
RP
1645 def _process_delay_admin_state_queue(self, op):
1646 if not self._delay_admin_state_iface_queue:
1647 return
1648 if op == 'up':
223ba5af 1649 func = self.netlink.link_up
a070c90e 1650 elif op == 'down':
223ba5af 1651 func = self.netlink.link_down
a070c90e
RP
1652 else:
1653 return
1654 for i in self._delay_admin_state_iface_queue:
1655 try:
1656 if self.link_exists(i):
1657 func(i)
3b01ed76 1658 except Exception as e:
a070c90e
RP
1659 self.logger.warn(str(e))
1660 pass
1661
223ba5af
JF
1662 def _get_iface_exclude_companion(self, ifacename):
1663 try:
1664 return ifupdown2.ifupdown.policymanager.policymanager_api.get_iface_default(
1665 module_name='main', ifname=ifacename,
1666 attr='exclude-companion')
1667 except:
1668 return ifupdown.policymanager.policymanager_api.get_iface_default(
1669 module_name='main', ifname=ifacename,
1670 attr='exclude-companion')
1671
1672 def _preprocess_excludepats(self, excludepats):
1673 new_excludepats = excludepats
1674 for e in excludepats:
1675 ifaceobjs = self.get_ifaceobjs(e)
1676 for iobj in ifaceobjs or []:
1677 ec = iobj.get_attr_value_first('exclude-companion')
1678 if not ec:
1679 ec = self._get_iface_exclude_companion(e)
1680 if not ec:
1681 continue
1682 else:
1683 ec = ec.encode('ascii','ignore')
1684 for ee in ec.split():
1685 if ee in new_excludepats:
1686 continue
1687 if self.get_ifaceobjs(ee):
1688 # if we know the object add it to the new
1689 # excludepats list
1690 new_excludepats.append(ee)
1691 self.logger.info('excludepats after processing companions [%s]' %' '.join(new_excludepats))
1692 return new_excludepats
1693
d08d5f54 1694 def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
2da58137 1695 excludepats=None, printdependency=None, syntaxcheck=False,
ad25e7bb 1696 type=None, skipupperifaces=False):
2c0ad8b3 1697 """This brings the interface(s) up
d486dd0d 1698
2c0ad8b3 1699 Args:
41febf89
RP
1700 ops (list): list of ops to perform on the interface(s).
1701 Eg: ['pre-up', 'up', 'post-up'
2c0ad8b3
RP
1702
1703 Kwargs:
1704 auto (bool): act on interfaces marked auto
1705 allow_classes (list): act on interfaces belonging to classes in the list
1706 ifacenames (list): act on interfaces specified in this list
1707 excludepats (list): list of patterns of interfaces to exclude
1708 syntaxcheck (bool): only perform syntax check
1709 """
53b00224 1710
2da58137
RP
1711 self.set_type(type)
1712
5ee3e1a8 1713 if allow_classes:
2a8440a4 1714 ifupdownflags.flags.CLASS = True
dbc018d3
RP
1715 if not self.flags.ADDONS_ENABLE:
1716 self.flags.STATEMANAGER_UPDATE = False
d08d5f54 1717 if auto:
d2b35716
RP
1718 ifupdownflags.flags.ALL = True
1719 ifupdownflags.flags.WITH_DEPENDS = True
d08d5f54 1720 try:
cfa06db6 1721 iface_read_ret = self.read_iface_config()
62ddec8b 1722 except Exception:
d08d5f54 1723 raise
a6f80f0e 1724
223ba5af
JF
1725 if excludepats:
1726 excludepats = self._preprocess_excludepats(excludepats)
1727
d486dd0d 1728 filtered_ifacenames = None
31a5f4c3 1729 if ifacenames:
41febf89 1730 ifacenames = self._preprocess_ifacenames(ifacenames)
a6f80f0e 1731
d486dd0d
JF
1732 if allow_classes:
1733 filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
1734
a6f80f0e 1735 # if iface list not given by user, assume all from config file
3b01ed76 1736 if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
a6f80f0e 1737
d486dd0d
JF
1738 if not filtered_ifacenames:
1739 # filter interfaces based on auto and allow classes
1740 filtered_ifacenames = [i for i in ifacenames
1741 if self._iface_whitelisted(auto, allow_classes,
d08d5f54 1742 excludepats, i)]
d486dd0d 1743
fe0a57d3 1744 if not filtered_ifacenames:
d08d5f54 1745 raise Exception('no ifaces found matching given allow lists')
a6f80f0e 1746
20dd6242 1747 if printdependency:
c798b0f4 1748 self.populate_dependency_info(ops, filtered_ifacenames)
d08d5f54 1749 self.print_dependency(filtered_ifacenames, printdependency)
739f665b 1750 return
cca03c30 1751 else:
c798b0f4 1752 self.populate_dependency_info(ops)
1753
cfa06db6
RP
1754 # If only syntax check was requested, return here.
1755 # return here because we want to make sure most
1756 # errors above are caught and reported.
21289e4a 1757 if syntaxcheck:
482b2fab
JF
1758 if not self._module_syntax_check(filtered_ifacenames):
1759 raise Exception()
cfa06db6
RP
1760 if not iface_read_ret:
1761 raise Exception()
666c6141
RP
1762 elif self._any_iface_errors(filtered_ifacenames):
1763 raise Exception()
21289e4a
RP
1764 return
1765
24aa45e5 1766 ret = None
525f0a30 1767 try:
2009513f
RP
1768 ret = self._sched_ifaces(filtered_ifacenames, ops,
1769 skipupperifaces=skipupperifaces,
1770 followdependents=True
d2b35716
RP
1771 if ifupdownflags.flags.WITH_DEPENDS
1772 else False)
525f0a30 1773 finally:
a070c90e 1774 self._process_delay_admin_state_queue('up')
fc5e1735 1775 if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
525f0a30 1776 self._save_state()
a6f80f0e 1777
2009513f
RP
1778 if not iface_read_ret or not ret:
1779 raise Exception()
1780
223ba5af
JF
1781 self.check_running_configuration(filtered_ifacenames)
1782
1783 def check_running_configuration(self, filtered_ifacenames, all=False):
1784 """
1785 Print warning and better info message when we don't recognize an interface
1786 AKA when the interface wasn't created.
1787
1788 :param filtered_ifacenames:
1789 :param all:
1790 :return:
1791 """
1792 if not filtered_ifacenames:
1793 filtered_ifacenames = []
1794
1795 for ifname, ifaceobj_list in self.ifaceobjdict.iteritems():
1796
1797 if not all and ifname not in filtered_ifacenames:
1798 continue
1799
1800 auto = True
1801
1802 for ifaceobj in ifaceobj_list:
1803 if not ifaceobj.auto:
1804 auto = False
1805 break
1806
1807 if auto and not os.path.exists("/sys/class/net/%s" % ifname):
1808 self.logger.warning("%s: interface not recognized - please check interface configuration" % ifname)
1809
d486dd0d
JF
1810 def _get_filtered_ifacenames_with_classes(self, auto, allow_classes, excludepats, ifacenames):
1811 # if user has specified ifacelist and allow_classes
1812 # append the allow_classes interfaces to user
1813 # ifacelist
3b01ed76 1814 filtered_ifacenames = [i for i in list(self.ifaceobjdict.keys())
d486dd0d
JF
1815 if self._iface_whitelisted(auto, allow_classes,
1816 excludepats, i)]
1817 filtered_ifacenames += ifacenames
1818
1819 for intf in ifacenames:
1820 for obj in self.get_ifaceobjs(intf) or []:
1821 obj.blacklisted = False
1822
1823 return filtered_ifacenames
1824
d08d5f54 1825 def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
2da58137
RP
1826 excludepats=None, printdependency=None, usecurrentconfig=False,
1827 type=None):
53b00224 1828 """ down an interface """
1829
2da58137
RP
1830 self.set_type(type)
1831
5ee3e1a8 1832 if allow_classes:
2a8440a4 1833 ifupdownflags.flags.CLASS = True
dbc018d3
RP
1834 if not self.flags.ADDONS_ENABLE:
1835 self.flags.STATEMANAGER_UPDATE = False
d08d5f54 1836 if auto:
d2b35716
RP
1837 ifupdownflags.flags.ALL = True
1838 ifupdownflags.flags.WITH_DEPENDS = True
5c721925 1839 # For down we need to look at old state, unless usecurrentconfig
1840 # is set
dbc018d3 1841 if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE and
53b00224 1842 self.statemanager.ifaceobjdict):
31a5f4c3 1843 # Since we are using state manager objects,
1844 # skip the updating of state manager objects
5c721925 1845 self.logger.debug('Looking at old state ..')
d08d5f54 1846 self.read_old_iface_config()
fe0a57d3 1847 else:
d486dd0d 1848 # If no old state available
d08d5f54 1849 try:
1850 self.read_iface_config()
3b01ed76 1851 except Exception as e:
d08d5f54 1852 raise Exception('error reading iface config (%s)' %str(e))
223ba5af
JF
1853
1854 if excludepats:
1855 excludepats = self._preprocess_excludepats(excludepats)
1856
d486dd0d 1857 filtered_ifacenames = None
d08d5f54 1858 if ifacenames:
1859 # If iface list is given by the caller, always check if iface
1860 # is present
31c58787 1861 try:
41febf89 1862 ifacenames = self._preprocess_ifacenames(ifacenames)
d486dd0d
JF
1863
1864 if allow_classes:
1865 filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
1866
3b01ed76 1867 except Exception as e:
31c58787 1868 raise Exception('%s' %str(e) +
1869 ' (interface was probably never up ?)')
1870
d486dd0d 1871
d08d5f54 1872 # if iface list not given by user, assume all from config file
3b01ed76 1873 if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
53b00224 1874
d486dd0d
JF
1875 if not filtered_ifacenames:
1876 # filter interfaces based on auto and allow classes
1877 filtered_ifacenames = [i for i in ifacenames
1878 if self._iface_whitelisted(auto, allow_classes,
1879 excludepats, i)]
1880
fe0a57d3 1881 if not filtered_ifacenames:
c0071225 1882 raise Exception('no ifaces found matching given allow lists ' +
41febf89 1883 '(or interfaces were probably never up ?)')
14dc390d 1884
d08d5f54 1885 if printdependency:
99b212b0 1886 self.populate_dependency_info(ops, filtered_ifacenames)
d08d5f54 1887 self.print_dependency(filtered_ifacenames, printdependency)
1888 return
99b212b0 1889 else:
1890 self.populate_dependency_info(ops)
525f0a30 1891
1892 try:
a4912b99 1893 self._sched_ifaces(filtered_ifacenames, ops,
dbc018d3 1894 followdependents=True
d2b35716 1895 if ifupdownflags.flags.WITH_DEPENDS else False)
525f0a30 1896 finally:
a070c90e 1897 self._process_delay_admin_state_queue('down')
fc5e1735 1898 if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
525f0a30 1899 self._save_state()
d08d5f54 1900
4c56a7c1
RP
1901 def query(self, ops, auto=False, format_list=False, allow_classes=None,
1902 ifacenames=None,
739f665b 1903 excludepats=None, printdependency=None,
6e16e5ae 1904 format='native', type=None):
53b00224 1905 """ query an interface """
1906
2da58137
RP
1907 self.set_type(type)
1908
d486dd0d 1909 # Let us forget internal squashing when it comes to
2ddd65c5
RP
1910 # ifquery. It can surprise people relying of ifquery
1911 # output
1912 self._ifaceobj_squash_internal = False
1913
5ee3e1a8 1914 if allow_classes:
2a8440a4 1915 ifupdownflags.flags.CLASS = True
dbc018d3 1916 if self.flags.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
5c721925 1917 return self.statemanager.dump_pretty(ifacenames)
dbc018d3 1918 self.flags.STATEMANAGER_UPDATE = False
89f5bbe5
JF
1919
1920 iface_read_ret = True
1921
d08d5f54 1922 if auto:
739f665b 1923 self.logger.debug('setting flag ALL')
d2b35716
RP
1924 ifupdownflags.flags.ALL = True
1925 ifupdownflags.flags.WITH_DEPENDS = True
739f665b 1926
d08d5f54 1927 if ops[0] == 'query-syntax':
d486dd0d 1928 self._modules_help(format)
d08d5f54 1929 return
1930 elif ops[0] == 'query-running':
739f665b 1931 # create fake devices to all dependents that dont have config
7f208e56
JF
1932 for i in ifacenames:
1933 self.create_n_save_ifaceobj(i, ifacePrivFlags(False, True))
739f665b 1934 else:
1935 try:
223ba5af 1936 iface_read_ret = self.read_iface_config(raw=ops[0] == "query-raw")
739f665b 1937 except Exception:
1938 raise
1939
53b00224 1940 if ifacenames and ops[0] != 'query-running':
d486dd0d
JF
1941 # If iface list is given, always check if iface is present
1942 ifacenames = self._preprocess_ifacenames(ifacenames)
1943
1944 if allow_classes:
1945 filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
739f665b 1946
1947 # if iface list not given by user, assume all from config file
3b01ed76 1948 if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
739f665b 1949
1950 # filter interfaces based on auto and allow classes
d08d5f54 1951 if ops[0] == 'query-running':
739f665b 1952 filtered_ifacenames = ifacenames
d486dd0d
JF
1953 elif not allow_classes:
1954 filtered_ifacenames = [
1955 i for i in ifacenames
1956 if self._iface_whitelisted(
1957 auto,
1958 allow_classes,
1959 excludepats, i
1960 )
1961 ]
1962
fe0a57d3 1963 if not filtered_ifacenames:
739f665b 1964 raise Exception('no ifaces found matching ' +
1965 'given allow lists')
37c0543d 1966
e1601369 1967 self.populate_dependency_info(ops)
d08d5f54 1968 if ops[0] == 'query-dependency' and printdependency:
1969 self.print_dependency(filtered_ifacenames, printdependency)
1970 return
739f665b 1971
4c56a7c1
RP
1972 if format_list and (ops[0] == 'query' or ops[0] == 'query-raw'):
1973 return self.print_ifaceobjs_list(filtered_ifacenames)
1974
634764bd 1975 if ops[0] == 'query' and not ifupdownflags.flags.WITHDEFAULTS:
75730152 1976 return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
1977 elif ops[0] == 'query-raw':
223ba5af 1978 return self.print_ifaceobjs_raw(filtered_ifacenames, format)
75730152 1979
6e16e5ae 1980 ret = self._sched_ifaces(filtered_ifacenames, ops,
d486dd0d 1981 followdependents=True
d2b35716 1982 if ifupdownflags.flags.WITH_DEPENDS else False)
739f665b 1983
634764bd
RP
1984 if ops[0] == 'query' and ifupdownflags.flags.WITHDEFAULTS:
1985 return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
1986 elif ops[0] == 'query-checkcurr':
89f5bbe5 1987 if self.print_ifaceobjscurr_pretty(filtered_ifacenames, format):
739f665b 1988 # if any of the object has an error, signal that silently
1989 raise Exception('')
d08d5f54 1990 elif ops[0] == 'query-running':
1991 self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
739f665b 1992 return
1993
89f5bbe5
JF
1994 if not iface_read_ret or not ret:
1995 raise Exception()
1996
4e07a2d5 1997 def _reload_currentlyup(self, upops, downops, auto=False, allow=None,
2da58137 1998 ifacenames=None, excludepats=None, usecurrentconfig=False,
cfa06db6 1999 syntaxcheck=False, **extra_args):
2da58137 2000 """ reload currently up interfaces """
c0071225 2001 new_ifaceobjdict = {}
739f665b 2002
4e07a2d5
RP
2003 self.logger.info('reloading interfaces that are currently up ..')
2004
2da58137 2005 try:
cfa06db6 2006 iface_read_ret = self.read_iface_config()
2da58137
RP
2007 except:
2008 raise
2da58137
RP
2009 if not self.ifaceobjdict:
2010 self.logger.warn("nothing to reload ..exiting.")
2011 return
2da58137 2012 already_up_ifacenames = []
3b01ed76 2013 if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
cfa06db6 2014
dbc018d3 2015 if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
2da58137 2016 and self.statemanager.ifaceobjdict):
3b01ed76 2017 already_up_ifacenames = list(self.statemanager.ifaceobjdict.keys())
2da58137 2018
2da58137
RP
2019 # Get already up interfaces that still exist in the interfaces file
2020 already_up_ifacenames_not_present = Set(
2021 already_up_ifacenames).difference(ifacenames)
2022 already_up_ifacenames_still_present = Set(
2023 already_up_ifacenames).difference(
2024 already_up_ifacenames_not_present)
4e07a2d5
RP
2025
2026 interfaces_to_up = already_up_ifacenames_still_present
2027
2028 # generate dependency graph of interfaces
2029 self.populate_dependency_info(upops, interfaces_to_up)
2030
2031 # If only syntax check was requested, return here.
2032 # return here because we want to make sure most
2033 # errors above are caught and reported.
2034 if syntaxcheck:
482b2fab
JF
2035 if not self._module_syntax_check(interfaces_to_up):
2036 raise Exception()
4e07a2d5
RP
2037 if not iface_read_ret:
2038 raise Exception()
2039 elif self._any_iface_errors(interfaces_to_up):
2040 raise Exception()
2041 return
2da58137
RP
2042
2043 if (already_up_ifacenames_not_present and
2044 self.config.get('ifreload_currentlyup_down_notpresent') == '1'):
2045 self.logger.info('reload: schedule down on interfaces: %s'
2046 %str(already_up_ifacenames_not_present))
2047
2048 # Save a copy of new iface objects and dependency_graph
2049 new_ifaceobjdict = dict(self.ifaceobjdict)
2050 new_dependency_graph = dict(self.dependency_graph)
2051
2052 # old interface config is read into self.ifaceobjdict
2053 self.read_old_iface_config()
2054
d486dd0d 2055 # reinitialize dependency graph
2da58137 2056 self.dependency_graph = OrderedDict({})
1042b709
RP
2057 falready_up_ifacenames_not_present = [i for i in
2058 already_up_ifacenames_not_present
2059 if self._iface_whitelisted(auto, allow,
2060 excludepats, i)]
2da58137 2061 self.populate_dependency_info(downops,
1042b709
RP
2062 falready_up_ifacenames_not_present)
2063 self._sched_ifaces(falready_up_ifacenames_not_present, downops,
e308cb82 2064 followdependents=False, sort=True)
2da58137 2065 else:
4e07a2d5 2066 self.logger.info('no interfaces to down ..')
2da58137
RP
2067
2068 # Now, run 'up' with new config dict
2069 # reset statemanager update flag to default
e308cb82 2070 if auto:
d2b35716
RP
2071 ifupdownflags.flags.ALL = True
2072 ifupdownflags.flags.WITH_DEPENDS = True
2da58137 2073 if new_ifaceobjdict:
3d44fbd0 2074 # and now, ifaceobjdict is back to current config
2da58137
RP
2075 self.ifaceobjdict = new_ifaceobjdict
2076 self.dependency_graph = new_dependency_graph
2077
2078 if not self.ifaceobjdict:
4e07a2d5
RP
2079 self.logger.info('no interfaces to up')
2080 return
2da58137
RP
2081 self.logger.info('reload: scheduling up on interfaces: %s'
2082 %str(interfaces_to_up))
2009513f
RP
2083 ret = self._sched_ifaces(interfaces_to_up, upops,
2084 followdependents=True
d2b35716 2085 if ifupdownflags.flags.WITH_DEPENDS else False)
fc5e1735 2086 if ifupdownflags.flags.DRYRUN:
2da58137
RP
2087 return
2088 self._save_state()
2089
2009513f
RP
2090 if not iface_read_ret or not ret:
2091 raise Exception()
2092
2da58137
RP
2093 def _reload_default(self, upops, downops, auto=False, allow=None,
2094 ifacenames=None, excludepats=None, usecurrentconfig=False,
cfa06db6 2095 syntaxcheck=False, **extra_args):
2da58137 2096 """ reload interface config """
2da58137 2097 new_ifaceobjdict = {}
739f665b 2098
2099 try:
cfa06db6 2100 iface_read_ret = self.read_iface_config()
20dd6242 2101 except:
739f665b 2102 raise
2103
c0071225
RP
2104 if not self.ifaceobjdict:
2105 self.logger.warn("nothing to reload ..exiting.")
2106 return
1042b709 2107
3b01ed76 2108 if not ifacenames: ifacenames = list(self.ifaceobjdict.keys())
1042b709
RP
2109 new_filtered_ifacenames = [i for i in ifacenames
2110 if self._iface_whitelisted(auto, allow,
2111 excludepats, i)]
37c0543d 2112 # generate dependency graph of interfaces
c798b0f4 2113 self.populate_dependency_info(upops)
1042b709 2114
cfa06db6
RP
2115 # If only syntax check was requested, return here.
2116 # return here because we want to make sure most
2117 # errors above are caught and reported.
2118 if syntaxcheck:
482b2fab
JF
2119 if not self._module_syntax_check(new_filtered_ifacenames):
2120 raise Exception()
cfa06db6
RP
2121 if not iface_read_ret:
2122 raise Exception()
4e07a2d5
RP
2123 elif self._any_iface_errors(new_filtered_ifacenames):
2124 raise Exception()
cfa06db6
RP
2125 return
2126
dbc018d3 2127 if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
14dc390d 2128 and self.statemanager.ifaceobjdict):
2129 # Save a copy of new iface objects and dependency_graph
2130 new_ifaceobjdict = dict(self.ifaceobjdict)
2131 new_dependency_graph = dict(self.dependency_graph)
37c0543d 2132
dbc018d3
RP
2133 self.ifaceobjdict = OrderedDict({})
2134 self.dependency_graph = OrderedDict({})
2135
739f665b 2136 # if old state is present, read old state and mark op for 'down'
2137 # followed by 'up' aka: reload
37c0543d 2138 # old interface config is read into self.ifaceobjdict
739f665b 2139 self.read_old_iface_config()
2140 op = 'reload'
2141 else:
2142 # oldconfig not available, continue with 'up' with new config
2143 op = 'up'
05ac52f0
RP
2144 new_ifaceobjdict = self.ifaceobjdict
2145 new_dependency_graph = self.dependency_graph
739f665b 2146
223ba5af
JF
2147 if excludepats:
2148 excludepats = self._preprocess_excludepats(excludepats)
2149
fe0a57d3 2150 if op == 'reload' and ifacenames:
3b01ed76 2151 ifacenames = list(self.ifaceobjdict.keys())
1042b709
RP
2152 old_filtered_ifacenames = [i for i in ifacenames
2153 if self._iface_whitelisted(auto, allow,
d08d5f54 2154 excludepats, i)]
e308cb82 2155
dbc018d3
RP
2156 # generate dependency graph of old interfaces,
2157 # This should make sure built in interfaces are
2158 # populated. disable check shared dependents as an optimization.
2159 # these are saved interfaces and dependency for these
2160 # have been checked before they became part of saved state.
62f2caa4 2161 try:
d486dd0d 2162 self.flags.CHECK_SHARED_DEPENDENTS = False
62f2caa4
RP
2163 self.populate_dependency_info(upops)
2164 self.flags.CHECK_SHARED_DEPENDENTS = True
3b01ed76 2165 except Exception as e:
62f2caa4
RP
2166 self.logger.info("error generating dependency graph for "
2167 "saved interfaces (%s)" %str(e))
2168 pass
d486dd0d 2169
dbc018d3 2170 # make sure we pick up built-in interfaces
e308cb82
RP
2171 # if config file had 'ifreload_down_changed' variable
2172 # set, also look for interfaces that changed to down them
2173 down_changed = int(self.config.get('ifreload_down_changed', '1'))
2174
37c0543d 2175 # Generate the interface down list
2176 # Interfaces that go into the down list:
2177 # - interfaces that were present in last config and are not
2178 # present in the new config
2179 # - interfaces that were changed between the last and current
2180 # config
37c0543d 2181 ifacedownlist = []
3b01ed76 2182 for ifname in list(self.ifaceobjdict.keys()):
2da58137 2183 lastifaceobjlist = self.ifaceobjdict.get(ifname)
dbc018d3
RP
2184 if not self.is_ifaceobj_builtin(lastifaceobjlist[0]):
2185 # if interface is not built-in and is not in
2186 # old filtered ifacenames
2187 if ifname not in old_filtered_ifacenames:
2188 continue
37c0543d 2189 objidx = 0
37c0543d 2190 # If interface is not present in the new file
2191 # append it to the down list
53b00224 2192 newifaceobjlist = new_ifaceobjdict.get(ifname)
2193 if not newifaceobjlist:
37c0543d 2194 ifacedownlist.append(ifname)
2195 continue
21289e4a
RP
2196 # If ifaceobj was present in the old interfaces file,
2197 # and does not have a config in the new interfaces file
2198 # but has been picked up as a dependent of another
2199 # interface, catch it here. This catches a common error
2200 # for example: remove a bond section from the interfaces
2201 # file, but leave it around as a bridge port
2202 # XXX: Ideally its better to just add it to the
d486dd0d 2203 # ifacedownlist. But we will be cautious here
21289e4a
RP
2204 # and just print a warning
2205 if (self.is_ifaceobj_noconfig(newifaceobjlist[0]) and
dbc018d3 2206 not self.is_ifaceobj_builtin(newifaceobjlist[0]) and
7ef04d1b
RP
2207 lastifaceobjlist[0].is_config_present() and
2208 lastifaceobjlist[0].link_kind):
5e30d3b5
JF
2209
2210 # Check if interface is picked up by a regex in the upperifaces.
2211 print_warning = True
2212
2213 for upper in newifaceobjlist[objidx].upperifaces or []:
2214 slaves = []
2215 for upper_ifaceobj in self.ifaceobjdict.get(upper):
2216 slaves.extend(upper_ifaceobj.get_attr_value("bond-slaves") or [])
2217 slaves.extend(upper_ifaceobj.get_attr_value("bridge-ports") or [])
2218 slaves_string = " ".join(slaves)
2219 if newifaceobjlist[objidx].name not in slaves_string:
2220 print_warning = "regex" not in slaves_string
2221 if not print_warning:
2222 break
2223 ###############################################################
2224
2225 warning_no_config_regex = (
2226 "%s: misconfig ? removed but still exists as a dependency of %s.\n"
2227 "Please remove the dependency manually `ifdown %s` if it is being "
2228 "picked up as part of a regex" % (
2229 newifaceobjlist[objidx].name,
2230 str(newifaceobjlist[objidx].upperifaces),
2231 newifaceobjlist[objidx].name
2232 )
2233 )
2234
2235 if print_warning:
2236 self.logger.warn(warning_no_config_regex)
2237 else:
2238 # The warning shouldn't be printed because we've detected that this
2239 # interface was pick up as part of a regex but the config doesn't
2240 # exist anymore. It was most likely removed from the config file itself
2241 # We should down this interface and remove it from the ifaceobjdict
2242 # and dependency graph used for the following ifreload.
2243 ifname_to_remove = newifaceobjlist[objidx].name
2244 ifacedownlist.append(ifname_to_remove)
2245
2246 try:
2247 if new_ifaceobjdict:
2248 del new_ifaceobjdict[ifname_to_remove]
2249
3b01ed76 2250 for k, v in new_dependency_graph.items():
5e30d3b5
JF
2251 if ifname_to_remove in v:
2252 v.remove(ifname_to_remove)
2253 del new_dependency_graph[ifname_to_remove]
2254 except Exception as e:
2255 self.logger.warning(warning_no_config_regex)
2256 self.logger.warning("while trying to fix this situation "
2257 "we ran into the following issues: %s" % str(e))
2258
2259 elif (lastifaceobjlist[0].link_kind and
d486dd0d
JF
2260 not newifaceobjlist[0].link_kind):
2261 self.logger.warn('%s: moved from being a %s to a'
2262 ' physical interface (non-logical interface).'
2263 'This interface will be downed.\n'
2264 ' If this was not intentional, please restore the'
2265 ' original interface definition and execute ifreload'
2266 % (newifaceobjlist[objidx].name,
2267 ifaceLinkKind.to_str(lastifaceobjlist[0].link_kind)))
2268 ifacedownlist.append(newifaceobjlist[objidx].name)
26434da6
RP
2269 if not down_changed:
2270 continue
53b00224 2271 if len(newifaceobjlist) != len(lastifaceobjlist):
37c0543d 2272 ifacedownlist.append(ifname)
2273 continue
e308cb82
RP
2274
2275 # If interface has changed between the current file
2276 # and the last installed append it to the down list
37c0543d 2277 # compare object list
53b00224 2278 for objidx in range(0, len(lastifaceobjlist)):
2279 oldobj = lastifaceobjlist[objidx]
2280 newobj = newifaceobjlist[objidx]
ca3f4fc7 2281 if not newobj.compare(oldobj):
37c0543d 2282 ifacedownlist.append(ifname)
2283 continue
2284
fe0a57d3 2285 if ifacedownlist:
2da58137 2286 self.logger.info('reload: scheduling down on interfaces: %s'
739f665b 2287 %str(ifacedownlist))
d486dd0d 2288 # reinitialize dependency graph
20dd6242 2289 self.dependency_graph = OrderedDict({})
dbc018d3 2290
37c0543d 2291 # Generate dependency info for old config
62f2caa4 2292 self.flags.CHECK_SHARED_DEPENDENTS = False
14dc390d 2293 self.populate_dependency_info(downops, ifacedownlist)
62f2caa4
RP
2294 self.flags.CHECK_SHARED_DEPENDENTS = True
2295
a070c90e 2296 try:
dbc018d3
RP
2297 # XXX: Hack to skip checking upperifaces during down.
2298 # the dependency list is not complete here
2299 # and we dont want to down the upperiface.
2300 # Hence during reload, set this to true.
2301 # This is being added to avoid a failure in
2302 # scheduler._check_upperifaces when we are dowing
d486dd0d 2303 # a builtin bridge port
dbc018d3 2304 self.flags.SCHED_SKIP_CHECK_UPPERIFACES = True
a4912b99 2305 self._sched_ifaces(ifacedownlist, downops,
e308cb82
RP
2306 followdependents=False,
2307 sort=True)
3b01ed76 2308 except Exception as e:
a070c90e
RP
2309 self.logger.error(str(e))
2310 pass
2311 finally:
dbc018d3 2312 self.flags.SCHED_SKIP_CHECK_UPPERIFACES = False
a070c90e 2313 self._process_delay_admin_state_queue('down')
37c0543d 2314 else:
4e07a2d5 2315 self.logger.info('no interfaces to down ..')
739f665b 2316
20dd6242 2317 # Now, run 'up' with new config dict
2318 # reset statemanager update flag to default
c0071225 2319 if not new_ifaceobjdict:
aa4e3022 2320 self.logger.debug('no interfaces to up')
c0071225 2321 return
e308cb82
RP
2322
2323 if auto:
d2b35716
RP
2324 ifupdownflags.flags.ALL = True
2325 ifupdownflags.flags.WITH_DEPENDS = True
3d44fbd0 2326 # and now, we are back to the current config in ifaceobjdict
53b00224 2327 self.ifaceobjdict = new_ifaceobjdict
2328 self.dependency_graph = new_dependency_graph
c798b0f4 2329
2da58137 2330 self.logger.info('reload: scheduling up on interfaces: %s'
1042b709 2331 %str(new_filtered_ifacenames))
8a360f1b 2332 ifupdownflags.flags.CACHE = True
a070c90e 2333 try:
2009513f
RP
2334 ret = self._sched_ifaces(new_filtered_ifacenames, upops,
2335 followdependents=True
d2b35716
RP
2336 if ifupdownflags.flags.WITH_DEPENDS
2337 else False)
3b01ed76 2338 except Exception as e:
24aa45e5 2339 ret = None
a070c90e 2340 self.logger.error(str(e))
a070c90e
RP
2341 finally:
2342 self._process_delay_admin_state_queue('up')
fc5e1735 2343 if ifupdownflags.flags.DRYRUN:
e37ad4a6 2344 return
53b00224 2345 self._save_state()
a6f80f0e 2346
2009513f
RP
2347 if not iface_read_ret or not ret:
2348 raise Exception()
2349
2da58137
RP
2350 def reload(self, *args, **kargs):
2351 """ reload interface config """
2da58137
RP
2352 self.logger.debug('reloading interface config ..')
2353 if kargs.get('currentlyup', False):
2354 self._reload_currentlyup(*args, **kargs)
2355 else:
2356 self._reload_default(*args, **kargs)
2357
223ba5af
JF
2358 self.check_running_configuration([], all=True)
2359
666c6141
RP
2360 def _any_iface_errors(self, ifacenames):
2361 for i in ifacenames:
2362 ifaceobjs = self.get_ifaceobjs(i)
2363 if not ifaceobjs: continue
2364 for ifaceobj in ifaceobjs:
2365 if (ifaceobj.status == ifaceStatus.NOTFOUND or
2366 ifaceobj.status == ifaceStatus.ERROR):
2367 return True
2368 return False
2369
7538dc77 2370 def _pretty_print_ordered_dict(self, prefix, argdict):
525f0a30 2371 outbuf = prefix + ' {\n'
3b01ed76 2372 for k, vlist in list(argdict.items()):
525f0a30 2373 outbuf += '\t%s : %s\n' %(k, str(vlist))
7538dc77 2374 self.logger.debug(outbuf + '}')
a6f80f0e 2375
53b00224 2376 def print_dependency(self, ifacenames, format):
2377 """ prints iface dependency information """
a6f80f0e 2378
53b00224 2379 if not ifacenames:
3b01ed76 2380 ifacenames = list(self.ifaceobjdict.keys())
53b00224 2381 if format == 'list':
3b01ed76
JF
2382 for k,v in list(self.dependency_graph.items()):
2383 print('%s : %s' %(k, str(v)))
53b00224 2384 elif format == 'dot':
2385 indegrees = {}
7f208e56
JF
2386 for i in list(self.dependency_graph.keys()):
2387 indegrees.update({i: self.get_iface_refcnt(i)})
53b00224 2388 graph.generate_dots(self.dependency_graph, indegrees)
a6f80f0e 2389
4c56a7c1
RP
2390 def print_ifaceobjs_list(self, ifacenames):
2391 for i in ifacenames:
3b01ed76 2392 print(i)
4c56a7c1 2393
223ba5af 2394 def print_ifaceobjs_raw(self, ifacenames, format=None):
53b00224 2395 """ prints raw lines for ifaces from config file """
2396
223ba5af
JF
2397 if format == "json":
2398 self.print_ifaceobjs_pretty(ifacenames, format)
2399 return
2400
739f665b 2401 for i in ifacenames:
31a5f4c3 2402 for ifaceobj in self.get_ifaceobjs(i):
d486dd0d 2403 if self.is_ifaceobj_builtin(ifaceobj):
75730152 2404 continue
2405 ifaceobj.dump_raw(self.logger)
d2b35716
RP
2406 if (ifupdownflags.flags.WITH_DEPENDS and
2407 not ifupdownflags.flags.ALL):
62ddec8b 2408 dlist = ifaceobj.lowerifaces
fe0a57d3 2409 if not dlist: continue
53b00224 2410 self.print_ifaceobjs_raw(dlist)
739f665b 2411
2cd06f78 2412 def _get_ifaceobjs_pretty(self, ifacenames, ifaceobjs, running=False):
2413 """ returns iface obj list """
53b00224 2414
a6f80f0e 2415 for i in ifacenames:
31a5f4c3 2416 for ifaceobj in self.get_ifaceobjs(i):
2cd06f78 2417 if ((not running and self.is_ifaceobj_noconfig(ifaceobj)) or
f78ba1de
RP
2418 (running and not ifaceobj.is_config_present() and
2419 not self.is_iface_builtin_byname(i) and
2420 not ifaceobj.upperifaces)):
75730152 2421 continue
2cd06f78 2422 ifaceobjs.append(ifaceobj)
d2b35716
RP
2423 if (ifupdownflags.flags.WITH_DEPENDS and
2424 not ifupdownflags.flags.ALL):
62ddec8b 2425 dlist = ifaceobj.lowerifaces
fe0a57d3 2426 if not dlist: continue
2cd06f78 2427 self._get_ifaceobjs_pretty(dlist, ifaceobjs, running)
739f665b 2428
2cd06f78 2429 def print_ifaceobjs_pretty(self, ifacenames, format='native'):
2430 """ pretty prints iface in format given by keyword arg format """
eab25b7c 2431
2cd06f78 2432 ifaceobjs = []
2433 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs)
2434 if not ifaceobjs: return
2435 if format == 'json':
3b01ed76
JF
2436 print(json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
2437 indent=4, separators=(',', ': ')))
2cd06f78 2438 else:
2da58137
RP
2439 expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
2440 for i in ifaceobjs:
2441 if not expand and (i.flags & iface.IFACERANGE_ENTRY):
2442 # print only the first one
2443 if i.flags & iface.IFACERANGE_START:
2444 i.dump_pretty(use_realname=True)
2445 else:
2446 i.dump_pretty()
53b00224 2447
2cd06f78 2448 def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
eab25b7c 2449 ret = 0
a6f80f0e 2450 for i in ifacenames:
923290bd 2451 ifaceobjscurr = self.get_ifaceobjcurr(i)
2452 if not ifaceobjscurr: continue
2453 for ifaceobj in ifaceobjscurr:
2454 if (ifaceobj.status == ifaceStatus.NOTFOUND or
2455 ifaceobj.status == ifaceStatus.ERROR):
2456 ret = 1
2457 if self.is_ifaceobj_noconfig(ifaceobj):
2458 continue
2459 ifaceobjs.append(ifaceobj)
d2b35716
RP
2460 if (ifupdownflags.flags.WITH_DEPENDS and
2461 not ifupdownflags.flags.ALL):
923290bd 2462 dlist = ifaceobj.lowerifaces
2463 if not dlist: continue
2464 dret = self._get_ifaceobjscurr_pretty(dlist, ifaceobjs)
2465 if dret: ret = 1
2cd06f78 2466 return ret
2467
2468 def print_ifaceobjscurr_pretty(self, ifacenames, format='native'):
2469 """ pretty prints current running state of interfaces with status.
2470
2471 returns 1 if any of the interface has an error,
2472 else returns 0
2473 """
2cd06f78 2474 ifaceobjs = []
2475 ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
2476 if not ifaceobjs: return
307e06bb
RP
2477
2478 # override ifaceStatusUserStrs
2479 ifaceStatusUserStrs.SUCCESS = self.config.get('ifquery_check_success_str', _success_sym)
2480 ifaceStatusUserStrs.ERROR = self.config.get('ifquery_check_error_str', _error_sym)
2481 ifaceStatusUserStrs.UNKNOWN = self.config.get('ifquery_check_unknown_str', '')
2cd06f78 2482 if format == 'json':
3b01ed76
JF
2483 print(json.dumps(ifaceobjs, cls=ifaceJsonEncoderWithStatus,
2484 indent=2, separators=(',', ': ')))
2cd06f78 2485 else:
7f208e56
JF
2486 for ifaceobj in ifaceobjs:
2487 ifaceobj.dump_pretty(with_status=True)
eab25b7c 2488 return ret
a6f80f0e 2489
d08d5f54 2490 def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
53b00224 2491 """ pretty prints iface running state """
2492
2cd06f78 2493 ifaceobjs = []
2494 self._get_ifaceobjs_pretty(ifacenames, ifaceobjs, running=True)
2495 if not ifaceobjs: return
2496 if format == 'json':
3b01ed76
JF
2497 print(json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
2498 separators=(',', ': ')))
2cd06f78 2499 else:
7f208e56
JF
2500 for i in ifaceobjs:
2501 i.dump_pretty()
53b00224 2502
2503 def _dump(self):
3b01ed76
JF
2504 print('ifupdown main object dump')
2505 print(self.pp.pprint(self.modules))
2506 print(self.pp.pprint(self.ifaceobjdict))
53b00224 2507
2508 def _dump_ifaceobjs(self, ifacenames):
2509 for i in ifacenames:
2510 ifaceobjs = self.get_ifaceobjs(i)
2511 for i in ifaceobjs:
2512 i.dump(self.logger)
3b01ed76 2513 print('\n')