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