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