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