]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/bridge.py
addons: bridge: fix bridge-vlan-vni-map example in modinfo
[mirror_ifupdown2.git] / ifupdown2 / addons / bridge.py
CommitLineData
35681c06 1#!/usr/bin/env python3
d486dd0d
JF
2#
3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
7import re
8import time
9import itertools
10
d486dd0d
JF
11from collections import Counter
12
13try:
5f4ab659 14 from ifupdown2.lib.addon import Bridge
223ba5af 15
d486dd0d
JF
16 import ifupdown2.ifupdown.exceptions as exceptions
17 import ifupdown2.ifupdown.policymanager as policymanager
18 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
19
20 from ifupdown2.nlmanager.nlmanager import Link
21
22 from ifupdown2.ifupdown.iface import *
23 from ifupdown2.ifupdown.utils import utils
d486dd0d
JF
24
25 from ifupdown2.ifupdownaddons.cache import *
d486dd0d 26 from ifupdown2.ifupdownaddons.modulebase import moduleBase
bd441a51 27except (ImportError, ModuleNotFoundError):
5f4ab659 28 from lib.addon import Bridge
223ba5af 29
d486dd0d
JF
30 import ifupdown.exceptions as exceptions
31 import ifupdown.policymanager as policymanager
32 import ifupdown.ifupdownflags as ifupdownflags
33
34 from nlmanager.nlmanager import Link
35
36 from ifupdown.iface import *
37 from ifupdown.utils import utils
d486dd0d
JF
38
39 from ifupdownaddons.cache import *
d486dd0d
JF
40 from ifupdownaddons.modulebase import moduleBase
41
42
43class bridgeFlags:
44 PORT_PROCESSED = 0x1
45 PORT_PROCESSED_OVERRIDE = 0x2
46
47
5f4ab659 48class bridge(Bridge, moduleBase):
d486dd0d
JF
49 """ ifupdown2 addon module to configure linux bridges """
50
223ba5af
JF
51 _modinfo = {
52 "mhelp": "Bridge configuration module. Supports both vlan aware and non "
53 "vlan aware bridges. For the vlan aware bridge, the port "
54 "specific attributes must be specified under the port. And for "
55 "vlan unaware bridge port specific attributes must be specified "
56 "under the bridge.",
57 "attrs": {
58 "bridge-vlan-aware": {
59 "help": "vlan aware bridge. Setting this "
60 "attribute to yes enables vlan filtering"
61 " on the bridge",
62 "validvals": ["yes", "no"],
63 "example": ["bridge-vlan-aware yes/no"],
64 "default": "no"
65 },
66 "bridge-ports": {
67 "help": "bridge ports",
68 "multivalue": True,
69 "required": True,
70 "validvals": ["<interface-list>"],
71 "example": [
72 "bridge-ports swp1.100 swp2.100 swp3.100",
73 "bridge-ports glob swp1-3.100",
74 "bridge-ports regex (swp[1|2|3].100)"
75 ]
76 },
77 "bridge-stp": {
78 "help": "bridge-stp yes/no",
79 "example": ["bridge-stp no"],
80 "validvals": ["yes", "on", "off", "no"],
81 "default": "no"
82 },
83 "bridge-bridgeprio": {
84 "help": "bridge priority",
85 "validrange": ["0", "65535"],
86 "example": ["bridge-bridgeprio 32768"],
87 "default": "32768"
88 },
89 "bridge-ageing": {
90 "help": "bridge ageing",
91 "validrange": ["0", "65535"],
92 "example": ["bridge-ageing 300"],
93 "default": "300"
94 },
95 "bridge-fd": {
96 "help": "bridge forward delay",
97 "validrange": ["0", "255"],
98 "example": ["bridge-fd 15"],
99 "default": "15"
100 },
101 # XXX: recheck values
102 "bridge-gcint": {
103 "help": "bridge garbage collection interval in secs",
104 "validrange": ["0", "255"],
105 "example": ["bridge-gcint 4"],
106 "default": "4",
107 "compat": True,
108 "deprecated": True
109 },
110 "bridge-hello": {
111 "help": "bridge set hello time",
112 "validrange": ["0", "255"],
113 "example": ["bridge-hello 2"],
114 "default": "2"
115 },
116 "bridge-maxage": {
117 "help": "bridge set maxage",
118 "validrange": ["0", "255"],
119 "example": ["bridge-maxage 20"],
120 "default": "20"
121 },
122 "bridge-pathcosts": {
123 "help": "bridge set port path costs",
124 "validvals": ["<interface-range-list>"],
125 "validrange": ["0", "65535"],
126 "example": [
127 "under the port (for vlan aware bridge): bridge-pathcosts 100",
128 "under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100"
129 ],
130 "default": "100"
131 },
132 "bridge-portprios": {
133 "help": "bridge port prios",
134 "validvals": ["<interface-range-list>"],
135 "validrange": ["0", "65535"],
136 "example": [
137 "under the port (for vlan aware bridge): bridge-portprios 32",
138 "under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32"
139 ],
140 },
141 "bridge-mclmc": {
142 "help": "set multicast last member count",
143 "validrange": ["0", "255"],
144 "example": ["bridge-mclmc 2"],
145 "default": "2"
146 },
147 "bridge-mcrouter": {
148 "help": "Set bridge multicast routers: 0 - disabled - no, 1 - automatic (queried), 2 - permanently enabled - yes",
149 "validvals": ["yes", "no", "0", "1", "2"],
150 "example": ["bridge-mcrouter 1"],
151 "default": "yes"
152 },
153 "bridge-mcsnoop": {
154 "help": "set multicast snooping",
155 "validvals": ["yes", "no", "0", "1"],
156 "default": "yes",
157 "example": ["bridge-mcsnoop yes"]
158 },
159 "bridge-mcsqc": {
160 "help": "set multicast startup query count",
161 "validrange": ["0", "255"],
162 "default": "2",
163 "example": ["bridge-mcsqc 2"]
164 },
165 "bridge-mcqifaddr": {
166 "help": "set multicast query to use ifaddr",
167 "validvals": ["yes", "no", "0", "1"],
168 "default": "no",
169 "example": ["bridge-mcqifaddr no"]
170 },
171 "bridge-mcquerier": {
172 "help": "set multicast querier",
173 "validvals": ["yes", "no", "0", "1"],
174 "default": "no",
175 "example": ["bridge-mcquerier no"]
176 },
177 "bridge-hashel": {
178 "help": "set hash elasticity",
179 "validrange": ["0", "4096"],
180 "default": "4",
181 "example": ["bridge-hashel 4096"]
182 },
183 "bridge-hashmax": {
184 "help": "set hash max",
185 "validrange": ["0", "4096"],
186 "default": "512",
187 "example": ["bridge-hashmax 4096"]
188 },
189 "bridge-mclmi": {
190 "help": "set multicast last member interval (in secs)",
191 "validrange": ["0", "255"],
192 "default": "1",
193 "example": ["bridge-mclmi 1"]
194 },
195 "bridge-mcmi": {
196 "help": "set multicast membership interval (in secs)",
223ba5af
JF
197 "default": "260",
198 "example": ["bridge-mcmi 260"]
199 },
200 "bridge-mcqpi": {
201 "help": "set multicast querier interval (in secs)",
202 "validrange": ["0", "255"],
203 "default": "255",
204 "example": ["bridge-mcqpi 255"]
205 },
206 "bridge-mcqi": {
207 "help": "set multicast query interval (in secs)",
208 "validrange": ["0", "255"],
209 "default": "125",
210 "example": ["bridge-mcqi 125"]
211 },
212 "bridge-mcqri": {
213 "help": "set multicast query response interval (in secs)",
214 "validrange": ["0", "255"],
215 "default": "10",
216 "example": ["bridge-mcqri 10"]
217 },
218 "bridge-mcsqi": {
219 "help": "set multicast startup query interval (in secs)",
220 "validrange": ["0", "255"],
221 "default": "31",
222 "example": ["bridge-mcsqi 31"]
223 },
224 "bridge-mcqv4src": {
225 "help": "set per VLAN v4 multicast querier source address",
226 "validvals": ["<number-ipv4-list>", ],
227 "multivalue": True,
228 "compat": True,
229 "example": ["bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1"]
230 },
231 "bridge-portmcrouter": {
232 "help": "Set port multicast routers: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled",
233 "validvals": ["<interface-disabled-automatic-enabled>"],
234 "example": [
235 "under the port (for vlan aware bridge): bridge-portmcrouter 0",
236 "under the port (for vlan aware bridge): bridge-portmcrouter 1",
237 "under the port (for vlan aware bridge): bridge-portmcrouter 2",
238 "under the port (for vlan aware bridge): bridge-portmcrouter disabled",
239 "under the port (for vlan aware bridge): bridge-portmcrouter automatic",
240 "under the port (for vlan aware bridge): bridge-portmcrouter enabled",
241 "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2",
242 "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled",
243 "under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1",
244 ]
245 },
246 "bridge-portmcfl": {
247 "help": "port multicast fast leave.",
248 "validvals": ["<interface-yes-no-0-1-list>"],
249 "default": "no",
250 "example": [
251 "under the port (for vlan aware bridge): bridge-portmcfl no",
252 "under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no"
253 ]
254 },
255 "bridge-waitport": {
256 "help": "wait for a max of time secs for the"
257 " specified ports to become available,"
258 "if no ports are specified then those"
259 " specified on bridge-ports will be"
260 " used here. Specifying no ports here "
261 "should not be used if we are using "
262 "regex or \"all\" on bridge_ports,"
263 "as it wouldnt work.",
264 "default": "0",
265 "validvals": ["<number-interface-list>"],
266 "example": ["bridge-waitport 4 swp1 swp2"]
267 },
268 "bridge-maxwait": {
269 "help": "forces to time seconds the maximum time "
270 "that the Debian bridge setup scripts will "
271 "wait for the bridge ports to get to the "
272 "forwarding status, doesn\"t allow factional "
273 "part. If it is equal to 0 then no waiting"
274 " is done",
275 "validrange": ["0", "255"],
276 "default": "0",
277 "example": ["bridge-maxwait 3"]
278 },
279 "bridge-vids": {
280 "help": "bridge port vids. Can be specified "
281 "under the bridge or under the port. "
282 "If specified under the bridge the ports "
283 "inherit it unless overridden by a "
284 "bridge-vids attribute under the port",
285 "multivalue": True,
286 "validvals": ["<number-comma-range-list>"],
287 "example": [
288 "bridge-vids 4000",
289 "bridge-vids 2000 2200-3000"
290 ],
291 "aliases": ["bridge-trunk"]
292 },
293 "bridge-pvid": {
294 "help": "bridge port pvid. Must be specified under"
295 " the bridge port",
296 "validrange": ["0", "4096"],
297 "example": ["bridge-pvid 1"]
298 },
299 "bridge-access": {
300 "help": "bridge port access vlan. Must be "
301 "specified under the bridge port",
302 "validrange": ["1", "4094"],
303 "example": ["bridge-access 300"]
304 },
305 "bridge-allow-untagged": {
306 "help": "indicate if the bridge port accepts "
307 "untagged packets or not. Must be "
308 "specified under the bridge port. "
309 "Default is \"yes\"",
310 "validvals": ["yes", "no"],
311 "example": ["bridge-allow-untagged yes"],
312 "default": "yes"
313 },
314 "bridge-port-vids": {
315 "help": "bridge vlans",
316 "compat": True,
317 "example": ["bridge-port-vids bond0=1-1000,1010-1020"]
318 },
319 "bridge-port-pvids": {
320 "help": "bridge port vlans",
321 "compat": True,
322 "example": ["bridge-port-pvids bond0=100 bond1=200"]
323 },
324 "bridge-learning": {
325 "help": "bridge port learning flag",
326 "validvals": ["on", "off", "<interface-on-off-list>"],
327 "default": "on",
328 "example": ["bridge-learning off"]
329 },
330 "bridge-igmp-version": {
331 "help": "mcast igmp version",
332 "validvals": ["2", "3"],
333 "default": "2",
334 "example": ["bridge-igmp-version 2"]
335 },
336 "bridge-mld-version": {
337 "help": "mcast mld version",
338 "validvals": ["1", "2"],
339 "default": "1",
340 "example": ["bridge-mld-version 1"]
341 },
342 "bridge-unicast-flood": {
343 "help": "bridge port unicast flood flag",
344 "validvals": ["on", "off", "<interface-on-off-list>"],
345 "default": "on",
346 "example": ["under the port (for vlan aware bridge): bridge-unicast-flood on",
347 "under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on"]
348 },
349 "bridge-multicast-flood": {
350 "help": "bridge port multicast flood flag",
351 "validvals": ["on", "off", "<interface-on-off-list>"],
352 "default": "on",
353 "example": [
354 "under the port (for vlan aware bridge): bridge-multicast-flood on",
355 "under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on"
356 ]
357 },
358 "bridge-broadcast-flood": {
359 "help": "bridge port broadcast flood flag",
360 "validvals": ["on", "off", "<interface-on-off-list>"],
361 "default": "on",
362 "example": [
363 "under the port (for vlan aware bridge): bridge-broadcast-flood on",
364 "under the bridge (for vlan unaware bridge): bridge-broadcast-flood swp1=on swp2=on"
365 ]
366 },
367 "bridge-vlan-protocol": {
368 "help": "bridge vlan protocol",
369 "default": "802.1q",
370 "validvals": ["802.1q", "802.1ad"],
371 "example": ["bridge-vlan-protocol 802.1q"]
372 },
373 "bridge-vlan-stats": {
374 "help": "bridge vlan stats",
375 "default": "off",
376 "validvals": ["on", "off"],
377 "example": ["bridge-vlan-stats off"]
378 },
379 "bridge-arp-nd-suppress": {
380 "help": "bridge port arp nd suppress flag",
381 "validvals": ["on", "off", "<interface-on-off-list>"],
382 "default": "off",
383 "example": [
384 "under the port (for vlan aware bridge): bridge-arp-nd-suppress on",
385 "under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on"
386 ]
387 },
388 "bridge-mcstats": {
389 "help": "bridge multicast stats",
390 "default": "off",
391 "validvals": ["on", "off", "1", "0", "yes", "no"],
392 "example": ["bridge-mcstats off"]
393 },
394 "bridge-l2protocol-tunnel": {
395 "help": "layer 2 protocol tunneling",
396 "validvals": [ # XXX: lists all combinations, should move to
397 # a better representation
398 "all",
399 "cdp",
400 "cdp lacp",
401 "cdp lacp lldp",
402 "cdp lacp lldp pvst",
403 "cdp lacp lldp stp",
404 "cdp lacp pvst",
405 "cdp lacp pvst stp",
406 "cdp lacp stp",
407 "cdp lldp",
408 "cdp lldp pvst",
409 "cdp lldp pvst stp",
410 "cdp lldp stp",
411 "cdp pvst",
412 "cdp pvst stp",
413 "cdp stp",
414 "lacp",
415 "lacp lldp",
416 "lacp lldp pvst",
417 "lacp lldp pvst stp",
418 "lacp lldp stp",
419 "lacp pvst",
420 "lacp pvst stp",
421 "lacp stp",
422 "lldp",
423 "lldp pvst",
424 "lldp pvst stp",
425 "lldp stp",
426 "pvst",
427 "pvst stp",
428 "stp",
429 "<interface-l2protocol-tunnel-list>"],
430 "example": [
431 "under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all",
432 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst",
433 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst",
434 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp",
435 "under the port (for vlan aware bridge): bridge-l2protocol-tunnel all"
436 ]
437 },
438 "bridge-ports-condone-regex": {
439 "help": "bridge ports to ignore/condone when reloading config / removing interfaces",
440 "required": False,
441 "example": ["bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$"]
442 },
e537a6e6
JF
443 "bridge-vlan-vni-map": {
444 "help": "Single vxlan support",
f54c45de 445 "example": ["bridge-vlan-vni-map 1000-1001=1000-1001"],
e537a6e6 446 },
7840bd2c
JF
447 "bridge-always-up": {
448 "help": "Enabling this attribute on a bridge will enslave a dummy interface to the bridge",
449 "required": False,
450 "validvals": ["yes", "no", "on", "off"]
451 }
223ba5af
JF
452 }
453 }
454
455 bridge_utils_missing_warning = True
d486dd0d
JF
456
457 # Netlink attributes not associated with ifupdown2
458 # attributes are left commented-out for a future use
459 # and kept in order :)
223ba5af 460 _ifla_br_attributes_map = {
d486dd0d 461 # Link.IFLA_BR_UNSPEC,
223ba5af
JF
462 'bridge-fd': Link.IFLA_BR_FORWARD_DELAY,
463 'bridge-hello': Link.IFLA_BR_HELLO_TIME,
464 'bridge-maxage': Link.IFLA_BR_MAX_AGE,
465 'bridge-ageing': Link.IFLA_BR_AGEING_TIME,
466 'bridge-stp': Link.IFLA_BR_STP_STATE,
01f27c67 467 # 'bridge-bridgeprio': Link.IFLA_BR_PRIORITY,
223ba5af
JF
468 'bridge-vlan-aware': Link.IFLA_BR_VLAN_FILTERING,
469 'bridge-vlan-protocol': Link.IFLA_BR_VLAN_PROTOCOL,
d486dd0d
JF
470 # Link.IFLA_BR_GROUP_FWD_MASK,
471 # Link.IFLA_BR_ROOT_ID,
472 # Link.IFLA_BR_BRIDGE_ID,
473 # Link.IFLA_BR_ROOT_PORT,
474 # (Link.IFLA_BR_ROOT_PATH_COST,,
475 # Link.IFLA_BR_TOPOLOGY_CHANGE,
476 # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
477 # Link.IFLA_BR_HELLO_TIMER,
478 # Link.IFLA_BR_TCN_TIMER,
479 # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
480 # Link.IFLA_BR_GC_TIMER,
481 # Link.IFLA_BR_GROUP_ADDR,
482 # Link.IFLA_BR_FDB_FLUSH,
223ba5af 483 'bridge-mcrouter': Link.IFLA_BR_MCAST_ROUTER,
d486dd0d 484 #('bridge-mcsnoop', Link.IFLA_BR_MCAST_SNOOPING), # requires special handling so we won't loop on this attr
223ba5af
JF
485 'bridge-mcqifaddr': Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
486 'bridge-mcquerier': Link.IFLA_BR_MCAST_QUERIER,
487 'bridge-hashel': Link.IFLA_BR_MCAST_HASH_ELASTICITY,
488 'bridge-hashmax': Link.IFLA_BR_MCAST_HASH_MAX,
489 'bridge-mclmc': Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
490 'bridge-mcsqc': Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
491 'bridge-mclmi': Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
492 'bridge-mcmi': Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
493 'bridge-mcqpi': Link.IFLA_BR_MCAST_QUERIER_INTVL,
494 'bridge-mcqi': Link.IFLA_BR_MCAST_QUERY_INTVL,
495 'bridge-mcqri': Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
496 'bridge-mcsqi': Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
d486dd0d
JF
497 # Link.IFLA_BR_NF_CALL_IPTABLES,
498 # Link.IFLA_BR_NF_CALL_IP6TABLES,
499 # Link.IFLA_BR_NF_CALL_ARPTABLES,
500 # Link.IFLA_BR_VLAN_DEFAULT_PVID,
501 # Link.IFLA_BR_PAD,
502 # (Link.IFLA_BR_VLAN_STATS_ENABLED, 'bridge-vlan-stats'), # already dealt with, in a separate loop
223ba5af
JF
503 'bridge-igmp-version': Link.IFLA_BR_MCAST_IGMP_VERSION,
504 'bridge-mcstats': Link.IFLA_BR_MCAST_STATS_ENABLED,
505 'bridge-mld-version': Link.IFLA_BR_MCAST_MLD_VERSION
506 }
d486dd0d
JF
507 # 'bridge-vlan-stats & bridge-mcstat are commented out even though, today
508 # they are supported. It is done this way because this dictionary is used
509 # in a loop, but these attributes require additional work. Thus they are
510 # excluded from this loop without overhead.
511
d486dd0d
JF
512 _ifla_br_attributes_translate_user_config_to_netlink_map = dict(
513 (
514 # Link.IFLA_BR_UNSPEC,
515 (Link.IFLA_BR_FORWARD_DELAY, lambda x: int(x) * 100),
516 (Link.IFLA_BR_HELLO_TIME, lambda x: int(x) * 100),
517 (Link.IFLA_BR_MAX_AGE, lambda x: int(x) * 100),
518 (Link.IFLA_BR_AGEING_TIME, lambda x: int(x) * 100),
519 # Link.IFLA_BR_STP_STATE, # STP is treated outside the loop
520 (Link.IFLA_BR_PRIORITY, int),
521 (Link.IFLA_BR_VLAN_FILTERING, utils.get_boolean_from_string),
223ba5af 522 (Link.IFLA_BR_VLAN_PROTOCOL, str.upper),
d486dd0d
JF
523 # Link.IFLA_BR_GROUP_FWD_MASK,
524 # Link.IFLA_BR_ROOT_ID,
525 # Link.IFLA_BR_BRIDGE_ID,
526 # Link.IFLA_BR_ROOT_PORT,
527 # Link.IFLA_BR_ROOT_PATH_COST,
528 # Link.IFLA_BR_TOPOLOGY_CHANGE,
529 # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
530 # Link.IFLA_BR_HELLO_TIMER,
531 # Link.IFLA_BR_TCN_TIMER,
532 # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
533 # Link.IFLA_BR_GC_TIMER,
534 # Link.IFLA_BR_GROUP_ADDR,
535 # Link.IFLA_BR_FDB_FLUSH,
536 (Link.IFLA_BR_MCAST_ROUTER, utils.get_int_from_boolean_and_string),
537 (Link.IFLA_BR_MCAST_SNOOPING, utils.get_boolean_from_string),
538 (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, utils.get_boolean_from_string),
539 (Link.IFLA_BR_MCAST_QUERIER, utils.get_boolean_from_string),
540 (Link.IFLA_BR_MCAST_HASH_ELASTICITY, int),
541 (Link.IFLA_BR_MCAST_HASH_MAX, int),
542 (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, int),
543 (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, int),
544 (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, lambda x: int(x) * 100),
545 (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, lambda x: int(x) * 100),
546 (Link.IFLA_BR_MCAST_QUERIER_INTVL, lambda x: int(x) * 100),
547 (Link.IFLA_BR_MCAST_QUERY_INTVL, lambda x: int(x) * 100),
548 (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, lambda x: int(x) * 100),
549 (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, lambda x: int(x) * 100),
550 # Link.IFLA_BR_NF_CALL_IPTABLES,
551 # Link.IFLA_BR_NF_CALL_IP6TABLES,
552 # Link.IFLA_BR_NF_CALL_ARPTABLES,
553 # Link.IFLA_BR_VLAN_DEFAULT_PVID,
554 # Link.IFLA_BR_PAD,
555 (Link.IFLA_BR_VLAN_STATS_ENABLED, utils.get_boolean_from_string),
556 (Link.IFLA_BR_MCAST_IGMP_VERSION, int),
557 (Link.IFLA_BR_MCAST_STATS_ENABLED, utils.get_boolean_from_string),
558 (Link.IFLA_BR_MCAST_MLD_VERSION, int)
559 )
560 )
561
223ba5af 562 _ifla_brport_attributes_map = {
d486dd0d
JF
563 # Link.IFLA_BRPORT_UNSPEC,
564 # Link.IFLA_BRPORT_STATE,
223ba5af
JF
565 'bridge-portprios': Link.IFLA_BRPORT_PRIORITY,
566 'bridge-pathcosts': Link.IFLA_BRPORT_COST,
d486dd0d
JF
567 # Link.IFLA_BRPORT_MODE,
568 # Link.IFLA_BRPORT_GUARD,
569 # Link.IFLA_BRPORT_PROTECT,
223ba5af
JF
570 'bridge-portmcfl': Link.IFLA_BRPORT_FAST_LEAVE,
571 'bridge-learning': Link.IFLA_BRPORT_LEARNING,
572 'bridge-unicast-flood': Link.IFLA_BRPORT_UNICAST_FLOOD,
d486dd0d
JF
573 # Link.IFLA_BRPORT_PROXYARP,
574 # Link.IFLA_BRPORT_LEARNING_SYNC,
575 # Link.IFLA_BRPORT_PROXYARP_WIFI,
576 # Link.IFLA_BRPORT_ROOT_ID,
577 # Link.IFLA_BRPORT_BRIDGE_ID,
578 # Link.IFLA_BRPORT_DESIGNATED_PORT,
579 # Link.IFLA_BRPORT_DESIGNATED_COST,
580 # Link.IFLA_BRPORT_ID,
581 # Link.IFLA_BRPORT_NO,
582 # Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
583 # Link.IFLA_BRPORT_CONFIG_PENDING,
584 # Link.IFLA_BRPORT_MESSAGE_AGE_TIMER,
585 # Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
586 # Link.IFLA_BRPORT_HOLD_TIMER,
587 # Link.IFLA_BRPORT_FLUSH,
223ba5af 588 'bridge-portmcrouter': Link.IFLA_BRPORT_MULTICAST_ROUTER,
d486dd0d 589 # Link.IFLA_BRPORT_PAD,
223ba5af 590 'bridge-multicast-flood': Link.IFLA_BRPORT_MCAST_FLOOD,
d486dd0d
JF
591 # Link.IFLA_BRPORT_MCAST_TO_UCAST,
592 # Link.IFLA_BRPORT_VLAN_TUNNEL,
223ba5af
JF
593 'bridge-broadcast-flood': Link.IFLA_BRPORT_BCAST_FLOOD,
594 'bridge-l2protocol-tunnel': Link.IFLA_BRPORT_GROUP_FWD_MASK,
d486dd0d
JF
595 # Link.IFLA_BRPORT_PEER_LINK,
596 # Link.IFLA_BRPORT_DUAL_LINK,
223ba5af
JF
597 'bridge-arp-nd-suppress': Link.IFLA_BRPORT_NEIGH_SUPPRESS,
598 }
d486dd0d
JF
599
600 _ifla_brport_multicast_router_dict_to_int = {
601 'disabled': 0,
602 '0': 0,
603 'no': 0,
604 'automatic': 1,
605 '1': 1,
606 'yes': 1,
607 'enabled': 2,
608 '2': 2,
609 }
610
223ba5af
JF
611 _ifla_brport_multicast_router_dict_int_to_str = {
612 0: "disabled",
613 1: "automatic",
614 2: "enabled"
615 }
616
d486dd0d
JF
617 # callable to translate <interface-yes-no-0-1-list> to netlink value
618 _ifla_brport_attributes_translate_user_config_to_netlink_map = dict(
619 (
620 (Link.IFLA_BRPORT_PRIORITY, int),
621 (Link.IFLA_BRPORT_COST, int),
622 (Link.IFLA_BRPORT_MULTICAST_ROUTER, lambda x: bridge._ifla_brport_multicast_router_dict_to_int.get(x, 0)),
623 (Link.IFLA_BRPORT_FAST_LEAVE, utils.get_boolean_from_string),
624 (Link.IFLA_BRPORT_LEARNING, utils.get_boolean_from_string),
625 (Link.IFLA_BRPORT_UNICAST_FLOOD, utils.get_boolean_from_string),
626 (Link.IFLA_BRPORT_MCAST_FLOOD, utils.get_boolean_from_string),
223ba5af 627 (Link.IFLA_BRPORT_BCAST_FLOOD, utils.get_boolean_from_string),
d486dd0d 628 (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x),
223ba5af 629 (Link.IFLA_BRPORT_NEIGH_SUPPRESS, utils.get_boolean_from_string)
d486dd0d
JF
630 )
631 )
632
633 def __init__(self, *args, **kargs):
5f4ab659 634 Bridge.__init__(self)
d486dd0d 635 moduleBase.__init__(self, *args, **kargs)
d486dd0d 636 self.name = self.__class__.__name__
d486dd0d
JF
637 self._resv_vlan_range = self._get_reserved_vlan_range()
638 self.logger.debug('%s: using reserved vlan range %s' % (self.__class__.__name__, str(self._resv_vlan_range)))
639
640 self.default_stp_on = utils.get_boolean_from_string(
641 policymanager.policymanager_api.get_attr_default(
642 module_name=self.__class__.__name__,
643 attr='bridge-stp'
644 )
645 )
646
647 self.default_vlan_stats = policymanager.policymanager_api.get_attr_default(
648 module_name=self.__class__.__name__,
649 attr='bridge-vlan-stats'
650 )
651
652 self.warn_on_untagged_bridge_absence = utils.get_boolean_from_string(
653 policymanager.policymanager_api.get_module_globals(
654 module_name=self.__class__.__name__,
655 attr='warn_on_untagged_bridge_absence'
656 )
657 )
658 self.logger.debug('bridge: init: warn_on_untagged_bridge_absence=%s'
659 % self.warn_on_untagged_bridge_absence)
660
661 self._vxlan_bridge_default_igmp_snooping = policymanager.policymanager_api.get_module_globals(
662 self.__class__.__name__,
663 'vxlan_bridge_default_igmp_snooping'
664 )
665 self.logger.debug('bridge: init: vxlan_bridge_default_igmp_snooping=%s'
666 % self._vxlan_bridge_default_igmp_snooping)
667
668 self.arp_nd_suppress_only_on_vxlan = utils.get_boolean_from_string(
669 policymanager.policymanager_api.get_module_globals(
670 module_name=self.__class__.__name__,
671 attr='allow_arp_nd_suppress_only_on_vxlan'
672 )
673 )
674 self.logger.debug('bridge: init: arp_nd_suppress_only_on_vxlan=%s' % self.arp_nd_suppress_only_on_vxlan)
675
1b845b1b
AP
676 self.bridge_always_up_dummy_brport = policymanager.policymanager_api.get_module_globals(
677 module_name=self.__class__.__name__,
678 attr='bridge_always_up_dummy_brport'
679 )
680 self.logger.debug('bridge: init: bridge_always_up_dummy_brport=%s' % self.bridge_always_up_dummy_brport)
681
d486dd0d
JF
682 try:
683 self.bridge_allow_multiple_vlans = utils.get_boolean_from_string(
684 self.sysctl_get('net.bridge.bridge-allow-multiple-vlans')
685 )
3218f49d 686 except Exception:
d486dd0d
JF
687 # Cumulus Linux specific variable. Failure probably means that
688 # ifupdown2 is running a a different system.
689 self.bridge_allow_multiple_vlans = True
690 self.logger.debug('bridge: init: multiple vlans allowed %s' % self.bridge_allow_multiple_vlans)
691
692 self.bridge_mac_iface_list = policymanager.policymanager_api.get_module_globals(self.__class__.__name__, 'bridge_mac_iface') or []
693 self.bridge_mac_iface = None, None # ifname, mac
694
695 self.bridge_set_static_mac_from_port = utils.get_boolean_from_string(
696 policymanager.policymanager_api.get_module_globals(
697 self.__class__.__name__, 'bridge_set_static_mac_from_port'
698 )
699 )
700
80adb3c1
JF
701 self.vxlan_bridge_igmp_snooping_enable_port_mcrouter = utils.get_boolean_from_string(
702 policymanager.policymanager_api.get_module_globals(
703 module_name=self.__class__.__name__,
704 attr="vxlan_bridge_igmp_snooping_enable_port_mcrouter"
705 ),
706 default=True
707 )
708
223ba5af
JF
709 self.bridge_vxlan_arp_nd_suppress = utils.get_boolean_from_string(
710 policymanager.policymanager_api.get_module_globals(
711 module_name=self.__class__.__name__,
712 attr="bridge-vxlan-arp-nd-suppress"
713 ),
714 default=False
715 )
716 self.bridge_vxlan_arp_nd_suppress_int = int(self.bridge_vxlan_arp_nd_suppress)
717
d486dd0d
JF
718 self.l2protocol_tunnel_callback = {
719 'all': self._l2protocol_tunnel_set_all,
720 'stp': self._l2protocol_tunnel_set_stp,
721 'cdp': self._l2protocol_tunnel_set_cdp,
722 'pvst': self._l2protocol_tunnel_set_pvst,
723 'lldp': self._l2protocol_tunnel_set_lldp,
724 'lacp': self._l2protocol_tunnel_set_lacp
725 }
726
727 self.query_check_l2protocol_tunnel_callback = {
728 'all': self._query_check_l2protocol_tunnel_all,
729 'stp': self._query_check_l2protocol_tunnel_stp,
730 'cdp': self._query_check_l2protocol_tunnel_cdp,
731 'pvst': self._query_check_l2protocol_tunnel_pvst,
732 'lldp': self._query_check_l2protocol_tunnel_lldp,
733 'lacp': self._query_check_l2protocol_tunnel_lacp
734 }
735
223ba5af
JF
736 self._bridge_attribute_query_check_handler = {
737 "bridge-maxwait": (self._query_check_br_attr_wait, None),
738 "bridge-waitport": (self._query_check_br_attr_wait, None),
739
740 "bridge-stp": (self._query_check_br_attr_stp, Link.IFLA_BR_STP_STATE),
741
742 "bridge-mcstats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_MCAST_STATS_ENABLED),
743 "bridge-vlan-stats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_VLAN_STATS_ENABLED),
744
745 "bridge-vlan-aware": (self._query_check_br_attr_boolean, Link.IFLA_BR_VLAN_FILTERING),
746 "bridge-mcqifaddr": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
747 "bridge-mcsnoop": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_SNOOPING),
748 "bridge-mcquerier": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERIER),
749 "bridge-mcrouter": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_ROUTER),
750
751 "bridge-vlan-protocol": (self._query_check_br_attr_string, Link.IFLA_BR_VLAN_PROTOCOL),
752
753 "bridge-mcsqc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
754 "bridge-mclmc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
755 "bridge-hashmax": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_MAX),
756 "bridge-hashel": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_ELASTICITY),
757 "bridge-bridgeprio": (self._query_check_br_attr_int, Link.IFLA_BR_PRIORITY),
758 "bridge-igmp-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_IGMP_VERSION),
759 "bridge-mld-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_MLD_VERSION),
760
761 "bridge-maxage": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MAX_AGE),
762 "bridge-fd": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_FORWARD_DELAY),
763 "bridge-hello": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_HELLO_TIME),
764 "bridge-ageing": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_AGEING_TIME),
765 "bridge-mcmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
766 "bridge-mcsqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
767 "bridge-mclmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
768 "bridge-mcqri": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
769 "bridge-mcqpi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERIER_INTVL),
770 "bridge-mcqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_INTVL),
771 }
772
773 self._brport_attribute_query_check_handler = {
774 "bridge-pathcosts": self._query_check_brport_attr_int,
775 "bridge-portprios": self._query_check_brport_attr_int,
776 "bridge-portmcfl": self._query_check_brport_attr_boolean_yes_no,
777 "bridge-learning": self._query_check_brport_attr_boolean_on_off,
778 "bridge-arp-nd-suppress": self._query_check_brport_attr_boolean_on_off,
779 "bridge-unicast-flood": self._query_check_brport_attr_boolean_on_off,
780 "bridge-multicast-flood": self._query_check_brport_attr_boolean_on_off,
781 "bridge-broadcast-flood": self._query_check_brport_attr_boolean_on_off,
782 "bridge-portmcrouter": self._query_check_brport_attr_portmcrouter,
783 }
784
785 self.bridge_vxlan_port_learning = utils.get_boolean_from_string(
786 policymanager.policymanager_api.get_module_globals(
787 self.__class__.__name__,
788 "bridge_vxlan_port_learning"
789 ),
790 default=True
791 )
792
d486dd0d
JF
793 @staticmethod
794 def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
795 if not ifla_brport_group_maskhi:
796 ifla_brport_group_maskhi = 0x1
797 else:
798 ifla_brport_group_maskhi |= 0x1
d486dd0d
JF
799 return ifla_brport_group_mask, ifla_brport_group_maskhi
800
801 @staticmethod
802 def _l2protocol_tunnel_set_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
803 if not ifla_brport_group_maskhi:
804 ifla_brport_group_maskhi = 0x2
805 else:
806 ifla_brport_group_maskhi |= 0x2
d486dd0d
JF
807 return ifla_brport_group_mask, ifla_brport_group_maskhi
808
809 @staticmethod
810 def _l2protocol_tunnel_set_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
811 if not ifla_brport_group_mask:
812 ifla_brport_group_mask = 0x1
813 else:
814 ifla_brport_group_mask |= 0x1
d486dd0d
JF
815 return ifla_brport_group_mask, ifla_brport_group_maskhi
816
817 @staticmethod
818 def _l2protocol_tunnel_set_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
819 if not ifla_brport_group_mask:
820 ifla_brport_group_mask = 0x4
821 else:
822 ifla_brport_group_mask |= 0x4
d486dd0d
JF
823 return ifla_brport_group_mask, ifla_brport_group_maskhi
824
825 @staticmethod
826 def _l2protocol_tunnel_set_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33
JF
827 if not ifla_brport_group_mask:
828 ifla_brport_group_mask = 0x4000
829 else:
830 ifla_brport_group_mask |= 0x4000
d486dd0d
JF
831 return ifla_brport_group_mask, ifla_brport_group_maskhi
832
833 @staticmethod
834 def _l2protocol_tunnel_set_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
835 # returns new values for ifla_brport_group_mask and ifla_brport_group_maskhi
836 return 0x1 | 0x4 | 0x4000, 0x1 | 0x2
837
838 @staticmethod
839 def _query_check_l2protocol_tunnel_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 840 return ifla_brport_group_mask and ifla_brport_group_mask & 0x1
d486dd0d
JF
841
842 @staticmethod
843 def _query_check_l2protocol_tunnel_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 844 return ifla_brport_group_maskhi and ifla_brport_group_maskhi & 0x2
d486dd0d
JF
845
846 @staticmethod
847 def _query_check_l2protocol_tunnel_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 848 return ifla_brport_group_maskhi and ifla_brport_group_maskhi & 0x1
d486dd0d
JF
849
850 @staticmethod
851 def _query_check_l2protocol_tunnel_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 852 return ifla_brport_group_mask and ifla_brport_group_mask & 0x4000
d486dd0d
JF
853
854 @staticmethod
855 def _query_check_l2protocol_tunnel_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
6deeac33 856 return ifla_brport_group_mask and ifla_brport_group_mask & 0x4
d486dd0d
JF
857
858 @staticmethod
859 def _query_check_l2protocol_tunnel_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
860 return ifla_brport_group_mask == (0x1 | 0x4 | 0x4000) and ifla_brport_group_maskhi == (0x1 | 0x2)
861
862 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
863 retval = self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc)
864 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
865 if not self.check_bridge_port_vid_attrs(ifaceobj):
866 retval = False
867 c1 = self.syntax_check_vxlan_in_vlan_aware_br(ifaceobj, ifaceobj_getfunc)
868 c2 = self.syntax_check_bridge_allow_multiple_vlans(ifaceobj, ifaceobj_getfunc)
869 return retval and c1 #and c2
870
871 def syntax_check_bridge_allow_multiple_vlans(self, ifaceobj, ifaceobj_getfunc):
872 result = True
873 if not self.bridge_allow_multiple_vlans and ifaceobj.link_kind & ifaceLinkKind.BRIDGE and ifaceobj.lowerifaces:
874 vlan_id = None
875 for brport_name in ifaceobj.lowerifaces:
876 for obj in ifaceobj_getfunc(brport_name) or []:
877 if obj.link_kind & ifaceLinkKind.VLAN:
878 sub_intf_vlan_id = self._get_vlan_id(obj)
879 if vlan_id and vlan_id != sub_intf_vlan_id:
880 self.logger.error('%s: ignore %s: multiple vlans not allowed under bridge '
881 '(sysctl net.bridge.bridge-allow-multiple-vlans not set)'
882 % (ifaceobj.name, brport_name))
883 result = False
884 continue
885 vlan_id = sub_intf_vlan_id
886 return result
887
888 def check_bridge_port_vid_attrs(self, ifaceobj):
889 if (ifaceobj.get_attr_value('bridge-access') and
890 (self.get_ifaceobj_bridge_vids_value(ifaceobj) or
891 ifaceobj.get_attr_value('bridge-pvid'))):
c46af1c9 892 self.logger.warning('%s: bridge-access given, bridge-vids and bridge-pvid '
d486dd0d
JF
893 'will be ignored' % ifaceobj.name)
894 return False
895 return True
896
897 def check_bridge_vlan_aware_port(self, ifaceobj, ifaceobj_getfunc):
898 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
899 ports = self._get_bridge_port_list(ifaceobj)
900 if not ports:
901 return True
902 result = True
903 for port_name in ports:
904 port_obj_l = ifaceobj_getfunc(port_name)
905 if port_obj_l and port_obj_l[0].link_kind & ifaceLinkKind.VLAN:
906 self.logger.error('%s: %s: vlan sub-interface is not '
907 'supported in a vlan-aware bridge'
908 % (ifaceobj.name, port_name))
909 result = False
910 if (port_obj_l and
911 port_obj_l[0].get_attr_value('bridge-arp-nd-suppress') and
912 self.arp_nd_suppress_only_on_vxlan and
913 not port_obj_l[0].link_kind & ifaceLinkKind.VXLAN):
914 self.log_error('\'bridge-arp-nd-suppress\' is not '
915 'supported on a non-vxlan port %s'
916 %port_obj_l[0].name)
917 result = False
918 return result
919 return True
920
921 def _error_vxlan_in_vlan_aware_br(self, ifaceobj, bridgename):
922 self.log_error('`bridge-access` attribute is mandatory when vxlan '
923 'device (%s) is part of vlan aware bridge (%s)'
924 % (ifaceobj.name, bridgename), ifaceobj)
925
926 def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
927 if not ifaceobj_getfunc:
928 return True
e537a6e6 929 if (ifaceobj.link_kind & ifaceLinkKind.VXLAN and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
d486dd0d
JF
930 if ifaceobj.get_attr_value('bridge-access'):
931 return True
932 for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
933 ifaceobj_upper_list = ifaceobj_getfunc(iface)
934 if not ifaceobj_upper_list:
935 continue
936 ifaceobj_upper = ifaceobj_upper_list[0]
937 bridge_vids = self._get_bridge_vids(iface, ifaceobj_getfunc)
938 if ifaceobj_upper.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
939 vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
940 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
941 if (not vids
942 or not pvid
943 or not self._compare_vids(bridge_vids,
944 vids,
945 pvid=pvid)):
946 self._error_vxlan_in_vlan_aware_br(ifaceobj,
947 ifaceobj_upper.name)
948 return False
949 return True
950
951 @staticmethod
952 def _is_bridge(ifaceobj):
953 return (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
954 ifaceobj.get_attr_value_first('bridge-ports') or
955 ifaceobj.get_attr_value_first('bridge-vlan-aware'))
956
d1265fd5
JF
957 def _get_ifaceobj_bridge_ports(self, ifaceobj):
958 bridge_ports = []
959
960 for brport in ifaceobj.get_attr_value('bridge-ports') or []:
961 if brport != 'none':
962 bridge_ports.extend(brport.split())
963
964 return ' '.join(bridge_ports)
d486dd0d 965
52ac1cfc 966 def check_valid_bridge(self, ifaceobj, ifname):
223ba5af 967 if self.cache.link_exists(ifname) and not self.cache.link_is_bridge(ifname):
52ac1cfc
JF
968 self.log_error('misconfiguration of bridge attribute(s) on existing non-bridge interface (%s)' % ifname, ifaceobj=ifaceobj)
969 return False
970 return True
971
d486dd0d 972 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
52ac1cfc 973 if not self._is_bridge(ifaceobj) or not self.check_valid_bridge(ifaceobj, ifaceobj.name):
d486dd0d
JF
974 return None
975 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
976 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
977 ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
978 # for special vlan aware bridges, we need to add another bit
979 if utils.get_boolean_from_string(ifaceobj.get_attr_value_first('bridge-vlan-aware')):
980 ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
981 ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
5f4ab659
JF
982
983 # store the name of all bridge vlan aware in a global list
984 self.bridge_vlan_aware_list.append(ifaceobj.name)
985
d486dd0d
JF
986 ifaceobj.role |= ifaceRole.MASTER
987 ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
988 return self.parse_port_list(ifaceobj.name,
989 self._get_ifaceobj_bridge_ports(ifaceobj),
990 ifacenames_all)
991
992 def get_dependent_ifacenames_running(self, ifaceobj):
223ba5af 993 if not self.cache.bridge_exists(ifaceobj.name):
d486dd0d 994 return None
223ba5af 995 return self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
996
997 def _get_bridge_port_list(self, ifaceobj):
998
999 # port list is also available in the previously
1000 # parsed dependent list. Use that if available, instead
1001 # of parsing port expr again
1002 port_list = ifaceobj.lowerifaces
1003 if port_list:
1004 return port_list
1005 ports = self._get_ifaceobj_bridge_ports(ifaceobj)
1006 if ports:
1007 return self.parse_port_list(ifaceobj.name, ports)
1008 else:
1009 return None
1010
1011 def _get_bridge_port_list_user_ordered(self, ifaceobj):
1012 # When enslaving bridge-ports we need to return the exact user
1013 # configured bridge ports list (bridge will inherit the mac of the
1014 # first device.
1015 ports = self._get_ifaceobj_bridge_ports(ifaceobj)
1016 return self.parse_port_list(ifaceobj.name, ports) if ports else None
1017
f5c97c0d
MW
1018 def _get_bridge_port_condone_regex(self, ifaceobj, get_string = False):
1019 bridge_port_condone_regex = ifaceobj.get_attr_value_first('bridge-ports-condone-regex')
1020 # If bridge-ports-ignore-regex is configured, do NOT use the parse_port_list()
1021 # function to gather a list of ports matching the regex here and now but set
1022 # up a compiled regex to be used in a match later. This way we try to avoid
1023 # a race condition where an (possibly VM) interface is created after this
1024 # function has been called but before the bridgeports are validated.
1025 if bridge_port_condone_regex:
1026 if get_string:
1027 return bridge_port_condone_regex
1028 return re.compile (r"%s" % bridge_port_condone_regex)
1029 return None
1030
d486dd0d
JF
1031 def _process_bridge_waitport(self, ifaceobj, portlist):
1032 waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
1033 if not waitport_value: return
1034 try:
1035 waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
1036 if not waitportvals: return
1037 try:
1038 waitporttime = int(waitportvals[0])
3218f49d 1039 except Exception:
d486dd0d
JF
1040 self.log_warn('%s: invalid waitport value \'%s\''
1041 %(ifaceobj.name, waitportvals[0]))
1042 return
1043 if waitporttime <= 0: return
1044 try:
1045 waitportlist = self.parse_port_list(ifaceobj.name,
1046 waitportvals[1])
3b01ed76 1047 except IndexError as e:
d486dd0d
JF
1048 # ignore error and use all bridge ports
1049 waitportlist = portlist
1050 pass
1051 if not waitportlist: return
1052 self.logger.info('%s: waiting for ports %s to exist ...'
1053 %(ifaceobj.name, str(waitportlist)))
1054 starttime = time.time()
1055 while ((time.time() - starttime) < waitporttime):
1056 if all([False for p in waitportlist
223ba5af 1057 if not self.cache.link_exists(p)]):
d486dd0d
JF
1058 break;
1059 time.sleep(1)
3b01ed76 1060 except Exception as e:
d486dd0d
JF
1061 self.log_warn('%s: unable to process waitport: %s'
1062 %(ifaceobj.name, str(e)))
1063
1064 def _enable_disable_ipv6(self, port, enable='1'):
1065 try:
1066 self.write_file('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % port, enable)
3b01ed76 1067 except Exception as e:
d486dd0d
JF
1068 self.logger.info(str(e))
1069
223ba5af 1070 def handle_ipv6(self, ports, state):
d486dd0d
JF
1071 for p in ports:
1072 self._enable_disable_ipv6(p, state)
1073
1074 def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports):
1075 """ pretty print bridge port add errors.
1076 since the commands are batched and the kernel only returns error
1077 codes, this function tries to interpret some error codes
1078 and prints clearer errors """
1079
1080 if re.search('RTNETLINK answers: Invalid argument', errstr):
1081 # Cumulus Linux specific error checks
1082 try:
1083 if self.sysctl_get('net.bridge.bridge-allow-multiple-vlans') == '0':
1084 vlanid = None
1085 for bport in bridgeports:
1086 currvlanid = self._get_vlan_id_from_ifacename(bport)
1087 if vlanid:
1088 if currvlanid != vlanid:
1089 self.log_error('%s: ' %bridgeifaceobj.name +
1090 'net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed', bridgeifaceobj)
1091 break
1092 if currvlanid:
1093 vlanid = currvlanid
1094 except Exception as e:
1095 errstr += '\n%s' % str(e)
1096 self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj)
1097
1098 def _add_ports(self, ifaceobj, ifaceobj_getfunc):
1099 bridgeports = self._get_bridge_port_list(ifaceobj)
f5c97c0d 1100 bridgeportscondoneregex = self._get_bridge_port_condone_regex(ifaceobj)
d486dd0d
JF
1101 runningbridgeports = []
1102
7840bd2c
JF
1103 # bridge-always-up #####################################################
1104 bridge_always_up = ifaceobj.get_attr_value_first("bridge-always-up")
1105 dummy_brport = None
1106
1107 if utils.get_boolean_from_string(bridge_always_up):
1108 # the dummy port will be added to the bridgeports list so the
1109 # following code don't de-enslave the dummy device.
1110 dummy_brport = self.bridge_always_up(ifaceobj.name, bridgeports)
1111
1112 ########################################################################
1113
d486dd0d 1114 self._process_bridge_waitport(ifaceobj, bridgeports)
d486dd0d
JF
1115 # Delete active ports not in the new port list
1116 if not ifupdownflags.flags.PERFMODE:
223ba5af 1117 runningbridgeports = self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
1118 if runningbridgeports:
1119 for bport in runningbridgeports:
1120 if not bridgeports or bport not in bridgeports:
f5c97c0d
MW
1121 if bridgeportscondoneregex and bridgeportscondoneregex.match(bport):
1122 self.logger.info("%s: port %s will stay enslaved as it matches with bridge-ports-condone-regex" % (ifaceobj.name, bport))
1123 continue
223ba5af 1124 self.netlink.link_set_nomaster(bport)
d486dd0d
JF
1125 # set admin DOWN on all removed ports
1126 # that don't have config outside bridge
1127 if not ifaceobj_getfunc(bport):
223ba5af 1128 self.netlink.link_down(bport)
d486dd0d
JF
1129 # enable ipv6 for ports that were removed
1130 self.handle_ipv6([bport], '0')
1131 else:
1132 runningbridgeports = []
1133 if not bridgeports:
d486dd0d
JF
1134 return []
1135 err = 0
3b01ed76 1136 newbridgeports = set(bridgeports).difference(set(runningbridgeports))
d486dd0d
JF
1137 newly_enslaved_ports = []
1138
1139 newbridgeports_ordered = []
1140 for br_port in self._get_bridge_port_list_user_ordered(ifaceobj):
1141 if br_port in newbridgeports:
1142 newbridgeports_ordered.append(br_port)
1143
7840bd2c
JF
1144 if dummy_brport:
1145 # add the dummy port to the list of interface to enslave
1146 # link_set_master should make sure that the device is not
1147 # already enslaved.
1148 newbridgeports_ordered.append(dummy_brport)
1149
223ba5af
JF
1150 self.iproute2.batch_start()
1151
d486dd0d
JF
1152 for bridgeport in newbridgeports_ordered:
1153 try:
1154 if (not ifupdownflags.flags.DRYRUN and
223ba5af 1155 not self.cache.link_exists(bridgeport)):
d486dd0d
JF
1156 self.log_error('%s: bridge port %s does not exist'
1157 %(ifaceobj.name, bridgeport), ifaceobj)
1158 err += 1
1159 continue
223ba5af 1160 hwaddress = self.cache.get_link_address(bridgeport)
ca45cd9e 1161 if not ifupdownflags.flags.DRYRUN and not self._valid_ethaddr(hwaddress):
d486dd0d
JF
1162 self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
1163 bridgeport) + 'invalid ether addr %s'
1164 %hwaddress)
1165 continue
223ba5af 1166 self.iproute2.link_set_master(bridgeport, ifaceobj.name)
d486dd0d
JF
1167 newly_enslaved_ports.append(bridgeport)
1168 self.handle_ipv6([bridgeport], '1')
223ba5af 1169 self.iproute2.addr_flush(bridgeport)
3b01ed76 1170 except Exception as e:
d486dd0d
JF
1171 self.logger.error(str(e))
1172 pass
223ba5af
JF
1173
1174 self.iproute2.batch_commit()
1175 self.cache.force_add_slave_list(ifaceobj.name, newly_enslaved_ports)
d486dd0d
JF
1176
1177 if err:
1178 self.log_error('bridge configuration failed (missing ports)')
1179
7840bd2c
JF
1180 try:
1181 # to avoid any side effect we remove the dummy brport from the
1182 # list of supposedly newly configured ports.
1183 newly_enslaved_ports.remove(dummy_brport)
3218f49d 1184 except Exception:
7840bd2c
JF
1185 pass
1186
d486dd0d
JF
1187 return newly_enslaved_ports
1188
7840bd2c
JF
1189 def get_dummy_brport_name_for_bridge(self, bridge_name):
1190 """
1b845b1b
AP
1191 dummy brport will have user provided name if it's defined in 'bridge_always_up_dummy_brport' policy
1192 Otherwise dummy brport will have pre-formated name: brport-if$BRIDGE_IFINDEX
7840bd2c
JF
1193 That way we can avoid collision with existing interfaces
1194 """
1b845b1b
AP
1195 if self.bridge_always_up_dummy_brport:
1196 return self.bridge_always_up_dummy_brport
7840bd2c
JF
1197 # this can raise: NetlinkCacheIfnameNotFoundError
1198 return "brport-if%d" % self.cache.get_ifindex(bridge_name)
1199
1200 def bridge_always_up(self, bridge_name, newbridgeports_ordered):
1201 dummy_brport = self.get_dummy_brport_name_for_bridge(bridge_name)
1202
1203 if not self.cache.link_exists(dummy_brport):
1204 self.logger.info("%s: bridge-always-up yes: enslaving dummy port: %s" % (bridge_name, dummy_brport))
1205 self.netlink.link_add(ifname=dummy_brport, kind="dummy")
1206 self.netlink.link_up_force(dummy_brport)
1207
1208 newbridgeports_ordered.append(dummy_brport)
1209 return dummy_brport
1210
d486dd0d
JF
1211 def _process_bridge_maxwait(self, ifaceobj, portlist):
1212 maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
1213 if not maxwait: return
1214 try:
1215 maxwait = int(maxwait)
3218f49d 1216 except Exception:
d486dd0d
JF
1217 self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
1218 maxwait))
1219 return
1220 if not maxwait: return
1221 self.logger.info('%s: waiting for ports to go to fowarding state ..'
1222 %ifaceobj.name)
1223 try:
1224 starttime = time.time()
1225 while ((time.time() - starttime) < maxwait):
1226 if all([False for p in portlist
1227 if self.read_file_oneline(
1228 '/sys/class/net/%s/brif/%s/state'
1229 %(ifaceobj.name, p)) != '3']):
1230 break;
1231 time.sleep(1)
3b01ed76 1232 except Exception as e:
d486dd0d
JF
1233 self.log_warn('%s: unable to process maxwait: %s'
1234 %(ifaceobj.name, str(e)))
1235
1236 def _ints_to_ranges(self, ints):
3b01ed76 1237 for a, b in itertools.groupby(enumerate(ints), lambda x_y: x_y[1] - x_y[0]):
d486dd0d
JF
1238 b = list(b)
1239 yield b[0][1], b[-1][1]
1240
1241 def _ranges_to_ints(self, rangelist):
1242 """ returns expanded list of integers given set of string ranges
1243 example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
1244 """
1245 result = []
1246 try:
1247 for part in rangelist:
1248 if '-' in part:
1249 a, b = part.split('-')
1250 a, b = int(a), int(b)
3b01ed76 1251 result.extend(list(range(a, b + 1)))
d486dd0d
JF
1252 else:
1253 a = int(part)
1254 result.append(a)
3218f49d 1255 except Exception:
c46af1c9 1256 self.logger.warning('unable to parse vids \'%s\''
d486dd0d
JF
1257 %''.join(rangelist))
1258 pass
1259 return result
1260
1261 def _compress_into_ranges(self, vids_ints):
1262 return ['%d' %start if start == end else '%d-%d' %(start, end)
1263 for start, end in self._ints_to_ranges(vids_ints)]
1264
1265 def _diff_vids(self, vids1_ints, vids2_ints):
3b01ed76 1266 return set(vids2_ints).difference(vids1_ints), set(vids1_ints).difference(vids2_ints)
d486dd0d 1267
223ba5af 1268 def _compare_vids(self, vids1, vids2, pvid=None, expand_range=True):
d486dd0d
JF
1269 """ Returns true if the vids are same else return false """
1270
223ba5af
JF
1271 if expand_range:
1272 vids1_ints = self._ranges_to_ints(vids1)
1273 vids2_ints = self._ranges_to_ints(vids2)
1274 else:
1275 vids1_ints = self._ranges_to_ints(vids1)
1276 vids2_ints = vids2
3b01ed76 1277 set_diff = set(vids1_ints).symmetric_difference(vids2_ints)
d486dd0d
JF
1278 if pvid and int(pvid) in set_diff:
1279 set_diff.remove(int(pvid))
1280 if set_diff:
1281 return False
1282 else:
1283 return True
1284
1285 def _set_bridge_mcqv4src_compat(self, ifaceobj):
1286 #
1287 # Sets old style igmp querier
1288 #
1289 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
1290 if attrval:
1291 running_mcqv4src = {}
1292 if not ifupdownflags.flags.PERFMODE:
223ba5af 1293 running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
d486dd0d
JF
1294 mcqs = {}
1295 srclist = attrval.split()
1296 for s in srclist:
1297 k, v = s.split('=')
1298 mcqs[k] = v
1299
3b01ed76 1300 k_to_del = set(list(running_mcqv4src.keys())).difference(list(mcqs.keys()))
d486dd0d 1301 for v in k_to_del:
223ba5af 1302 self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)
3b01ed76 1303 for v in list(mcqs.keys()):
223ba5af 1304 self.iproute2.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
d486dd0d 1305 elif not ifupdownflags.flags.PERFMODE:
223ba5af 1306 running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
d486dd0d 1307 if running_mcqv4src:
3b01ed76 1308 for v in list(running_mcqv4src.keys()):
223ba5af 1309 self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)
d486dd0d
JF
1310
1311 def _set_bridge_vidinfo_compat(self, ifaceobj):
1312 #
1313 # Supports old style vlan vid info format
1314 # for compatibility
1315 #
1316 bridge_port_pvids = ifaceobj.get_attr_value_first('bridge-port-pvids')
1317 bridge_port_vids = ifaceobj.get_attr_value_first('bridge-port-vids')
1318 if not bridge_port_pvids and not bridge_port_vids:
1319 return
1320
1321 # Handle bridge vlan attrs
1322 # Install pvids
1323 if bridge_port_pvids:
1324 portlist = self.parse_port_list(ifaceobj.name, bridge_port_pvids)
1325 if not portlist:
1326 self.log_warn('%s: could not parse \'%s %s\''
1327 %(ifaceobj.name, 'bridge-port-pvids',
1328 bridge_port_pvids))
1329 return
1330 for p in portlist:
1331 try:
1332 (port, pvid) = p.split('=')
1333 pvid = int(pvid)
223ba5af 1334 running_pvid = self.cache.get_pvid(port)
d486dd0d
JF
1335 if running_pvid:
1336 if running_pvid == pvid:
1337 continue
1338 else:
223ba5af
JF
1339 self.iproute2.bridge_vlan_del_pvid(port, running_pvid)
1340 self.iproute2.bridge_vlan_add_pvid(port, pvid)
3b01ed76 1341 except Exception as e:
d486dd0d
JF
1342 self.log_warn('%s: failed to set pvid `%s` (%s)'
1343 %(ifaceobj.name, p, str(e)))
1344
1345 # install port vids
1346 if bridge_port_vids:
1347 portlist = self.parse_port_list(ifaceobj.name, bridge_port_vids)
1348 if not portlist:
1349 self.log_warn('%s: could not parse \'%s %s\'' %(ifaceobj.name,
1350 'bridge-port-vids', bridge_port_vids))
1351 return
1352 for p in portlist:
1353 try:
1354 (port, val) = p.split('=')
1355 vids = val.split(',')
1356 vids_int = self._ranges_to_ints(vids)
223ba5af 1357 _, running_vids = self.cache.get_pvid_and_vids(port)
d486dd0d
JF
1358 if running_vids:
1359 (vids_to_del, vids_to_add) = \
1360 self._diff_vids(vids_int, running_vids)
1361 if vids_to_del:
223ba5af 1362 self.iproute2.bridge_vlan_del_vid_list(port,
d486dd0d
JF
1363 self._compress_into_ranges(vids_to_del))
1364 if vids_to_add:
223ba5af 1365 self.iproute2.bridge_vlan_add_vid_list(port,
d486dd0d
JF
1366 self._compress_into_ranges(vids_to_add))
1367 else:
223ba5af 1368 self.iproute2.bridge_vlan_add_vid_list(port, vids_int)
3b01ed76 1369 except Exception as e:
d486dd0d
JF
1370 self.log_warn('%s: failed to set vid `%s` (%s)'
1371 %(ifaceobj.name, p, str(e)))
1372
1373 def _is_running_stp_state_on(self, bridgename):
1374 """ Returns True if running stp state is on, else False """
1375
1376 stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
1377 try:
1378 running_stp_state = self.read_file_oneline(stp_state_file)
1379 return running_stp_state and running_stp_state != '0'
3218f49d 1380 except Exception:
d486dd0d
JF
1381 return False
1382
1383 def _is_config_stp_state_on(self, ifaceobj):
1384 """ Returns true if user specified stp state is on, else False """
1385
1386 stp_attr = ifaceobj.get_attr_value_first('bridge-stp')
1387 if not stp_attr:
1388 return self.default_stp_on
1389 return utils.get_boolean_from_string(stp_attr)
1390
1391 def get_bridge_mcsnoop_value(self, ifaceobj):
1392 mcsnoop = ifaceobj.get_attr_value_first('bridge-mcsnoop')
3832dce6
JF
1393
1394 if mcsnoop:
1395 return mcsnoop
1396
1397 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
1398 if self._vxlan_bridge_default_igmp_snooping is not None:
1399 return self._vxlan_bridge_default_igmp_snooping
1400
1401 return self.get_attr_default_value("bridge-mcsnoop")
d486dd0d
JF
1402
1403 def fill_ifla_info_data_with_ifla_br_attribute(self,
1404 ifla_info_data,
1405 link_just_created,
1406 ifname,
1407 nl_attr,
1408 attr_name,
223ba5af
JF
1409 user_config,
1410 cached_value):
d486dd0d
JF
1411 try:
1412 translate_func = self._ifla_br_attributes_translate_user_config_to_netlink_map.get(nl_attr)
1413
1414 if not callable(translate_func):
1415 return
1416
1417 if not user_config:
1418 user_config = policymanager.policymanager_api.get_iface_default(
1419 module_name=self.__class__.__name__,
1420 ifname=ifname,
1421 attr=attr_name
1422 )
1423
223ba5af
JF
1424 if not link_just_created and cached_value is None:
1425 # the link already exists but we don't have any value
1426 # cached for this attr, it probably means that the
1427 # capability is not available on this system (i.e old kernel)
1428 self.logger.debug("%s: ignoring %s %s: capability probably not supported on this system"
1429 % (ifname, attr_name, user_config))
1430 return
d486dd0d
JF
1431
1432 if not user_config and not link_just_created and cached_value is not None:
1433 # there is no user configuration for this attribute
1434 # if the bridge existed before we need to check if
1435 # this attribute needs to be reset to default value
1436 default_value = self.get_attr_default_value(attr_name)
1437
1438 if default_value:
1439 # the attribute has a default value, we need to convert it to
1440 # netlink format to compare it with the cache value
1441 default_value_nl = translate_func(default_value) # default_value.lower()
1442
1443 if default_value_nl != cached_value:
1444 # the running value difers from the default value
1445 # but the user didn't specify any config
1446 # resetting attribute to default
1447 ifla_info_data[nl_attr] = default_value_nl
1448 self.logger.info('%s: reset %s to default: %s' % (ifname, attr_name, default_value))
1449 elif user_config:
1450 user_config_nl = translate_func(user_config) # user_config.lower()
1451
1452 if user_config_nl != cached_value:
1453 ifla_info_data[nl_attr] = user_config_nl
1454
1455 if cached_value is not None:
1456 self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
1457 else:
1458 self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
1459 except Exception as e:
1460 self.logger.warning('%s: %s: %s' % (ifname, attr_name, str(e)))
1461
1462 def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_aware):
1463 ifla_info_data = dict()
1464 ifname = ifaceobj.name
1465
223ba5af 1466 self.logger.info('%s: applying bridge settings' % ifname)
d486dd0d 1467
223ba5af
JF
1468 cached_ifla_info_data = self.cache.get_link_info_data(ifname)
1469
1470 try:
1471 # we compare the user value (or policy value) with the current running state
1472 # we need to divide the cached value by 100 to ignore small difference.
1473 # i.e. our default value is 31 but the kernel default seems to be 3125
c817ba4d 1474 cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] //= 100
223ba5af 1475 cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] *= 100
3218f49d 1476 except Exception:
223ba5af
JF
1477 pass
1478
3b01ed76 1479 for attr_name, nl_attr in self._ifla_br_attributes_map.items():
d486dd0d
JF
1480 self.fill_ifla_info_data_with_ifla_br_attribute(
1481 ifla_info_data=ifla_info_data,
1482 link_just_created=link_just_created,
1483 ifname=ifname,
1484 nl_attr=nl_attr,
1485 attr_name=attr_name,
223ba5af
JF
1486 user_config=ifaceobj.get_attr_value_first(attr_name),
1487 cached_value=cached_ifla_info_data.get(nl_attr)
d486dd0d
JF
1488 )
1489
01f27c67
JF
1490 # special cases ########################################################
1491
1492 # bridge-bridgeprio
1493 # if mstpctl-treeprio is configured on the bridge
1494 # do not reset the bridge-bridgeprio to the default value
1495 # NOTE: this is the case for every bridge/mstpctl attribute pairs.
1496 # TODO: more code should be added to handle this in the future.
1497 mstpctl_treeprio = ifaceobj.get_attr_value_first("mstpctl-treeprio")
1498 bridge_bridgeprio = ifaceobj.get_attr_value_first("bridge-bridgeprio")
1499
1500 if mstpctl_treeprio:
1501 self.logger.info("%s: mstpctl-treeprio attribute is set - ignorning bridge-bridgeprio" % ifname)
1502 else:
1503 self.fill_ifla_info_data_with_ifla_br_attribute(
1504 ifla_info_data=ifla_info_data,
1505 link_just_created=link_just_created,
1506 ifname=ifname,
1507 nl_attr=Link.IFLA_BR_PRIORITY,
1508 attr_name='bridge-bridgeprio',
1509 user_config=bridge_bridgeprio,
1510 cached_value=cached_ifla_info_data.get(Link.IFLA_BR_PRIORITY)
1511 )
1512
d486dd0d
JF
1513 # bridge-mcsnoop
1514 self.fill_ifla_info_data_with_ifla_br_attribute(
1515 ifla_info_data=ifla_info_data,
1516 link_just_created=link_just_created,
1517 ifname=ifname,
1518 nl_attr=Link.IFLA_BR_MCAST_SNOOPING,
1519 attr_name='bridge-mcsnoop',
223ba5af
JF
1520 user_config=self.get_bridge_mcsnoop_value(ifaceobj),
1521 cached_value=cached_ifla_info_data.get(Link.IFLA_BR_MCAST_SNOOPING)
1522
d486dd0d
JF
1523 )
1524
1525 # bridge-vlan-stats
1526 if bridge_vlan_aware:
1527 self.fill_ifla_info_data_with_ifla_br_attribute(
1528 ifla_info_data=ifla_info_data,
1529 link_just_created=link_just_created,
1530 ifname=ifname,
1531 nl_attr=Link.IFLA_BR_VLAN_STATS_ENABLED,
1532 attr_name='bridge-vlan-stats',
223ba5af
JF
1533 user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats,
1534 cached_value=cached_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
d486dd0d
JF
1535 )
1536
1537 try:
1538 if self._is_config_stp_state_on(ifaceobj):
1539 if not self._is_running_stp_state_on(ifname):
1540 ifla_info_data[Link.IFLA_BR_STP_STATE] = 1
1541 self.logger.info('%s: stp state reset, reapplying port settings' % ifname)
1542 ifaceobj.module_flags[ifaceobj.name] = \
1543 ifaceobj.module_flags.setdefault(self.name, 0) | \
1544 bridgeFlags.PORT_PROCESSED_OVERRIDE
1545 else:
1546 # If stp not specified and running stp state on, set it to off
1547 if self._is_running_stp_state_on(ifname):
1548 self.logger.info('%s: bridge-stp not specified but running: turning stp off')
1549 ifla_info_data[Link.IFLA_BR_STP_STATE] = 0
1550 except Exception as e:
1551 self.logger.warning('%s: bridge stp: %s' % (ifname, str(e)))
1552
1553 if ifla_info_data:
24931ffa 1554 self.netlink.link_set_bridge_info_data(ifname, ifla_info_data)
d486dd0d
JF
1555
1556 def _check_vids(self, ifaceobj, vids):
1557 ret = True
1558 for v in vids:
1559 try:
1560 if '-' in v:
1561 va, vb = v.split('-')
1562 va, vb = int(va), int(vb)
1563 self._handle_reserved_vlan(va, ifaceobj.name, end=vb)
1564 else:
1565 va = int(v)
1566 self._handle_reserved_vlan(va, ifaceobj.name)
1567 except exceptions.ReservedVlanException as e:
1568 raise e
1569 except Exception:
c46af1c9 1570 self.logger.warning('%s: unable to parse vid \'%s\''
d486dd0d
JF
1571 %(ifaceobj.name, v))
1572 return ret
1573
d486dd0d 1574 def _get_running_vids_n_pvid_str(self, ifacename):
223ba5af 1575 pvid, vids = self.cache.get_pvid_and_vids(ifacename)
d486dd0d
JF
1576
1577 if vids:
1578 ret_vids = self._compress_into_ranges(vids)
1579 else:
1580 ret_vids = None
1581
1582 if pvid:
1583 ret_pvid = '%s' %pvid
1584 else:
1585 ret_pvid = None
1586 return (ret_vids, ret_pvid)
1587
1588 def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid,
1589 isbridge):
1590 """ This method is a combination of methods _apply_bridge_vids and
1591 _apply_bridge_port_pvids above. A combined function is
1592 found necessary to do the deletes first and the adds later
1593 because kernel does honor vid info flags during deletes.
1594
1595 """
e537a6e6 1596 if not isbridge and (bportifaceobj.link_kind & ifaceLinkKind.VXLAN and not bportifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
d486dd0d
JF
1597 if not vids or not pvid or len(vids) > 1 or vids[0] != pvid:
1598 self._error_vxlan_in_vlan_aware_br(bportifaceobj,
1599 bportifaceobj.upperifaces[0])
1600 return
1601
1602 vids_int = self._ranges_to_ints(vids)
1603 try:
1604 pvid_int = int(pvid) if pvid else 0
1605 except Exception:
c46af1c9 1606 self.logger.warning('%s: unable to parse pvid \'%s\''
d486dd0d
JF
1607 %(bportifaceobj.name, pvid))
1608 pvid_int = 0
1609 pass
1610
1611 vids_to_del = []
1612 vids_to_add = vids_int
1613 pvid_to_del = None
1614 pvid_to_add = pvid_int
1615
1616 try:
1617 if not self._check_vids(bportifaceobj, vids):
1618 return
1619
223ba5af 1620 running_pvid, running_vids = self.cache.get_pvid_and_vids(bportifaceobj.name)
d486dd0d
JF
1621
1622 if not running_vids and not running_pvid:
1623 # There cannot be a no running pvid.
1624 # It might just not be in our cache:
1625 # this can happen if at the time we were
1626 # creating the bridge vlan cache, the port
1627 # was not part of the bridge. And we need
1628 # to make sure both vids and pvid is not in
1629 # the cache, to declare that our cache may
1630 # be stale.
1631 running_pvid = 1
1632 running_vids = [1]
1633
1634 if running_vids:
1635 (vids_to_del, vids_to_add) = \
1636 self._diff_vids(vids_to_add, running_vids)
1637
1638 if running_pvid:
1639 if running_pvid != pvid_int and running_pvid != 0:
1640 pvid_to_del = running_pvid
1641
1642 if (pvid_to_del and (pvid_to_del in vids_int) and
1643 (pvid_to_del not in vids_to_add)):
1644 # kernel deletes dont take into account
1645 # bridge vid flags and its possible that
1646 # the pvid deletes we do end up deleting
1647 # the vids. Be proactive and add the pvid
1648 # to the vid add list if it is in the vids
1649 # and not already part of vids_to_add.
1650 # This helps with a small corner case:
1651 # - running
1652 # pvid 100
1653 # vid 101 102
1654 # - new change is going to move the state to
1655 # pvid 101
1656 # vid 100 102
1657 vids_to_add.add(pvid_to_del)
1658 except exceptions.ReservedVlanException as e:
1659 raise e
3b01ed76 1660 except Exception as e:
d486dd0d
JF
1661 self.log_error('%s: failed to process vids/pvids'
1662 %bportifaceobj.name + ' vids = %s' %str(vids) +
1663 'pvid = %s ' %pvid + '(%s)' %str(e),
1664 bportifaceobj, raise_error=False)
1665 try:
1666 if vids_to_del:
1667 if pvid_to_add in vids_to_del:
1668 vids_to_del.remove(pvid_to_add)
e537a6e6
JF
1669
1670 vids_to_del = self.remove_bridge_vlans_mapped_to_vnis_from_vids_list(None, bportifaceobj, vids_to_del)
1671
223ba5af 1672 self.iproute2.bridge_vlan_del_vid_list_self(bportifaceobj.name,
d486dd0d
JF
1673 self._compress_into_ranges(
1674 vids_to_del), isbridge)
3b01ed76 1675 except Exception as e:
d486dd0d
JF
1676 self.log_warn('%s: failed to del vid `%s` (%s)'
1677 %(bportifaceobj.name, str(vids_to_del), str(e)))
1678
1679 try:
1680 if pvid_to_del:
223ba5af 1681 self.iproute2.bridge_vlan_del_pvid(bportifaceobj.name,
d486dd0d 1682 pvid_to_del)
3b01ed76 1683 except Exception as e:
d486dd0d
JF
1684 self.log_warn('%s: failed to del pvid `%s` (%s)'
1685 %(bportifaceobj.name, pvid_to_del, str(e)))
1686
1687 try:
1688 if vids_to_add:
223ba5af 1689 self.iproute2.bridge_vlan_add_vid_list_self(bportifaceobj.name,
d486dd0d
JF
1690 self._compress_into_ranges(
1691 vids_to_add), isbridge)
3b01ed76 1692 except Exception as e:
d486dd0d
JF
1693 self.log_error('%s: failed to set vid `%s` (%s)'
1694 %(bportifaceobj.name, str(vids_to_add),
1695 str(e)), bportifaceobj, raise_error=False)
1696
1697 try:
1698 if pvid_to_add and pvid_to_add != running_pvid:
223ba5af 1699 self.iproute2.bridge_vlan_add_pvid(bportifaceobj.name,
d486dd0d 1700 pvid_to_add)
3b01ed76 1701 except Exception as e:
d486dd0d
JF
1702 self.log_error('%s: failed to set pvid `%s` (%s)'
1703 %(bportifaceobj.name, pvid_to_add, str(e)),
1704 bportifaceobj)
1705
e537a6e6
JF
1706 def get_bridge_vlans_mapped_to_vnis_as_integer_list(self, ifaceobj):
1707 """
1708 Get all vlans that the user wants to configured in vlan-vni maps
1709 """
1710 try:
1711 vids = []
1712
1713 for vlans_vnis_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
1714 vids.extend(self._ranges_to_ints([vlans_vnis_map.split("=")[0]]))
1715
1716 return vids
1717 except Exception as e:
1718 self.logger.debug("get_bridge_vlans_mapped_to_vnis_as_integer_list: %s" % str(e))
1719 return []
1720
1721 def remove_bridge_vlans_mapped_to_vnis_from_vids_list(self, bridge_ifaceobj, vxlan_ifaceobj, vids_list):
1722 """
1723 For single vxlan we need to remove the vlans mapped to vnis
1724 from the vids list otherwise they will get removed from the brport
1725 """
1726 if not (vxlan_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
1727 return vids_list
1728
1729 user_config_vids = []
1730
1731 if bridge_ifaceobj:
1732 for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(bridge_ifaceobj):
1733 user_config_vids.append(vid)
1734
1735 if vxlan_ifaceobj:
1736 for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(vxlan_ifaceobj):
1737 user_config_vids.append(vid)
1738
1739 for vlan in user_config_vids:
1740 try:
1741 vids_list.remove(vlan)
3218f49d 1742 except Exception:
e537a6e6
JF
1743 pass
1744
1745 return vids_list
1746
d486dd0d
JF
1747 def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
1748 bridge_vids=None,
1749 bridge_pvid=None):
1750 vids = None
1751 pvids = None
1752 vids_final = []
1753 pvid_final = None
1754 bport_access = bportifaceobj.get_attr_value_first('bridge-access')
1755 if bport_access:
1756 vids = re.split(r'[\s\t]\s*', bport_access)
1757 pvids = vids
1758 allow_untagged = 'yes'
1759 self.check_bridge_port_vid_attrs(bportifaceobj)
1760 else:
1761 allow_untagged = bportifaceobj.get_attr_value_first('bridge-allow-untagged') or 'yes'
1762
1763 bport_vids = self.get_ifaceobj_bridge_vids_value(bportifaceobj)
1764 if bport_vids:
1765 vids = re.split(r'[\s\t,]\s*', bport_vids)
1766
1767 bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
1768 if bport_pvids:
1769 pvids = re.split(r'[\s\t]\s*', bport_pvids)
1770
1771 if vids:
1772 vids_final = vids
1773 elif bridge_vids:
1774 vids_final = bridge_vids
1775
1776 if allow_untagged == 'yes':
1777 if pvids:
1778 pvid_final = pvids[0]
1779 elif bridge_pvid:
1780 pvid_final = bridge_pvid
1781 else:
1782 pvid_final = '1'
1783 else:
1784 pvid_final = None
1785
1786 self._apply_bridge_vids_and_pvid(bportifaceobj, vids_final,
1787 pvid_final, False)
1788
1789 def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware):
1790 err = False
1791
1792 if (ifaceobj.get_attr_value_first('bridge-port-vids') and
1793 ifaceobj.get_attr_value_first('bridge-port-pvids')):
1794 # Old style bridge port vid info
1795 # skip new style setting on ports
1796 return
1797 self.logger.info('%s: applying bridge configuration '
1798 %ifaceobj.name + 'specific to ports')
1799
1800 bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
1801 if bridge_vids:
1802 bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
1803 else:
1804 bridge_vids = None
1805
1806 bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
1807 if bridge_pvid:
1808 bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
1809 else:
1810 bridge_pvid = None
1811
1812 if (ifaceobj.module_flags.get(self.name, 0x0) &
1813 bridgeFlags.PORT_PROCESSED_OVERRIDE):
1814 port_processed_override = True
1815 else:
1816 port_processed_override = False
1817
1818 bridgeports = self._get_bridge_port_list(ifaceobj)
1819 if not bridgeports:
1820 self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
1821 return
223ba5af 1822 self.iproute2.batch_start()
d486dd0d 1823 for bport in bridgeports:
223ba5af
JF
1824 # on link_set_master we need to wait until we cache the correct
1825 # notification and register the brport as slave
1826 if not self.cache.bridge_port_exists(ifaceobj.name, bport):
d486dd0d
JF
1827 self.logger.info('%s: skipping bridge config' %ifaceobj.name +
1828 ' for port %s (missing port)' %bport)
1829 continue
1830 self.logger.info('%s: processing bridge config for port %s'
1831 %(ifaceobj.name, bport))
1832 bportifaceobjlist = ifaceobj_getfunc(bport)
1833 if not bportifaceobjlist:
223ba5af 1834 continue
d486dd0d
JF
1835 for bportifaceobj in bportifaceobjlist:
1836 # Dont process bridge port if it already has been processed
1837 # and there is no override on port_processed
1838 if (not port_processed_override and
1839 (bportifaceobj.module_flags.get(self.name,0x0) &
1840 bridgeFlags.PORT_PROCESSED)):
1841 continue
1842 try:
1843 # Add attributes specific to the vlan aware bridge
1844 if bridge_vlan_aware:
1845 self._apply_bridge_vlan_aware_port_settings_all(
1846 bportifaceobj, bridge_vids, bridge_pvid)
1847 elif self.warn_on_untagged_bridge_absence:
1848 self._check_untagged_bridge(ifaceobj.name, bportifaceobj, ifaceobj_getfunc)
1849 except exceptions.ReservedVlanException as e:
1850 raise e
3b01ed76 1851 except Exception as e:
d486dd0d 1852 err = True
c46af1c9 1853 self.logger.warning('%s: %s' %(ifaceobj.name, str(e)))
d486dd0d 1854 pass
223ba5af 1855 self.iproute2.batch_commit()
d486dd0d
JF
1856 if err:
1857 raise Exception('%s: errors applying port settings' %ifaceobj.name)
1858
1859 def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc):
1860 if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN:
1861 lower_ifaceobj_list = ifaceobj_getfunc(bridgeportifaceobj.lowerifaces[0])
1862 if lower_ifaceobj_list and lower_ifaceobj_list[0] and \
1863 not lower_ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
c46af1c9 1864 self.logger.warning('%s: untagged bridge not found. Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.' % bridgename)
d486dd0d
JF
1865 self.warn_on_untagged_bridge_absence = False
1866
1867 def bridge_port_get_bridge_name(self, ifaceobj):
223ba5af 1868 bridgename = self.cache.get_bridge_name_from_port(ifaceobj.name)
d486dd0d
JF
1869 if not bridgename:
1870 # bridge port is not enslaved to a bridge we need to find
1871 # the bridge in it's upper ifaces then enslave it
1872 for u in ifaceobj.upperifaces:
223ba5af 1873 if self.cache.link_is_bridge(u):
d486dd0d
JF
1874 return True, u
1875 return False, None
1876 # return should_enslave port, bridgename
1877 return False, bridgename
1878
1879 def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port):
1880 if should_enslave_port:
223ba5af 1881 self.netlink.link_set_master(ifaceobj.name, bridge_name)
d486dd0d
JF
1882 self.handle_ipv6([ifaceobj.name], '1')
1883
1884 bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
1885 bridge_pvid = self._get_bridge_pvid(bridge_name, ifaceobj_getfunc)
1886 try:
1887 self._apply_bridge_vlan_aware_port_settings_all(ifaceobj, bridge_vids, bridge_pvid)
1888 except Exception as e:
1889 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
1890 return
1891
1892 def up_bridge_port(self, ifaceobj, ifaceobj_getfunc):
1893 should_enslave_port, bridge_name = self.bridge_port_get_bridge_name(ifaceobj)
1894
1895 if not bridge_name:
1896 # bridge doesn't exist
1897 return
1898
223ba5af 1899 vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridge_name)
d486dd0d
JF
1900 if vlan_aware_bridge:
1901 self.up_bridge_port_vlan_aware_bridge(ifaceobj,
1902 ifaceobj_getfunc,
1903 bridge_name,
1904 should_enslave_port)
1905
1906 bridge_ifaceobj = ifaceobj_getfunc(bridge_name)[0]
1907
1908 self.up_apply_brports_attributes(target_ports=[ifaceobj.name],
1909 ifaceobj=bridge_ifaceobj,
1910 ifaceobj_getfunc=ifaceobj_getfunc,
1911 bridge_vlan_aware=vlan_aware_bridge)
1912
1913 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED
1914
223ba5af 1915 def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_just_created):
d486dd0d
JF
1916 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
1917 if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
1918 return False
223ba5af
JF
1919 if not link_just_created and not self.cache.bridge_is_vlan_aware(ifaceobj.name):
1920 # if bridge-vlan-aware was added on a existing old-bridge, we need to reprocess all ports
d486dd0d
JF
1921 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
1922 return True
1923 return False
1924
1925 @staticmethod
1926 def parse_interface_list_value(user_config):
1927 config = dict()
1928 for entry in user_config.split():
1929 ifname, value = entry.split('=')
1930 config[ifname] = value
1931 return config
1932
223ba5af 1933 def sync_bridge_learning_to_vxlan_brport(self, bridge_name, brport_ifaceobj, brport_name, brport_ifla_info_slave_data, user_config_brport_learning_nl, cached_brport_learning):
d486dd0d
JF
1934 """
1935 brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
1936 and
1937 brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
1938
1939 Checks are not performed in this function and must be verified
1940 before. This is done this way to avoid calling this method on
1941 non vlan & bridge port interfaces thus wasting a bit less time
1942 """
1943
1944 kind = None
1945 ifla_info_data = {}
1946
223ba5af
JF
1947 if user_config_brport_learning_nl is None:
1948 user_config_brport_learning_nl = self.bridge_vxlan_port_learning
1949 # bridge-learning is not configured by the user or by a policy
1950 # use "bridge-vxlan-port-learning" policy to set bridge-learning (default on)
d486dd0d 1951
223ba5af
JF
1952 if user_config_brport_learning_nl != cached_brport_learning:
1953 brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] \
1954 = cached_brport_learning \
1955 = user_config_brport_learning_nl
d486dd0d 1956
223ba5af
JF
1957 self.logger.info(
1958 "%s: %s: set bridge-learning %s"
1959 % (bridge_name, brport_name, "on" if user_config_brport_learning_nl else "off")
1960 )
1961 else:
1962 # in this case, the current bridge-learning value is properly configured and
1963 # doesn't need to be reset. We need to make sure that BRPORT_LEARNING is not
1964 # part of ifla_info_slave_data.
1965 try:
1966 del brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING]
3218f49d 1967 except Exception:
223ba5af
JF
1968 pass
1969
1970 #
1971 # vxlan-learning sync:
1972 #
d486dd0d 1973
223ba5af
JF
1974 brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first("vxlan-learning")
1975 # if vxlan-learning is defined by the user or via policy file we need
1976 # to honor his config and not sync vxlan-learning with bridge-learning
d486dd0d 1977
223ba5af
JF
1978 if not brport_vxlan_learning_config:
1979 # check policy file
1980 brport_vxlan_learning_config = policymanager.policymanager_api.get_attr_default("vxlan", "vxlan-learning")
d486dd0d 1981
223ba5af
JF
1982 # convert vxlan-learning string to netlink value (if None use brport-learning value instead)
1983 brport_vxlan_learning_config_nl = utils.get_boolean_from_string(brport_vxlan_learning_config) \
1984 if brport_vxlan_learning_config \
1985 else cached_brport_learning
d486dd0d 1986
223ba5af
JF
1987 if brport_vxlan_learning_config_nl != self.cache.get_link_info_data_attribute(brport_name, Link.IFLA_VXLAN_LEARNING):
1988 self.logger.info(
1989 "%s: %s: vxlan learning and bridge learning out of sync: set vxlan-learning %s"
1990 % (bridge_name, brport_name, "on" if brport_vxlan_learning_config_nl else "off")
1991 )
1992 ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_vxlan_learning_config_nl}
1993 kind = "vxlan"
d486dd0d
JF
1994
1995 # if kind and ifla_info_data are set they will be added to the
1996 # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING
1997 return kind, ifla_info_data
1998
d486dd0d
JF
1999 def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
2000 ifname = ifaceobj.name
e537a6e6 2001 single_vxlan_device_ifaceobj = None
d486dd0d
JF
2002
2003 try:
2004 brports_ifla_info_slave_data = dict()
2005 brport_ifaceobj_dict = dict()
223ba5af
JF
2006 brport_name_list = []
2007
2008 cache_brports_ifla_info_slave_data = {}
2009
2010 port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED_OVERRIDE
d486dd0d 2011
223ba5af 2012 running_brports = self.cache.get_slaves(ifname)
d486dd0d 2013
223ba5af
JF
2014 # If target_ports is specified we want to configure only this
2015 # sub-list of port, we need to check if these ports are already
2016 # enslaved, if not they will be ignored.
2017 # If target_ports is not populated we will apply the brport
2018 # attributes on all running brport.
d486dd0d
JF
2019 if target_ports:
2020 new_targets = []
2021 for brport_name in target_ports:
2022 if brport_name not in running_brports:
2023 self.logger.info('%s: not enslaved to bridge %s: ignored for now' % (brport_name, ifname))
2024 else:
2025 new_targets.append(brport_name)
2026 running_brports = new_targets
2027
d486dd0d
JF
2028 for port in running_brports:
2029 brport_list = ifaceobj_getfunc(port)
2030 if brport_list:
223ba5af
JF
2031 port_already_processed = False
2032
2033 # ports just added to the bridge have to be processed
2034 if port not in newly_enslaved_ports:
2035 # check if brport was already processed
2036 for brportifaceobj in brport_list:
2037 if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED:
2038 # skip port if already processed (probably by `up_bridge_port`)
2039 port_already_processed = True
2040 self.logger.info("%s: port %s: already processed" % (ifname, port))
2041 break
2042
2043 if not port_already_processed:
2044 brport_name_list.append(port)
2045 brport_ifaceobj_dict[port] = brport_list[0]
2046 brports_ifla_info_slave_data[port] = dict()
2047
2048 if not ifupdownflags.flags.PERFMODE and port not in newly_enslaved_ports:
2049 # if the port has just been enslaved, info_slave_data is not cached yet
2050 cache_brports_ifla_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
2051 else:
2052 cache_brports_ifla_info_slave_data[port] = {}
2053
2054 if not brport_name_list:
2055 self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
2056 return
2057
2058 self.logger.info('%s: applying bridge port configuration: %s' % (ifname, brport_name_list))
2059
2060 cached_bridge_mcsnoop = self.cache.get_bridge_multicast_snooping(ifname)
d486dd0d
JF
2061
2062 bridge_ports_learning = {}
223ba5af
JF
2063 bridge_ports_vxlan_arp_suppress = {}
2064 cached_bridge_ports_learning = {}
d486dd0d
JF
2065
2066 # we iterate through all IFLA_BRPORT supported attributes
3b01ed76 2067 for attr_name, nl_attr in self._ifla_brport_attributes_map.items():
d486dd0d
JF
2068 br_config = ifaceobj.get_attr_value_first(attr_name)
2069 translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr)
2070
2071 if not translate_func:
2072 # if no translation function is found,
2073 # we ignore this attribute and continue
2074 continue
2075
2076 if not br_config:
2077 # user didn't specify any value for this attribute
2078 # looking at policy overrides
2079 br_config = policymanager.policymanager_api.get_iface_default(
2080 module_name=self.__class__.__name__,
2081 ifname=ifname,
2082 attr=attr_name
2083 )
2084
2085 if br_config:
2086 #if bridge_vlan_aware:
2087 # self.logger.info('%s: is a vlan-aware bridge, "%s %s" '
2088 # 'should be configured under the ports'
2089 # % (ifname, attr_name, br_config))
2090
2091 # convert the <interface-yes-no-0-1-list> and <interface-range-list> value to subdict
2092 # brport_name: { attr: value }
2093 # example:
2094 # bridge-portprios swp1=5 swp2=32
2095 # swp1: { bridge-portprios: 5 } swp2: { bridge-portprios: 32}
2096 if '=' in br_config:
2097 try:
2098 br_config = self.parse_interface_list_value(br_config)
3218f49d 2099 except Exception:
d486dd0d
JF
2100 self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config))
2101 continue
2102
3b01ed76 2103 for brport_ifaceobj in list(brport_ifaceobj_dict.values()):
d486dd0d
JF
2104 brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
2105 brport_name = brport_ifaceobj.name
2106
223ba5af
JF
2107 if not ifupdownflags.flags.PERFMODE:
2108 cached_value = cache_brports_ifla_info_slave_data.get(brport_name, {}).get(nl_attr, None)
d486dd0d
JF
2109 else:
2110 cached_value = None
2111
2112 if not brport_config:
2113 # if a brport attribute was specified under the bridge and not under the port
2114 # we assign the bridge value to the port. If an attribute is both defined under
2115 # the bridge and the brport we keep the value of the port and ignore the br val.
2116 if type(br_config) == dict:
2117 # if the attribute value was in the format interface-list-value swp1=XX swp2=YY
2118 # br_config is a dictionary, example:
2119 # bridge-portprios swp1=5 swp2=32 = {swp1: 5, swp2: 32}
2120 brport_config = br_config.get(brport_name)
2121 else:
2122 brport_config = br_config
2123
2124 if not brport_config:
2125 brport_config = policymanager.policymanager_api.get_iface_default(
2126 module_name=self.__class__.__name__,
2127 ifname=brport_name,
2128 attr=attr_name
2129 )
2130
2131 user_config = brport_config
2132
2133 # attribute specific work
2134 # This shouldn't be here but we don't really have a choice otherwise this
2135 # will require too much code duplication and will make the code very complex
223ba5af
JF
2136 if nl_attr == Link.IFLA_BRPORT_NEIGH_SUPPRESS:
2137 bridge_ports_vxlan_arp_suppress[brport_name] = user_config
d486dd0d 2138 try:
223ba5af
JF
2139 if user_config:
2140 if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2141 self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
2142 'is not supported on a non-vxlan port'
2143 % (ifaceobj.name, brport_name))
2144 continue
2145 elif bridge_vlan_aware:
2146 if not self.arp_nd_suppress_only_on_vxlan:
2147 user_config = self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
2148 elif self.arp_nd_suppress_only_on_vxlan and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2149 # ignore the case of VXLAN brport - handled later in the code
2150 continue
3218f49d 2151 except Exception:
d486dd0d
JF
2152 continue
2153 elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK:
2154 # special handking for group_fwd_mask because Cisco proprietary
2155 # protocol needs to be set via a private netlink attribute
2156 self.ifla_brport_group_fwd_mask(ifname, brport_name,
2157 brports_ifla_info_slave_data,
2158 user_config, cached_value)
2159 continue
2160
2161 #if brport_config:
2162 # if not bridge_vlan_aware:
2163 # self.logger.info('%s: %s: is not a vlan-aware bridge, "%s %s" '
2164 # 'should be configured under the bridge'
2165 # % (ifname, brport_name,
2166 # attr_name, brport_config))
2167
2168 if user_config:
2169 user_config_nl = translate_func(user_config)
2170 # check config value against running value
2171 if user_config_nl != cached_value:
2172 brports_ifla_info_slave_data[brport_name][nl_attr] = user_config_nl
2173 self.logger.info('%s: %s: set %s %s' % (ifname, brport_name, attr_name, user_config))
2174 self.logger.debug('(cache %s)' % cached_value)
2175
2176 if nl_attr == Link.IFLA_BRPORT_LEARNING:
2177 # for vxlan-learning sync purposes we need to save the user config for each brports.
2178 # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
2179 # IFLA_BRPORT_LEARNING if the user value is already configured and running
2180 # nevertheless we still need to check if the vxlan-learning is rightly synced with
2181 # the brport since it might go out of sync for X and Y reasons.
223ba5af 2182 # we also store the cached value to avoid an extra cache lookup.
d486dd0d 2183 bridge_ports_learning[brport_name] = user_config_nl
223ba5af 2184 cached_bridge_ports_learning[brport_name] = cached_value
d486dd0d
JF
2185
2186 elif cached_value is not None:
2187 # no config found, do we need to reset to default?
2188 default = self.get_attr_default_value(attr_name)
2189 if default:
2190 default_netlink = translate_func(default)
2191
223ba5af
JF
2192 if nl_attr == Link.IFLA_BRPORT_LEARNING:
2193 # for vxlan-learning sync purposes we need to save the user config for each brports.
2194 # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
2195 # IFLA_BRPORT_LEARNING if the user value is already configured and running
2196 # nevertheless we still need to check if the vxlan-learning is rightly synced with
2197 # the brport since it might go out of sync for X and Y reasons.
2198 # we also store the cached value to avoid an extra cache lookup.
2199 cached_bridge_ports_learning[brport_name] = cached_value
2200 bridge_ports_learning[brport_name] = self.bridge_vxlan_port_learning
2201
2202 if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
2203 # bridge-learning for vxlan device is handled separatly in sync_bridge_learning_to_vxlan_brport
2204 continue
2205
2206 if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
2207 # We don't query new slaves and not during boot
2208 try:
2209 if self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_PEER_LINK):
2210 if default_netlink != cached_value:
2211 self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
2212 % (ifname, brport_name))
2213 continue
2214 except Exception as e:
2215 self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
d486dd0d
JF
2216
2217 if default_netlink != cached_value:
2218 self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s'
2219 % (ifname, brport_name, attr_name, default))
2220 self.logger.debug('(cache %s)' % cached_value)
2221 brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink
2222
223ba5af
JF
2223 # is the current bridge (ifaceobj) a QinQ bridge?
2224 # This variable is initialized to None and will be
2225 # change to True/False, so that the check is only
2226 # performed once
2227 qinq_bridge = None
2228
d486dd0d 2229 # applying bridge port configuration via netlink
3b01ed76 2230 for brport_name, brport_ifla_info_slave_data in list(brports_ifla_info_slave_data.items()):
d486dd0d
JF
2231
2232 brport_ifaceobj = brport_ifaceobj_dict.get(brport_name)
2233 if (brport_ifaceobj
2234 and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
2235 and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
2236 # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val
2237 # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1}
2238 kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name,
d486dd0d
JF
2239 brport_ifaceobj,
2240 brport_name,
2241 brport_ifla_info_slave_data,
223ba5af
JF
2242 bridge_ports_learning.get(brport_name),
2243 cached_bridge_ports_learning.get(brport_name))
9f8f3b0d
JF
2244
2245 if (self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string(
80adb3c1 2246 self.get_bridge_mcsnoop_value(ifaceobj)
9f8f3b0d 2247 )) or cached_bridge_mcsnoop:
80adb3c1 2248 # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter"
9f8f3b0d
JF
2249 # is on and mcsnoop is on (or mcsnoop is already enabled on the
2250 # bridge, set 'bridge-portmcrouter 2' on vxlan ports (if not set by the user)
223ba5af
JF
2251 if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER) \
2252 and self.cache.get_bridge_port_multicast_router(brport_name) != 2:
80adb3c1
JF
2253 brport_ifla_info_slave_data[Link.IFLA_BRPORT_MULTICAST_ROUTER] = 2
2254 self.logger.info("%s: %s: vxlan bridge igmp snooping: enable port multicast router" % (ifname, brport_name))
223ba5af
JF
2255
2256 #
2257 # handling attribute: bridge-arp-nd-suppress
2258 # defaults to bridge-vxlan-arp-nd-suppress policy (default False)
2259 #
2260 user_config_neigh_suppress = bridge_ports_vxlan_arp_suppress.get(brport_name)
2261
2262 if user_config_neigh_suppress is None:
2263
2264 if qinq_bridge is None:
2265 # QinQ bridge hasn't been checked yet
2266 qinq_bridge = self.is_qinq_bridge(
2267 ifaceobj,
2268 brport_name,
2269 running_brports,
2270 brport_ifaceobj_dict,
2271 ifaceobj_getfunc
2272 )
2273
2274 if qinq_bridge:
2275 # exclude QinQ bridge from arp-nd-suppress default policy on
2276 config_neigh_suppress = 0
2277 self.logger.info("%s: QinQ bridge detected: %s: set bridge-arp-nd-suppress off" % (ifname, brport_name))
2278 else:
2279 config_neigh_suppress = self.bridge_vxlan_arp_nd_suppress_int
2280 else:
2281 config_neigh_suppress = int(utils.get_boolean_from_string(user_config_neigh_suppress))
2282
2283 brport_neigh_suppress_cached_value = self.cache.get_link_info_slave_data_attribute(
2284 brport_name,
2285 Link.IFLA_BRPORT_NEIGH_SUPPRESS
2286 )
2287
2288 if config_neigh_suppress != brport_neigh_suppress_cached_value:
2289 brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS] = config_neigh_suppress
2290
2291 if not user_config_neigh_suppress:
2292 # if the configuration is not explicitely defined by the user
2293 # we need report that the default behavior is enabled by policy
2294 self.logger.info(
2295 "%s: set bridge-arp-nd-suppress %s by default on vxlan port (%s)"
2296 % (ifname, "on" if self.bridge_vxlan_arp_nd_suppress else "off", brport_name)
2297 )
2298 else:
2299 # the user configuration (or policy) is already configured and running
2300 # we need to remove this attribute from the request dictionary
2301 try:
2302 del brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS]
3218f49d 2303 except Exception:
223ba5af
JF
2304 pass
2305
2306 #
e537a6e6 2307 # SINGLE VXLAN - enable IFLA_BRPORT_VLAN_TUNNEL
223ba5af
JF
2308 #
2309
e537a6e6
JF
2310 if brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
2311 single_vxlan_device_ifaceobj = brport_ifaceobj
2312 brport_vlan_tunnel_cached_value = self.cache.get_link_info_slave_data_attribute(
2313 brport_name,
2314 Link.IFLA_BRPORT_VLAN_TUNNEL
2315 )
2316
2317 if not brport_vlan_tunnel_cached_value:
2318 self.logger.info("%s: %s: enabling vlan_tunnel on single vxlan device" % (ifname, brport_name))
2319 brport_ifla_info_slave_data[Link.IFLA_BRPORT_VLAN_TUNNEL] = 1
2320
d486dd0d
JF
2321 else:
2322 kind = None
2323 ifla_info_data = {}
2324
2325 if brport_ifla_info_slave_data or ifla_info_data:
2326 try:
223ba5af
JF
2327 self.netlink.link_set_brport_with_info_slave_data(
2328 ifname=brport_name,
2329 kind=kind,
2330 ifla_info_data=ifla_info_data,
2331 ifla_info_slave_data=brport_ifla_info_slave_data
2332 )
d486dd0d
JF
2333 except Exception as e:
2334 self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e)))
2335
223ba5af 2336 self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
d486dd0d
JF
2337
2338 except Exception as e:
2339 self.log_error(str(e), ifaceobj)
2340
e537a6e6
JF
2341 if single_vxlan_device_ifaceobj:
2342 self.apply_bridge_port_vlan_vni_map(single_vxlan_device_ifaceobj)
2343
2344 def apply_bridge_port_vlan_vni_map(self, ifaceobj):
2345 """
2346 bridge vlan add vid <vlan-id> dev vxlan0
2347 bridge vlan add dev vxlan0 vid <vlan-id> tunnel_info id <vni>
2348 """
2349 vxlan_name = ifaceobj.name
2350 try:
2351 self.iproute2.batch_start()
2352 for vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
2353
2354 try:
2355 vlans_str, vni_str = vlan_vni_map.split("=")
3218f49d 2356 except Exception:
e537a6e6
JF
2357 return self.__warn_bridge_vlan_vni_map_syntax_error(vlan_vni_map)
2358
2359 vlans = self._ranges_to_ints([vlans_str])
2360 vnis = self._ranges_to_ints([vni_str])
2361
2362 if len(vlans) != len(vnis):
2363 return self.__warn_bridge_vlan_vni_map_syntax_error(vlan_vni_map)
2364
2365 # TODO: query the cache prio to executing those commands
2366 self.iproute2.bridge_vlan_add_vid_list_self(vxlan_name, vlans, False)
2367 self.iproute2.bridge_vlan_add_vlan_tunnel_info(vxlan_name, vlans, vnis)
2368
2369 self.iproute2.batch_commit()
2370 except Exception as e:
2371 self.log_error("%s: error while processing bridge-vlan-vni-map attribute: %s" % (vxlan_name, str(e)))
2372
2373 def __warn_bridge_vlan_vni_map_syntax_error(self, user_config_vlan_vni_map):
2374 self.logger.warning("%s: syntax error: bridge-vlan-vni-map %s" % user_config_vlan_vni_map)
2375
223ba5af
JF
2376 def is_qinq_bridge(self, ifaceobj, brport_name, running_brports, brport_ifaceobj_dict, ifaceobj_getfunc):
2377 """ Detect QinQ bridge
2378 Potential improvement: We could add a ifaceobj.link_privflags called
2379 BRIDGE_QINQ but for now it is not necessary.
2380 """
2381
2382 # bridge-vlan-aware case
2383 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
2384 return (ifaceobj.get_attr_value_first("bridge-vlan-protocol") or "").lower() == "802.1ad"
2385
2386 # old-bridge
2387 else:
2388 for qinq_running_brport in running_brports:
2389 if qinq_running_brport == brport_name:
2390 continue
2391
2392 qinq_running_brport_ifaceobj = brport_ifaceobj_dict.get(qinq_running_brport)
2393
2394 if not qinq_running_brport_ifaceobj:
2395 continue
2396
2397 if qinq_running_brport_ifaceobj.link_kind & ifaceLinkKind.VLAN:
2398 for lower_iface in qinq_running_brport_ifaceobj.lowerifaces or []:
2399 for lower_ifaceobj in ifaceobj_getfunc(lower_iface) or []:
2400 if (lower_ifaceobj.get_attr_value_first("vlan-protocol") or "").lower() == "802.1ad":
2401 return True
2402 return False
2403
2404 def bridge_process_vidinfo_mcqv4src_maxwait(self, ifaceobj):
2405 self._set_bridge_vidinfo_compat(ifaceobj)
2406 self._set_bridge_mcqv4src_compat(ifaceobj)
2407 self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
2408
d486dd0d
JF
2409 def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask):
2410 """
2411 Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI
2412 Since this is the only ifupdown2 attribute dealing with more than 1 netlink
2413 field we need to have special handling for that.
2414 """
2415 ifla_brport_group_fwd_mask = 0
2416 ifla_brport_group_fwd_maskhi = 0
9dfc8a44 2417
d486dd0d 2418 if user_config:
9dfc8a44 2419 for group in user_config.replace(",", " ").split():
d486dd0d
JF
2420 if not group:
2421 continue
2422
2423 callback = self.l2protocol_tunnel_callback.get(group)
2424
2425 if not callable(callback):
2426 self.logger.warning('%s: %s: bridge-l2protocol-tunnel ignoring invalid parameter \'%s\'' % (ifname, brport_name, group))
2427 else:
2428 ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi)
2429
2430 # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above
223ba5af 2431 cached_ifla_brport_group_fwd_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
d486dd0d
JF
2432
2433 log_mask_change = True
2434 # if user specify bridge-l2protocol-tunnel stp cdp
2435 # we need to set both MASK and MASKHI but we only want to log once
2436
2437 if cached_ifla_brport_group_fwd_mask is None:
2438 cached_ifla_brport_group_fwd_mask = 0
2439 if cached_ifla_brport_group_fwd_maskhi is None:
2440 cached_ifla_brport_group_fwd_maskhi = 0
2441
2442 # if the cache value is None it means that the kernel doesn't support this attribute
2443 # or that the cache is stale, we dumped this intf before it was enslaved in the bridge
2444
2445 if ifla_brport_group_fwd_mask != cached_ifla_brport_group_fwd_mask:
2446 if log_mask_change:
2447 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
2448 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_mask)
2449 log_mask_change = False
2450 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASK] = ifla_brport_group_fwd_mask
2451
2452 if ifla_brport_group_fwd_maskhi != cached_ifla_brport_group_fwd_maskhi:
2453 if log_mask_change:
2454 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
2455 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_maskhi)
2456 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASKHI] = ifla_brport_group_fwd_maskhi
2457
255ffb1f
JF
2458 def get_bridge_mtu(self, ifaceobj):
2459 user_config_mtu = ifaceobj.get_attr_value_first("mtu")
2460
2461 if not user_config_mtu:
2462 user_config_mtu = policymanager.policymanager_api.get_attr_default(
393524ab 2463 module_name="address",
255ffb1f
JF
2464 attr="mtu"
2465 )
2466
2467 try:
2468 if user_config_mtu:
393524ab
JF
2469 int(user_config_mtu)
2470 self.logger.info("%s: set bridge mtu %s" % (ifaceobj.name, user_config_mtu))
2471 return user_config_mtu
255ffb1f
JF
2472 except Exception as e:
2473 self.logger.warning("%s: invalid bridge mtu %s: %s" % (ifaceobj.name, user_config_mtu, str(e)))
393524ab 2474 return None
255ffb1f 2475
d486dd0d
JF
2476 def up_bridge(self, ifaceobj, ifaceobj_getfunc):
2477 ifname = ifaceobj.name
2478
2479 if ifupdownflags.flags.PERFMODE:
d486dd0d
JF
2480 link_exists = False
2481 else:
223ba5af 2482 link_exists = self.cache.link_exists(ifaceobj.name)
d486dd0d
JF
2483
2484 if not link_exists:
393524ab 2485 self.netlink.link_add_bridge(ifname)
223ba5af 2486 link_just_created = True
393524ab
JF
2487
2488 bridge_mtu = self.get_bridge_mtu(ifaceobj)
2489 if bridge_mtu:
2490 self.sysfs.link_set_mtu(ifname, bridge_mtu, int(bridge_mtu))
d486dd0d 2491 else:
223ba5af 2492 link_just_created = False
d486dd0d
JF
2493 self.logger.info('%s: bridge already exists' % ifname)
2494
223ba5af 2495 bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, link_just_created)
d486dd0d
JF
2496
2497 self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)
2498
2499 try:
2500 newly_enslaved_ports = self._add_ports(ifaceobj, ifaceobj_getfunc)
2501 self.up_apply_brports_attributes(ifaceobj, ifaceobj_getfunc, bridge_vlan_aware,
2502 newly_enslaved_ports=newly_enslaved_ports)
2503 except Exception as e:
2504 self.logger.warning('%s: apply bridge ports settings: %s' % (ifname, str(e)))
2505
2506 running_ports = ''
2507 try:
223ba5af 2508 running_ports = self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
2509 if not running_ports:
2510 return
d486dd0d
JF
2511 self._apply_bridge_port_settings_all(ifaceobj,
2512 ifaceobj_getfunc=ifaceobj_getfunc,
2513 bridge_vlan_aware=bridge_vlan_aware)
2514 except exceptions.ReservedVlanException as e:
2515 raise e
2516 except Exception as e:
2517 self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e)))
2518 finally:
2519 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
223ba5af 2520 self.iproute2.batch_start()
d486dd0d 2521 for p in running_ports:
1d35560d
JF
2522 ifaceobj_list = ifaceobj_getfunc(p)
2523 if (ifaceobj_list and ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN):
223ba5af 2524 self.iproute2.link_down(p)
d486dd0d 2525 continue
223ba5af
JF
2526 self.iproute2.link_up(p)
2527 try:
2528 self.iproute2.batch_commit()
2529 except Exception as e:
2530 # link set up on bridge ports failed - ignore and log debug
2531 self.logger.debug("%s: %s" % (ifname, str(e)))
d486dd0d
JF
2532
2533 try:
2534 self._up_bridge_mac(ifaceobj, ifaceobj_getfunc)
2535 except Exception as e:
2536 self.logger.warning('%s: setting bridge mac address: %s' % (ifaceobj.name, str(e)))
2537
2538 def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc):
2539 if self.bridge_mac_iface and self.bridge_mac_iface[0] and self.bridge_mac_iface[1]:
2540 return self.bridge_mac_iface
2541
2542 if self.bridge_mac_iface_list:
2543 self.logger.debug('bridge mac iface list: %s' % self.bridge_mac_iface_list)
2544
2545 for bridge_mac_intf in self.bridge_mac_iface_list:
2546 ifaceobj_list = ifaceobj_getfunc(bridge_mac_intf)
2547 iface_mac = None
2548
2549 if ifaceobj_list:
2550 for obj in ifaceobj_list:
2551 iface_user_configured_hwaddress = utils.strip_hwaddress(obj.get_attr_value_first('hwaddress'))
2552 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2553 if iface_user_configured_hwaddress:
2554 iface_mac = iface_user_configured_hwaddress
2555
223ba5af 2556 if not iface_mac and not self.cache.link_exists(bridge_mac_intf):
d486dd0d
JF
2557 continue
2558
2559 if not iface_mac:
223ba5af 2560 iface_mac = self.cache.get_link_address(bridge_mac_intf)
d486dd0d
JF
2561 # if hwaddress attribute is not configured we use the running mac addr
2562
2563 self.bridge_mac_iface = (bridge_mac_intf, iface_mac)
2564 return self.bridge_mac_iface
2565 elif self.bridge_set_static_mac_from_port:
2566 # no policy was provided, we need to get the first physdev or bond ports
2567 # and use its hwaddress to set the bridge mac
2568 for port in self._get_bridge_port_list_user_ordered(ifaceobj) or []:
2569 # iterate through the bridge-port list
2570 for port_obj in ifaceobj_getfunc(port) or []:
2571 # check if the port is a physdev (link_kind is null) or a bon
2572 if port_obj.link_kind != ifaceLinkKind.VXLAN:
2573 iface_user_configured_hwaddress = utils.strip_hwaddress(port_obj.get_attr_value_first('hwaddress'))
2574 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2575 if iface_user_configured_hwaddress:
2576 iface_mac = iface_user_configured_hwaddress.lower()
2577 # we need to "normalize" the user provided MAC so it can match with
2578 # what we have in the cache (data retrieved via a netlink dump by
2579 # nlmanager). nlmanager return all macs in lower-case
2580 else:
223ba5af 2581 iface_mac = self.cache.get_link_address(port)
d486dd0d
JF
2582
2583 if iface_mac:
2584 self.bridge_mac_iface = (port, iface_mac)
2585 return self.bridge_mac_iface
2586
2587 return None, None
2588
2589 def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac):
2590 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'):
223ba5af 2591 self.iproute2.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
d486dd0d
JF
2592
2593 def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc):
2594 """
2595 We have a day one bridge mac changing problem with changing ports
2596 (basically bridge mac changes when the port it inherited the mac from
2597 gets de-enslaved).
2598
2599 We have discussed this problem many times before and tabled it.
2600 The issue has aggravated with vxlan bridge ports having auto-generated
2601 random macs...which change on every reboot.
2602
2603 ifupdown2 extract from policy files an iface to select a mac from and
2604 configure it automatically.
2605 """
2606 if ifaceobj.get_attr_value('hwaddress'):
2607 # if the user configured a static hwaddress
2608 # there is no need to assign one
2609 return
2610
2611 ifname = ifaceobj.name
2612 mac_intf, bridge_mac = self._get_bridge_mac(ifaceobj, ifname, ifaceobj_getfunc)
2613 self.logger.debug("%s: _get_bridge_mac returned (%s, %s)"
2614 %(ifname, mac_intf, bridge_mac))
2615
2616 if bridge_mac:
2617 # if an interface is configured with the following attribute:
2618 # hwaddress 08:00:27:42:42:4
2619 # the cache_check won't match because nlmanager return "08:00:27:42:42:04"
2620 # from the kernel. The only way to counter that is to convert all mac to int
2621 # and compare the ints, it will increase perfs and be safer.
223ba5af 2622 cached_value = self.cache.get_link_address(ifname)
d486dd0d 2623 self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
223ba5af 2624 if cached_value and utils.mac_str_to_int(cached_value) == utils.mac_str_to_int(bridge_mac):
d486dd0d
JF
2625 # the bridge mac is already set to the bridge_mac_intf's mac
2626 return
2627
2628 self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf))
2629 try:
223ba5af 2630 self.netlink.link_set_address(ifname, bridge_mac) # force=True
d486dd0d
JF
2631 except Exception as e:
2632 self.logger.info('%s: %s' % (ifname, str(e)))
2633 # log info this error because the user didn't explicitly configured this
2634 else:
223ba5af 2635 self._add_bridge_mac_to_fdb(ifaceobj, self.cache.get_link_address(ifname))
d486dd0d
JF
2636
2637 def _up(self, ifaceobj, ifaceobj_getfunc=None):
2638 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
2639 self.up_bridge_port(ifaceobj, ifaceobj_getfunc)
2640
2641 elif ifaceobj.link_kind & ifaceLinkKind.BRIDGE:
2642 self.up_bridge(ifaceobj, ifaceobj_getfunc)
2643
c8e0a11e 2644 else:
3b01ed76 2645 bridge_attributes = list(self._modinfo.get('attrs', {}).keys())
c8e0a11e 2646
3b01ed76 2647 for ifaceobj_config_attr in list(ifaceobj.config.keys()):
c8e0a11e
JF
2648 if ifaceobj_config_attr in bridge_attributes:
2649 self.logger.warning('%s: invalid use of bridge attribute (%s) on non-bridge stanza'
2650 % (ifaceobj.name, ifaceobj_config_attr))
2651
d486dd0d
JF
2652 def _down(self, ifaceobj, ifaceobj_getfunc=None):
2653 if not self._is_bridge(ifaceobj):
2654 return
2655 ifname = ifaceobj.name
223ba5af 2656 if not self.cache.link_exists(ifname):
d486dd0d 2657 return
7840bd2c
JF
2658
2659 try:
2660 self.netlink.link_del(self.get_dummy_brport_name_for_bridge(ifname))
3218f49d 2661 except Exception:
7840bd2c
JF
2662 pass
2663
d486dd0d 2664 try:
223ba5af 2665 running_ports = self.cache.get_slaves(ifname)
d486dd0d
JF
2666 if running_ports:
2667 self.handle_ipv6(running_ports, '0')
2668 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
7f208e56
JF
2669 for p in running_ports:
2670 self.netlink.link_down(p)
d486dd0d
JF
2671 except Exception as e:
2672 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
2673 try:
223ba5af 2674 self.netlink.link_del(ifname)
d486dd0d
JF
2675 except Exception as e:
2676 ifaceobj.set_status(ifaceStatus.ERROR)
2677 self.logger.error(str(e))
2678 # netlink exception already contains the ifname
2679
2680 def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
2681 running_attrs = {}
2682 if ports:
2683 running_bridge_port_vids = ''
2684 for p in ports:
2685 try:
223ba5af 2686 _, running_vids = self.cache.get_pvid_and_vids(p)
d486dd0d
JF
2687 if running_vids:
2688 running_bridge_port_vids += ' %s=%s' %(p,
2689 ','.join(running_vids))
2690 except Exception:
2691 pass
2692 running_attrs['bridge-port-vids'] = running_bridge_port_vids
2693
2694 running_bridge_port_pvid = ''
2695 for p in ports:
2696 try:
223ba5af 2697 running_pvid = self.cache.get_pvid(p)
d486dd0d
JF
2698 if running_pvid:
2699 running_bridge_port_pvid += ' %s=%s' %(p,
2700 running_pvid)
2701 except Exception:
2702 pass
2703 running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
2704
223ba5af 2705 _, running_bridge_vids = self.cache.get_pvid_and_vids(ifaceobjrunning.name)
d486dd0d
JF
2706 if running_bridge_vids:
2707 running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
2708 return running_attrs
2709
2710 def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
2711 bridgeports=None):
2712 running_attrs = {}
2713
2714 # 'bridge-vids' under the bridge is all about 'vids' on the port.
2715 # so query the ports
2716 running_bridgeport_vids = []
2717 running_bridgeport_pvids = []
2718 for bport in bridgeports:
2719 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2720 if vids:
2721 running_bridgeport_vids.append(' '.join(vids))
2722 if pvid:
2723 running_bridgeport_pvids.append(pvid)
2724
2725 bridge_vids = None
2726 if running_bridgeport_vids:
2727 (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
2728 if freq == len(bridgeports):
2729 running_attrs['bridge-vids'] = vidval
2730 bridge_vids = vidval.split()
2731
2732 bridge_pvid = None
2733 if running_bridgeport_pvids:
2734 (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
2735 if freq == len(bridgeports) and vidval != '1':
2736 running_attrs['bridge-pvid'] = vidval
2737 bridge_pvid = vidval.split()[0]
2738
2739 # Go through all bridge ports and find their vids
2740 for bport in bridgeports:
2741 bportifaceobj = ifaceobj_getfunc(bport)
2742 if not bportifaceobj:
2743 continue
2744 bport_vids = []
2745 bport_pvid = None
2746 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2747 if vids and vids != bridge_vids:
2748 bport_vids = vids
2749 if pvid and pvid != bridge_pvid:
2750 bport_pvid = pvid
2751 if bport_vids and bport_pvid in bport_vids:
2752 bport_vids.remove(bport_pvid)
2753 if (not bport_vids and bport_pvid and bport_pvid != '1'):
2754 bportifaceobj[0].replace_config('bridge-access', bport_pvid)
2755 bportifaceobj[0].delete_config('bridge-pvid')
2756 bportifaceobj[0].delete_config('bridge-vids')
2757 else:
2758 if bport_pvid and bport_pvid != '1':
2759 bportifaceobj[0].replace_config('bridge-pvid', bport_pvid)
2760 else:
2761 # delete any stale bridge-vids under ports
2762 bportifaceobj[0].delete_config('bridge-pvid')
2763 if bport_vids:
2764 bportifaceobj[0].replace_config('bridge-vids',
2765 ' '.join(bport_vids))
2766 else:
2767 # delete any stale bridge-vids under ports
2768 bportifaceobj[0].delete_config('bridge-vids')
2769 return running_attrs
2770
2771 def _query_running_mcqv4src(self, ifaceobjrunning):
223ba5af 2772 running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobjrunning.name)
3b01ed76 2773 mcqs = ['%s=%s' %(v, i) for v, i in list(running_mcqv4src.items())]
d486dd0d
JF
2774 mcqs.sort()
2775 mcq = ' '.join(mcqs)
2776 return mcq
2777
2778 def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
2779 bridge_vlan_aware=False):
223ba5af
JF
2780
2781 ifname = ifaceobjrunning.name
d486dd0d
JF
2782 bridgeattrdict = {}
2783 userspace_stp = 0
2784 ports = None
d486dd0d
JF
2785 try:
2786 if self.systcl_get_net_bridge_stp_user_space() == '1':
2787 userspace_stp = 1
2788 except Exception as e:
2789 self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
2790
223ba5af
JF
2791 bridge_ifla_info_data = self.cache.get_link_info_data(ifname)
2792
d486dd0d
JF
2793
2794 # Fill bridge_ports and bridge stp attributes first
223ba5af
JF
2795 #
2796 # bridge-ports
2797 #
2798 bridgeattrdict["bridge-ports"] = [" ".join(self.cache.get_slaves(ifname))]
2799
2800 #
2801 # bridge-stp
2802 #
2803 cached_stp = bool(bridge_ifla_info_data.get(Link.IFLA_BR_STP_STATE))
2804
2805 if cached_stp != utils.get_boolean_from_string(
2806 self.get_mod_subattr("bridge-stp", "default")
2807 ):
2808 bridgeattrdict['bridge-stp'] = ["yes" if cached_stp else "no"]
2809
2810 skip_kernel_stp_attrs = cached_stp and userspace_stp
2811
2812 if skip_kernel_stp_attrs:
2813 bridge_attributes_map = {
2814 "bridge-mcqifaddr": Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
2815 "bridge-mcquerier": Link.IFLA_BR_MCAST_QUERIER,
2816 "bridge-mcrouter": Link.IFLA_BR_MCAST_ROUTER,
2817 "bridge-mcstats": Link.IFLA_BR_MCAST_STATS_ENABLED,
2818 "bridge-mcsnoop": Link.IFLA_BR_MCAST_SNOOPING,
2819 "bridge-mclmc": Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
2820 "bridge-mclmi": Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
2821 "bridge-mcqri": Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
2822 "bridge-mcqpi": Link.IFLA_BR_MCAST_QUERIER_INTVL,
2823 "bridge-mcsqc": Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
2824 "bridge-mcsqi": Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
2825 "bridge-mcmi": Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
2826 "bridge-mcqi": Link.IFLA_BR_MCAST_QUERY_INTVL,
2827 }
2828 else:
2829 bridge_attributes_map = dict(self._ifla_br_attributes_map)
2830 try:
2831 del bridge_attributes_map[Link.IFLA_BR_STP_STATE]
3218f49d 2832 except Exception:
223ba5af
JF
2833 pass
2834
2835 #
2836 # bridge-vlan-stats
2837 #
2838 cached_vlan_stats = bridge_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
2839
2840 if cached_vlan_stats != utils.get_boolean_from_string(
2841 self.get_mod_subattr("bridge-vlan-stats", "default")
2842 ):
2843 bridgeattrdict['bridge-vlan-stats'] = ["on" if cached_vlan_stats else "off"]
2844
2845 try:
2846 del bridge_attributes_map[Link.IFLA_BR_VLAN_STATS_ENABLED]
3218f49d 2847 except Exception:
223ba5af 2848 pass
d486dd0d 2849
c817ba4d 2850 lambda_nl_value_int_divide100 = lambda x: str(x // 100)
223ba5af
JF
2851 lambda_nl_value_to_yes_no_boolean = lambda x: "yes" if x else "no"
2852
2853 bridge_attr_value_netlink_to_string_dict = {
2854 Link.IFLA_BR_VLAN_PROTOCOL: lambda x: x.lower(), # return lower case vlan protocol
2855 Link.IFLA_BR_AGEING_TIME: lambda_nl_value_int_divide100,
2856 Link.IFLA_BR_MAX_AGE: lambda_nl_value_int_divide100,
2857 Link.IFLA_BR_FORWARD_DELAY: lambda_nl_value_int_divide100,
2858 Link.IFLA_BR_HELLO_TIME: lambda_nl_value_int_divide100,
2859 Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL: lambda_nl_value_int_divide100,
2860 Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: lambda_nl_value_int_divide100,
2861 Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL: lambda_nl_value_int_divide100,
2862 Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: lambda_nl_value_int_divide100,
2863 Link.IFLA_BR_MCAST_QUERIER_INTVL: lambda_nl_value_int_divide100,
2864 Link.IFLA_BR_MCAST_QUERY_INTVL: lambda_nl_value_int_divide100,
2865 Link.IFLA_BR_VLAN_FILTERING: lambda_nl_value_to_yes_no_boolean,
2866 Link.IFLA_BR_MCAST_QUERY_USE_IFADDR: lambda_nl_value_to_yes_no_boolean,
2867 Link.IFLA_BR_MCAST_SNOOPING: lambda_nl_value_to_yes_no_boolean,
2868 Link.IFLA_BR_MCAST_QUERIER: lambda_nl_value_to_yes_no_boolean,
2869 Link.IFLA_BR_MCAST_ROUTER: lambda_nl_value_to_yes_no_boolean,
2870 }
2871
3b01ed76 2872 for attr_name, attr_nl in bridge_attributes_map.items():
223ba5af
JF
2873 default_value = self.get_mod_subattr(attr_name, "default")
2874 cached_value = bridge_ifla_info_data.get(attr_nl)
2875
2876 if cached_value is None:
d486dd0d 2877 continue
223ba5af
JF
2878
2879 cached_value_string = bridge_attr_value_netlink_to_string_dict.get(attr_nl, str)(cached_value)
2880
2881 if default_value != cached_value_string:
2882 bridgeattrdict[attr_name] = [cached_value_string]
d486dd0d
JF
2883
2884 if bridge_vlan_aware:
2885 if not ports:
2886 ports = {}
2887 bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
2888 ifaceobj_getfunc,
3b01ed76 2889 list(ports.keys()))
d486dd0d
JF
2890 else:
2891 bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
2892 ports)
2893 if bridgevidinfo:
3b01ed76 2894 bridgeattrdict.update({k : [v] for k, v in list(bridgevidinfo.items())
d486dd0d
JF
2895 if v})
2896
2897 mcq = self._query_running_mcqv4src(ifaceobjrunning)
2898 if mcq:
2899 bridgeattrdict['bridge-mcqv4src'] = [mcq]
2900
2901 if skip_kernel_stp_attrs:
2902 return bridgeattrdict
2903
2904 # Do this only for vlan-UNAWARE-bridge
2905 if ports and not bridge_vlan_aware:
2906 portconfig = {'bridge-pathcosts' : '',
2907 'bridge-portprios' : '',
2908 'bridge-learning' : '',
2909 'bridge-unicast-flood' : '',
2910 'bridge-multicast-flood' : '',
223ba5af 2911 'bridge-broadcast-flood' : '',
d486dd0d
JF
2912 'bridge-arp-nd-suppress' : '',
2913 }
3b01ed76 2914 for p, v in list(ports.items()):
223ba5af 2915 v = str(self.cache.get_brport_cost(p))
d486dd0d
JF
2916 if v and v != self.get_mod_subattr('bridge-pathcosts',
2917 'default'):
2918 portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
2919
223ba5af 2920 v = str(self.cache.get_brport_priority(p))
d486dd0d
JF
2921 if v and v != self.get_mod_subattr('bridge-portprios',
2922 'default'):
2923 portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
2924
223ba5af 2925 v = utils.get_onff_from_onezero(self.cache.get_brport_learning(p))
d486dd0d
JF
2926 if (v and
2927 v != self.get_mod_subattr('bridge-learning', 'default')):
2928 portconfig['bridge-learning'] += ' %s=%s' %(p, v)
2929
223ba5af 2930 v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(p))
d486dd0d
JF
2931 if (v and
2932 v != self.get_mod_subattr('bridge-unicast-flood',
2933 'default')):
2934 portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v)
2935
223ba5af 2936 v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(p))
d486dd0d
JF
2937 if (v and
2938 v != self.get_mod_subattr('bridge-multicast-flood',
2939 'default')):
2940 portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v)
2941
223ba5af
JF
2942 v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(p))
2943 if (v and
2944 v != self.get_mod_subattr('bridge-broadcast-flood',
2945 'default')):
2946 portconfig['bridge-broadcast-flood'] += ' %s=%s' %(p, v)
2947
2948 v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(p))
d486dd0d
JF
2949 if (v and
2950 v != self.get_mod_subattr('bridge-arp-nd-suppress',
2951 'default')):
2952 portconfig['bridge-arp-nd-suppress'] += ' %s=%s' %(p, v)
2953
3b01ed76 2954 bridgeattrdict.update({k : [v] for k, v in list(portconfig.items())
d486dd0d
JF
2955 if v})
2956
2957 return bridgeattrdict
2958
2959 def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
2960 running_mcqs = self._query_running_mcqv4src(ifaceobj)
2961 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
2962 if attrval:
2963 mcqs = attrval.split()
2964 mcqs.sort()
2965 mcqsout = ' '.join(mcqs)
2966 ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
2967 running_mcqs, 1 if running_mcqs != mcqsout else 0)
2968
223ba5af
JF
2969 def _query_check_bridge_vidinfo(self, ifname, ifaceobj, ifaceobjcurr):
2970 #
2971 # bridge-port-vids
2972 #
2973 bridge_port_vids_user_config = ifaceobj.get_attr_value_first("bridge-port-vids")
2974 if bridge_port_vids_user_config:
2975
2976 port_list = self.parse_port_list(ifname, bridge_port_vids_user_config)
2977
2978 if not port_list:
2979 self.log_warn("%s: could not parse 'bridge-port-vids %s'"
2980 % (ifname, bridge_port_vids_user_config))
2981 ifaceobjcurr.update_config_with_status("bridge-port-vids", "ERROR", 1)
d486dd0d 2982 return
223ba5af
JF
2983
2984 error = False
2985 for port_config in port_list:
d486dd0d 2986 try:
223ba5af
JF
2987 port, vids_raw = port_config.split("=")
2988 packed_vids = vids_raw.split(",")
2989
2990 running_pvid, running_vids = self.cache.get_pvid_and_vids(port)
2991
2992 if not self._compare_vids(packed_vids, running_vids, pvid=running_pvid, expand_range=False):
2993 error = True
d486dd0d 2994
223ba5af
JF
2995 except Exception as e:
2996 self.log_warn("%s: failure checking vid %s (%s)" % (ifname, port_config, str(e)))
2997
2998 ifaceobjcurr.update_config_with_status("bridge-port-vids", bridge_port_vids_user_config, error)
2999
3000 #
3001 # bridge-port-pvids
3002 #
d486dd0d
JF
3003 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
3004 if attrval:
3005 portlist = self.parse_port_list(ifaceobj.name, attrval)
3006 if not portlist:
3007 self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
223ba5af 3008 % (ifname, attrval))
d486dd0d 3009 return
223ba5af
JF
3010
3011 error = False
3012 running_pvid_config = []
d486dd0d 3013 for p in portlist:
223ba5af
JF
3014 (port, pvid) = p.split('=')
3015 running_pvid, _ = self.cache.get_pvid_and_vids(port)
3016
3017 running_pvid_config.append("%s=%s" % (port, running_pvid))
3018
3019 if running_pvid != int(pvid):
3020 error = True
3021
3022 ifaceobjcurr.update_config_with_status(
3023 "bridge-port-pvids",
3024 " ".join(running_pvid_config),
3025 int(error)
3026 )
d486dd0d
JF
3027
3028 vids = self.get_ifaceobj_bridge_vids(ifaceobj)
3029 if vids[1]:
3030 ifaceobjcurr.update_config_with_status(vids[0], vids[1], -1)
3031
3032 def _query_check_snooping_wdefault(self, ifaceobj):
3033 if (ifupdownflags.flags.WITHDEFAULTS
3034 and not self._vxlan_bridge_default_igmp_snooping
3035 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
3036 ifaceobj.replace_config('bridge-mcsnoop', 'no')
3037
223ba5af 3038 def _query_check_bridge(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
d486dd0d
JF
3039 if not self._is_bridge(ifaceobj):
3040 return
d486dd0d 3041
223ba5af 3042 ifname = ifaceobj.name
d486dd0d 3043
223ba5af
JF
3044 if not self.cache.bridge_exists(ifname):
3045 self.logger.info("%s: bridge: does not exist" % (ifname))
d486dd0d 3046 return
d486dd0d 3047
223ba5af 3048 self._query_check_snooping_wdefault(ifaceobj)
d486dd0d 3049
223ba5af 3050 user_config_attributes = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
d486dd0d 3051
223ba5af
JF
3052 # add default attributes if --with-defaults is set
3053 if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in user_config_attributes:
3054 user_config_attributes.append('bridge-stp')
d486dd0d 3055
223ba5af
JF
3056 if not user_config_attributes:
3057 return
d486dd0d 3058
223ba5af
JF
3059 if "bridge-ports" in user_config_attributes:
3060 self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, self.cache.get_slaves(ifname), ifaceobj_getfunc)
d1265fd5 3061
223ba5af 3062 if "bridge-ports-condone-regex" in user_config_attributes:
f5c97c0d
MW
3063 ifaceobjcurr.update_config_with_status(
3064 "bridge-ports-condone-regex",
3065 self._get_bridge_port_condone_regex(ifaceobj, True),
3066 0
3067 )
223ba5af
JF
3068
3069 # Those attributes require separate handling
3070 filter_attributes = [
3071 "bridge-trunk",
3072 "bridge-ports",
3073 "bridge-vids",
3074 "bridge-trunk",
3075 "bridge-mcqv4src",
3076 "bridge-port-vids",
3077 "bridge-port-pvids",
3078 "bridge-l2protocol-tunnel",
3079 "bridge-ports-condone-regex"
3080 ]
3081
3082 ignore_attributes = (
3083 # bridge-pvid and bridge-vids on a bridge does not correspond
3084 # directly to a running config on the bridge. They correspond to
3085 # default values for the bridge ports. And they are already checked
3086 # against running config of the bridge port and reported against a
3087 # bridge port. So, ignore these attributes under the bridge. Use '2'
3088 # for ignore today. XXX: '2' will be mapped to a defined value in
3089 # subsequent patches.
3090 "bridge-pvid",
3091 "bridge-allow-untagged",
3092 )
3093 for attr in ignore_attributes:
3094 if attr in user_config_attributes:
3095 ifaceobjcurr.update_config_with_status(attr, ifaceobj.get_attr_value_first(attr), 2)
3096 filter_attributes.append(attr)
3097
3b01ed76 3098 bridge_config = set(user_config_attributes).difference(filter_attributes)
223ba5af
JF
3099 cached_ifla_info_data = self.cache.get_link_info_data(ifname)
3100
3101 self._query_check_bridge_attributes(ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data)
3102 self._query_check_brport_attributes_on_bridge(ifname, ifaceobj, ifaceobjcurr, bridge_config)
3103 self._query_check_bridge_vidinfo(ifname, ifaceobj, ifaceobjcurr)
3104 self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
3105 self._query_check_l2protocol_tunnel_on_bridge(ifname, ifaceobj, ifaceobjcurr)
3106
7840bd2c
JF
3107 def _query_check_bridge_always_up(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
3108 bridge_always_up = ifaceobj.get_attr_value_first("bridge-always-up")
3109
3110 if bridge_always_up:
3111 bridge_config.remove("bridge-always-up")
3112
3113 if utils.get_boolean_from_string(bridge_always_up):
3114 try:
3115 link_exists = self.cache.link_exists(self.get_dummy_brport_name_for_bridge(ifname))
3218f49d 3116 except Exception:
7840bd2c
JF
3117 link_exists = False
3118
3119 ifaceobjcurr.update_config_with_status(
3120 "bridge-always-up",
3121 "yes" if link_exists else "no",
3122 not link_exists
3123 )
3124
223ba5af
JF
3125 def _query_check_bridge_attributes(self, ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data):
3126 for attr in list(bridge_config):
3127 query_check_handler, netlink_attr = self._bridge_attribute_query_check_handler.get(attr, (None, None))
3128
3129 if callable(query_check_handler):
3130 query_check_handler(attr, ifaceobj.get_attr_value_first(attr), ifaceobjcurr, cached_ifla_info_data.get(netlink_attr))
3131 bridge_config.remove(attr)
3132
7840bd2c
JF
3133 self._query_check_bridge_always_up(ifaceobj.name, ifaceobj, ifaceobjcurr, bridge_config)
3134
223ba5af
JF
3135 def _query_check_brport_attributes_on_bridge(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
3136 brports_info_slave_data = {}
3137 # bridge_config should only have bridge-port-list attributes
3138 for attr in bridge_config:
3139 attr_nl = self._ifla_brport_attributes_map.get(attr)
3140 brport_query_check_handler = self._brport_attribute_query_check_handler.get(attr)
3141
3142 if not attr_nl or not brport_query_check_handler:
3143 self.logger.warning("%s: query-check: missing handler for attribute: %s (%s)" % (ifname, attr, attr_nl))
d486dd0d 3144 continue
223ba5af
JF
3145
3146 running_config = []
3147 status = 0
3148
3149 for port_config in self.parse_port_list(ifname, ifaceobj.get_attr_value_first(attr)) or []:
3150 port, config = port_config.split("=")
3151
3152 if not port in brports_info_slave_data:
3153 info_slave_data = brports_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
d486dd0d 3154 else:
223ba5af
JF
3155 info_slave_data = brports_info_slave_data[port]
3156
3157 port_config, port_status = brport_query_check_handler(port, config, info_slave_data.get(attr_nl))
3158
3159 running_config.append(port_config)
3160
3161 if port_status:
3162 status = 1
3163
3164 ifaceobjcurr.update_config_with_status(
3165 attr,
3166 " ".join(running_config),
3167 status
3168 )
3169
3170 @staticmethod
3171 def _query_check_br_attr_wait(attr, wait_value, ifaceobjcurr, __):
3172 ifaceobjcurr.update_config_with_status(attr, wait_value, 0)
3173
3174 def _query_check_br_attr_stp(self, attr, stp_value, ifaceobjcurr, cached_value):
3175 if not stp_value:
3176 if ifupdownflags.flags.WITHDEFAULTS:
3177 stp_value = "on" if self.default_stp_on else "off"
d486dd0d 3178 else:
223ba5af 3179 return
d486dd0d 3180
223ba5af 3181 user_config_to_nl = utils.get_boolean_from_string(stp_value)
d486dd0d 3182
223ba5af
JF
3183 ifaceobjcurr.update_config_with_status(
3184 attr,
3185 "yes" if cached_value else "no",
3186 user_config_to_nl != bool(cached_value)
3187 )
3188
3189 @staticmethod
3190 def _query_check_br_attr_int(attr, user_config, ifaceobjcurr, cached_value):
3191 ifaceobjcurr.update_config_with_status(
3192 attr,
3193 str(cached_value),
3194 int(user_config) != cached_value
3195 )
3196
3197 @staticmethod
3198 def _query_check_br_attr_int_divided100(attr, user_config, ifaceobjcurr, cached_value):
c817ba4d 3199 value = cached_value // 100
223ba5af
JF
3200 ifaceobjcurr.update_config_with_status(
3201 attr,
3202 str(value),
3203 int(user_config) != value
3204 )
3205
3206 @staticmethod
3207 def _query_check_br_attr_boolean(attr, user_config, ifaceobjcurr, cached_value):
3208 ifaceobjcurr.update_config_with_status(
3209 attr,
3210 "yes" if cached_value else "no",
3211 utils.get_boolean_from_string(user_config) != cached_value
3212 )
3213
3214 @staticmethod
3215 def _query_check_br_attr_boolean_on_off(attr, user_config, ifaceobjcurr, cached_value):
3216 ifaceobjcurr.update_config_with_status(
3217 attr,
3218 "on" if cached_value else "off",
3219 utils.get_boolean_from_string(user_config) != cached_value
3220 )
3221
3222 @staticmethod
3223 def _query_check_br_attr_string(attr, user_config, ifaceobjcurr, cached_value):
3224 ifaceobjcurr.update_config_with_status(
3225 attr,
3226 cached_value,
3227 user_config.lower() != cached_value
3228 )
3229
3230 @staticmethod
3231 def _query_check_brport_attr_boolean_on_off(port, user_config, cached_value):
3232 return "%s=%s" % (port, "on" if cached_value else "off"), utils.get_boolean_from_string(user_config) != cached_value
3233
3234 @staticmethod
3235 def _query_check_brport_attr_boolean_yes_no(port, user_config, cached_value):
3236 return "%s=%s" % (port, "yes" if cached_value else "no"), utils.get_boolean_from_string(user_config) != cached_value
3237
3238 @staticmethod
3239 def _query_check_brport_attr_int(port, user_config, cached_value):
3240 return "%s=%s" % (port, cached_value), int(user_config) != cached_value
3241
3242 @classmethod
3243 def _query_check_brport_attr_portmcrouter(cls, port, user_config, cached_value):
3244 return (
3245 "%s=%s" % (port, cls._ifla_brport_multicast_router_dict_int_to_str.get(cached_value)),
3246 cls._ifla_brport_multicast_router_dict_to_int.get(user_config) != cached_value
3247 )
3248
3249 ####################################################################################################################
d486dd0d 3250
d1265fd5 3251 def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc):
7840bd2c
JF
3252
3253 # if bridge-always-up is set we need to remove the dummy brport from the running_port_list
3254 if utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-always-up")):
3255 try:
3256 running_port_list.remove(self.get_dummy_brport_name_for_bridge(ifaceobj.name))
3218f49d 3257 except Exception:
7840bd2c
JF
3258 pass
3259
d1265fd5
JF
3260 bridge_all_ports = []
3261 for obj in ifaceobj_getfunc(ifaceobj.name) or []:
cc99b41d 3262 bridge_all_ports.extend(self._get_bridge_port_list(obj) or [])
d1265fd5
JF
3263
3264 if not running_port_list and not bridge_all_ports:
3265 return
3266
d1265fd5
JF
3267 try:
3268 port_list = self._get_ifaceobj_bridge_ports(ifaceobj).split()
3269 # we want to display the same bridge-ports list as provided
3270 # in the interfaces file but if this list contains regexes or
3271 # globs, for now, we won't try to change it.
3272 if 'regex' in port_list or 'glob' in port_list:
3273 port_list = running_port_list
3274 else:
3275 ordered = []
3276 for i in range(0, len(port_list)):
3277 if port_list[i] in running_port_list:
3278 ordered.append(port_list[i])
3279 port_list = ordered
3218f49d 3280 except Exception:
d1265fd5 3281 port_list = running_port_list
f5c97c0d
MW
3282
3283 difference = set(running_port_list).symmetric_difference(bridge_all_ports)
3284 bridge_port_condone_regex = self._get_bridge_port_condone_regex(ifaceobj)
3285
3286 if bridge_port_condone_regex:
3287 # Drop any condoned port from the difference set
3288 condone_ports = [port for port in difference if bridge_port_condone_regex.match(port)]
3289
3290 for port in condone_ports:
3291 try:
3292 difference.remove(port)
3293 except ValueError:
3294 pass
3295
3296 # Tag all condoned ports in brackets in output
3297 if port not in bridge_all_ports:
3298 port_list.append("(%s)" % port)
3299
3300 ifaceobjcurr.update_config_with_status(
3301 "bridge-ports",
3302 " ".join(port_list) if port_list else "",
3303 0 if not difference else 1
3304 )
d1265fd5 3305
d486dd0d
JF
3306 def get_ifaceobj_bridge_vids(self, ifaceobj):
3307 vids = ('bridge-vids', ifaceobj.get_attr_value_first('bridge-vids'))
3308 if not vids[1]:
3309 vids = ('bridge-trunk', ifaceobj.get_attr_value_first('bridge-trunk'))
3310 return vids
3311
3312 def get_ifaceobj_bridge_vids_value(self, ifaceobj):
3313 return self.get_ifaceobj_bridge_vids(ifaceobj)[1]
3314
3315 def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
3316 ifaceobjs = ifaceobj_getfunc(bridgename)
3317 for ifaceobj in ifaceobjs:
3318 vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
3319 if vids: return re.split(r'[\s\t,]\s*', vids)
3320 return None
3321
3322 def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
3323 ifaceobjs = ifaceobj_getfunc(bridgename)
3324 pvid = None
3325 for ifaceobj in ifaceobjs:
3326 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
3327 if pvid:
3328 break
3329 return pvid
3330
223ba5af
JF
3331 def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc):
3332 running_pvid, running_vids = self.cache.get_pvid_and_vids(ifname)
3333
3334 #
3335 # bridge-access
3336 #
3337 brport_vid_access_user_config = ifaceobj.get_attr_value_first("bridge-access")
3338
3339 if brport_vid_access_user_config:
3340 try:
3341 vid_int = int(brport_vid_access_user_config)
3342 except ValueError as e:
3343 ifaceobjcurr.update_config_with_status("bridge-access", brport_vid_access_user_config, 1)
3344 raise Exception("%s: bridge-access invalid value: %s" % (ifname, str(e)))
3345
3346 ifaceobjcurr.update_config_with_status(
3347 "bridge-access",
3348 str(running_pvid),
3349 running_pvid != vid_int or running_vids[0] != vid_int
3350 )
d486dd0d
JF
3351 return
3352
223ba5af
JF
3353 #
3354 # bridge-pvid
3355 #
3356 brport_pvid_user_config = ifaceobj.get_attr_value_first("bridge-pvid")
3357
3358 if brport_pvid_user_config:
3359 try:
3360 pvid = int(brport_pvid_user_config)
3361 except ValueError as e:
3362 ifaceobjcurr.update_config_with_status("bridge-pvid", brport_pvid_user_config, 1)
3363 raise Exception("%s: bridge-pvid invalid value: %s" % (ifname, str(e)))
3364
3365 ifaceobjcurr.update_config_with_status(
3366 "bridge-pvid",
3367 str(running_pvid),
3368 running_pvid != pvid
3369 )
d486dd0d
JF
3370 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
3371 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
3372 (ifaceobj.flags & iface.OLDEST_SIBLING))):
3373 # if the interface has multiple iface sections,
3374 # we check the below only for the oldest sibling
3375 # or the last iface section
223ba5af
JF
3376 try:
3377 pvid = int(self._get_bridge_pvid(bridge_name, ifaceobj_getfunc))
3378 except (TypeError, ValueError):
3379 pvid = 0
d486dd0d
JF
3380 if pvid:
3381 if not running_pvid or running_pvid != pvid:
3382 ifaceobjcurr.status = ifaceStatus.ERROR
3383 ifaceobjcurr.status_str = 'bridge pvid error'
223ba5af 3384 elif not running_pvid or running_pvid != 1:
d486dd0d
JF
3385 ifaceobjcurr.status = ifaceStatus.ERROR
3386 ifaceobjcurr.status_str = 'bridge pvid error'
3387
3388 attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
3389 if vids:
3390 vids = re.split(r'[\s\t]\s*', vids)
e537a6e6
JF
3391
3392 # Special treatment to make sure that the vlans mapped with vnis
3393 # (in single-vxlan context) are not mistaken for regular vlans.
3394 # We need to proactively remove them from the "running_vids"
3395 vlans_mapped_with_vnis = self.get_bridge_vlans_mapped_to_vnis_as_integer_list(ifaceobj)
3396 new_running_vids = []
3397 user_config_vids = self._ranges_to_ints(vids)
3398 for v in running_vids:
3399 if v in user_config_vids:
3400 new_running_vids.append(v)
3401 elif v not in vlans_mapped_with_vnis:
3402 new_running_vids.append(v)
3403 running_vids = new_running_vids
3404 #####################################################################
3405
223ba5af 3406 if not running_vids or not self._compare_vids(vids, running_vids, running_pvid, expand_range=False):
6d94c17b 3407 running_vids = [str(o) for o in running_vids]
d486dd0d
JF
3408 ifaceobjcurr.update_config_with_status(attr_name,
3409 ' '.join(running_vids), 1)
3410 else:
3411 ifaceobjcurr.update_config_with_status(attr_name,
3412 ' '.join(vids), 0)
3413 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
3414 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
3415 (ifaceobj.flags & iface.OLDEST_SIBLING))):
3416 # if the interface has multiple iface sections,
3417 # we check the below only for the oldest sibling
3418 # or the last iface section
3419
3420 # check if it matches the bridge vids
223ba5af 3421 bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
d486dd0d 3422 if (bridge_vids and (not running_vids or
223ba5af 3423 not self._compare_vids(bridge_vids, running_vids, running_pvid, expand_range=False))):
d486dd0d
JF
3424 ifaceobjcurr.status = ifaceStatus.ERROR
3425 ifaceobjcurr.status_str = 'bridge vid error'
3426
223ba5af
JF
3427 _query_check_brport_attributes = (
3428 "bridge-pvid",
3429 "bridge-vids",
3430 "bridge-trunk",
3431 "bridge-access",
3432 "bridge-pathcosts",
3433 "bridge-portprios",
3434 "bridge-portmcrouter",
3435 "bridge-learning",
3436 "bridge-portmcfl",
3437 "bridge-unicast-flood",
3438 "bridge-multicast-flood",
3439 "bridge-broadcast-flood",
3440 "bridge-arp-nd-suppress",
3441 "bridge-l2protocol-tunnel"
3442 )
3443
d486dd0d
JF
3444 def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
3445 ifaceobj_getfunc):
223ba5af
JF
3446
3447 ifname = ifaceobj.name
3448
3449 if not self.cache.link_is_bridge_port(ifname):
3450 # Mark all bridge brport attributes as failed
3451 ifaceobjcurr.check_n_update_config_with_status_many(
3452 ifaceobj, self._query_check_brport_attributes, 1
3453 )
d486dd0d 3454 return
223ba5af
JF
3455
3456 bridge_name = self.cache.get_bridge_name_from_port(ifname)
3457 if not bridge_name:
c46af1c9 3458 self.logger.warning("%s: unable to determine bridge name" % ifname)
d486dd0d
JF
3459 return
3460
223ba5af
JF
3461 if self.cache.bridge_is_vlan_aware(bridge_name):
3462 self._query_check_bridge_port_vidinfo(ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
3463
3464 brport_info_slave_data = self.cache.get_link_info_slave_data(ifname)
3465
3466 #
3467 # bridge-portmcfl
3468 #
3469 portmcfl = ifaceobj.get_attr_value_first("bridge-portmcfl")
3470
3471 if portmcfl:
3472 cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE)
3473
3474 ifaceobjcurr.update_config_with_status(
3475 "bridge-portmcfl",
3476 "yes" if cached_value else "no",
3477 utils.get_boolean_from_string(portmcfl) != cached_value
3478 )
3479
3480 #
3481 # bridge-portmcrouter
3482 #
3483 portmcrouter = ifaceobj.get_attr_value_first("bridge-portmcrouter")
3484
3485 if portmcrouter:
3486 cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER)
3487
3488 ifaceobjcurr.update_config_with_status(
3489 "bridge-portmcrouter",
3490 self._ifla_brport_multicast_router_dict_int_to_str.get(cached_value),
3491 self._ifla_brport_multicast_router_dict_to_int.get(portmcrouter) != cached_value
3492 )
3493
3494 #
3495 # bridge-learning
3496 # bridge-unicast-flood
3497 # bridge-multicast-flood
3498 # bridge-broadcast-flood
3499 # bridge-arp-nd-suppress
3500 #
3501 for attr_name, attr_nl in (
3502 ("bridge-learning", Link.IFLA_BRPORT_LEARNING),
3503 ("bridge-unicast-flood", Link.IFLA_BRPORT_UNICAST_FLOOD),
3504 ("bridge-multicast-flood", Link.IFLA_BRPORT_MCAST_FLOOD),
3505 ("bridge-broadcast-flood", Link.IFLA_BRPORT_BCAST_FLOOD),
3506 ("bridge-arp-nd-suppress", Link.IFLA_BRPORT_NEIGH_SUPPRESS),
3507 ):
3508 attribute_value = ifaceobj.get_attr_value_first(attr_name)
3509
3510 if not attribute_value:
3511 continue
3512
3513 cached_value = brport_info_slave_data.get(attr_nl)
3514
3515 ifaceobjcurr.update_config_with_status(
3516 attr_name,
3517 "on" if cached_value else "off",
3518 utils.get_boolean_from_string(attribute_value) != cached_value
3519 )
3520
3521 #
3522 # bridge-pathcosts
3523 # bridge-portprios
3524 #
3525 for attr_name, attr_nl in (
3526 ("bridge-pathcosts", Link.IFLA_BRPORT_COST),
3527 ("bridge-portprios", Link.IFLA_BRPORT_PRIORITY),
3528 ):
3529 attribute_value = ifaceobj.get_attr_value_first(attr_name)
3530
3531 if not attribute_value:
d486dd0d
JF
3532 continue
3533
223ba5af
JF
3534 cached_value = brport_info_slave_data.get(attr_nl)
3535
d486dd0d 3536 try:
223ba5af
JF
3537 ifaceobjcurr.update_config_with_status(
3538 attr_name,
3539 str(cached_value),
3540 int(attribute_value) != cached_value
3541 )
3542 except ValueError as e:
3543 ifaceobjcurr.update_config_with_status(attr_name, str(cached_value), 1)
3544 raise Exception("%s: %s invalid value: %s" % (ifname, attr_name, str(e)))
d486dd0d
JF
3545
3546 self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
3547
e537a6e6
JF
3548 #
3549 # bridge-vlan-vni-map
3550 #
3551 fail = False
3552 cached_vlans, cached_vnis = self.get_vlan_vni_ranges(self.cache.get_vlan_vni(ifaceobj.name))
3553
8a78cd07 3554 for bridge_vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map") or []:
e537a6e6
JF
3555
3556 if fail:
3557 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3558 continue
3559
3560 try:
3561 vlans_str, vni_str = bridge_vlan_vni_map.split("=")
3218f49d 3562 except Exception:
e537a6e6
JF
3563 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3564 return self.__warn_bridge_vlan_vni_map_syntax_error(bridge_vlan_vni_map)
3565
3566 vlans_list = self._ranges_to_ints([vlans_str]) # self.bridge_vlan_vni_map_convert_user_config_to_set(vlans_str)
3567 vnis_list = self._ranges_to_ints([vni_str]) #self.bridge_vlan_vni_map_convert_user_config_to_set(vni_str)
3568
3569 # since there can be multiple entry of bridge-vlan-vni-map
3570 # we could simply check that all vlans and vnis are correctly
3571 # set on the vxlan but we would probably miss the case where extra
3572 # vlans and vnis were added. So we ned to keep a copy of the cache
3573 # entry and pop vlans and svis from the cache copy as we iterate
3574 # through the user config. After processing only extra vlans and
3575 # vnis should be left.
3576 try:
3577 for vlan in vlans_list:
3578 cached_vlans.remove(vlan)
3218f49d 3579 except Exception:
e537a6e6
JF
3580 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3581 fail = True
3582 continue
3583
3584 try:
3585 for vni in vnis_list:
3586 cached_vnis.remove(vni)
3218f49d 3587 except Exception:
e537a6e6
JF
3588 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 1)
3589 fail = True
3590 continue
3591
3592 ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map, 0)
3593
3594 if not fail and (cached_vlans or cached_vnis):
3595 # cached_vlans and cached_vnis are not empty, it means more
3596 # vlans-vni maps were configured on the bridge port
3597 ifaceobjcurr.update_config_with_status(
3598 "bridge-vlan-vni-map",
3599 "%s=%s" % (cached_vlans, cached_vnis),
3600 1
3601 )
3602
3603 @staticmethod
3604 def get_vlan_vni_ranges(bridge_vlan_tunnel):
3605 vlans = []
3606 vnis = []
3607
8a78cd07
JF
3608 if not bridge_vlan_tunnel:
3609 return vlans, vnis
3610
e537a6e6
JF
3611 tunnel_vlan_range = None
3612 tunnel_vni_range = None
3613
3614 for tunnel_vlan, tunnel_vni, tunnel_flags in bridge_vlan_tunnel:
3615
3616 if tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
3617 tunnel_vlan_range = tunnel_vlan
3618 tunnel_vni_range = tunnel_vni
3619
3620 elif tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_END:
3621 vlans.extend(range(tunnel_vlan_range, tunnel_vlan + 1))
3622 vnis.extend(range(tunnel_vni_range, tunnel_vni + 1))
3623
3624 else:
3625 vlans.append(tunnel_vlan)
3626 vnis.append(tunnel_vni)
3627
3628 return vlans, vnis
3629
d486dd0d
JF
3630 def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr):
3631 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3632
3633 if user_config_l2protocol_tunnel:
3634 result = 0
3635 try:
3636 self._query_check_l2protocol_tunnel(ifaceobj.name, user_config_l2protocol_tunnel)
3637 except Exception as e:
3638 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
3639 result = 1
3640 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
3641
223ba5af 3642 def _query_check_l2protocol_tunnel_on_bridge(self, ifname, ifaceobj, ifaceobjcurr):
d486dd0d
JF
3643 """
3644 In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport
3645 We need to make sure that all ports comply with the mask given under the bridge
3646 """
3647 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3648
3649 if user_config_l2protocol_tunnel:
3650 if '=' in user_config_l2protocol_tunnel:
3651 try:
3652 config_per_port_dict = self.parse_interface_list_value(user_config_l2protocol_tunnel)
3b01ed76 3653 brport_list = list(config_per_port_dict.keys())
3218f49d 3654 except Exception:
d486dd0d
JF
3655 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, 1)
3656 return
3657 else:
3658 config_per_port_dict = {}
223ba5af
JF
3659 brport_list = self.cache.get_slaves(ifname)
3660
d486dd0d
JF
3661 try:
3662 for brport_name in brport_list:
3663 self._query_check_l2protocol_tunnel(
3664 brport_name,
3665 config_per_port_dict.get(brport_name) if config_per_port_dict else user_config_l2protocol_tunnel
3666 )
3667 result = 0
3668 except Exception as e:
3669 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
3670 result = 1
3671 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
3672
3673 def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel):
223ba5af
JF
3674 cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
3675 cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
d486dd0d
JF
3676
3677 for protocol in re.split(',|\s*', user_config_l2protocol_tunnel):
3678 callback = self.query_check_l2protocol_tunnel_callback.get(protocol)
3679
3680 if callable(callback):
3681 if not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3682 raise Exception('%s: bridge-l2protocol-tunnel: protocol \'%s\' not present (cached value: %d | %d)'
3683 % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi))
3684
3685 def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None):
223ba5af
JF
3686 cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
3687 cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
d486dd0d 3688 running_protocols = []
3b01ed76 3689 for protocol_name, callback in list(self.query_check_l2protocol_tunnel_callback.items()):
d486dd0d 3690 if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3b01ed76 3691 running_protocols = list(self.query_check_l2protocol_tunnel_callback.keys())
d486dd0d
JF
3692 running_protocols.remove('all')
3693 break
3694 elif callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
3695 running_protocols.append(protocol_name)
3696 if running_protocols:
3697 if brport_ifaceobj:
3698 brport_ifaceobj.update_config('bridge-l2protocol-tunnel', ' '.join(running_protocols))
3699 elif bridge_ifaceobj:
3700 current_config = bridge_ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
3701
3702 if current_config:
3703 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s %s=%s' % (current_config, brport_name, ','.join(running_protocols)))
3704 else:
3705 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s=%s' % (brport_name, ','.join(running_protocols)))
3706
3707 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
3708 if self._is_bridge(ifaceobj):
d1265fd5 3709 self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
d486dd0d
JF
3710 else:
3711 self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
3712 ifaceobj_getfunc)
3713
3714 def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
223ba5af 3715 if self.cache.bridge_is_vlan_aware(ifaceobjrunning.name):
d486dd0d
JF
3716 ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
3717 ifaceobjrunning.update_config_dict(self._query_running_attrs(
3718 ifaceobjrunning,
3719 ifaceobj_getfunc,
3720 bridge_vlan_aware=True))
3721 else:
3722 ifaceobjrunning.update_config_dict(self._query_running_attrs(
3723 ifaceobjrunning, None))
3724
3725 def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
3726 if self.systcl_get_net_bridge_stp_user_space() == '1':
3727 return
3728
223ba5af 3729 v = str(self.cache.get_brport_cost(ifaceobjrunning.name))
d486dd0d
JF
3730 if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
3731 ifaceobjrunning.update_config('bridge-pathcosts', v)
3732
223ba5af 3733 v = str(self.cache.get_brport_priority(ifaceobjrunning.name))
d486dd0d
JF
3734 if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
3735 ifaceobjrunning.update_config('bridge-portprios', v)
3736
3737 def _query_running_bridge_port(self, ifaceobjrunning,
3738 ifaceobj_getfunc=None):
3739
223ba5af 3740 bridgename = self.cache.get_bridge_name_from_port(
d486dd0d
JF
3741 ifaceobjrunning.name)
3742 bridge_vids = None
3743 bridge_pvid = None
3744 if not bridgename:
c46af1c9 3745 self.logger.warning('%s: unable to find bridgename'
d486dd0d
JF
3746 %ifaceobjrunning.name)
3747 return
3748
223ba5af 3749 if not self.cache.bridge_is_vlan_aware(bridgename):
d486dd0d
JF
3750 try:
3751 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0])
3752 except Exception as e:
3753 self.logger.debug('%s: q_query_running_bridge_l2protocol_tunnel: %s' % (ifaceobjrunning.name, str(e)))
3754 return
3755
3756 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, brport_ifaceobj=ifaceobjrunning)
3757
3758 (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str(
3759 ifaceobjrunning.name)
3760 if bridge_port_vids and bridge_port_pvid in bridge_port_vids:
3761 bridge_port_vids.remove(bridge_port_pvid)
3762
3763 bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
3764 if bridgeifaceobjlist:
3765 bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
3766 bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
3767
3768 if not bridge_port_vids and bridge_port_pvid:
3769 # must be an access port
3770 if bridge_port_pvid != '1':
3771 ifaceobjrunning.update_config('bridge-access',
3772 bridge_port_pvid)
3773 else:
3774 if bridge_port_vids:
3775 if (not bridge_vids or bridge_port_vids != bridge_vids):
3776 ifaceobjrunning.update_config('bridge-vids',
3777 ' '.join(bridge_port_vids))
3778 if bridge_port_pvid and bridge_port_pvid != '1':
3779 if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
3780 ifaceobjrunning.update_config('bridge-pvid',
3781 bridge_port_pvid)
3782
223ba5af 3783 v = utils.get_onff_from_onezero(self.cache.get_brport_learning(ifaceobjrunning.name))
d486dd0d
JF
3784 if v and v != self.get_mod_subattr('bridge-learning', 'default'):
3785 ifaceobjrunning.update_config('bridge-learning', v)
3786
223ba5af 3787 v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(ifaceobjrunning.name))
d486dd0d
JF
3788 if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'):
3789 ifaceobjrunning.update_config('bridge-unicast-flood', v)
3790
223ba5af 3791 v = utils.get_onff_from_onezero(self.cache.get_brport_multicast_flood(ifaceobjrunning.name))
d486dd0d
JF
3792 if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'):
3793 ifaceobjrunning.update_config('bridge-multicast-flood', v)
3794
223ba5af
JF
3795 v = utils.get_onff_from_onezero(self.cache.get_brport_broadcast_flood(ifaceobjrunning.name))
3796 if v and v != self.get_mod_subattr('bridge-broadcast-flood', 'default'):
3797 ifaceobjrunning.update_config('bridge-broadcast-flood', v)
3798
3799 v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(ifaceobjrunning.name))
d486dd0d
JF
3800 # Display running 'arp-nd-suppress' only on vxlan ports
3801 # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes'
3802 # otherwise, display on all bridge-ports
3803
3804 bportifaceobj = ifaceobj_getfunc(ifaceobjrunning.name)[0]
3805 if (v and
3806 v != self.get_mod_subattr('bridge-arp-nd-suppress', 'default') and
3807 (not self.arp_nd_suppress_only_on_vxlan or
3808 (self.arp_nd_suppress_only_on_vxlan and
3809 bportifaceobj.link_kind & ifaceLinkKind.VXLAN))):
3810 ifaceobjrunning.update_config('bridge-arp-nd-suppress', v)
3811
3812 self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
3813
3814 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
3815 try:
223ba5af 3816 if self.cache.bridge_exists(ifaceobjrunning.name):
d486dd0d 3817 self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
223ba5af 3818 elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
d486dd0d
JF
3819 self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
3820 except Exception as e:
3821 raise Exception('%s: %s' % (ifaceobjrunning.name, str(e)))
3822
3823 def _query(self, ifaceobj, **kwargs):
3824 """ add default policy attributes supported by the module """
3825 if (not (ifaceobj.link_kind & ifaceLinkKind.BRIDGE) or
3826 ifaceobj.get_attr_value_first('bridge-stp')):
3827 return
3828 if self.default_stp_on:
3829 ifaceobj.update_config('bridge-stp', 'yes')
3830
223ba5af 3831 def __re_evaluate_bridge_vxlan(self, ifaceobj, ifaceobj_getfunc=None):
d486dd0d 3832 """
223ba5af
JF
3833 Quick fix for BRIDGE_VXLAN
3834
3835 BRIDGE_VXLAN is not set on the bridge because the VXLAN hasn't been processed yet
3836 (because its defined after the bridge in /e/n/i), here is what happens:
3837
3838 - ifupdownmain:populate_dependency_info()
3839 - loops over all the intf from /e/n/i (with the example config:
3840 ['lo', 'eth0', 'swp1', 'swp2', 'bridge', 'vni-10', 'bridge.100', 'vlan100'])
3841 ----> bridge is first in the list of interface (that we care about)
3842
3843 - ifupdownmain:query_lowerifaces()
3844 - bridge:get_dependent is called (debug: bridge: evaluating port expr '['swp1', 'swp2', 'vni-10']')
3845 - ifupdownmain:preprocess_dependency_list()
3846 - calls ifupdownmain:_set_iface_role_n_kind() on all the brports:
3847
3848 in _set_iface_role_n_kind:
3849 ifaceobj is the brport
3850 upperifaceobj is the bridge
3851
3852 it tries to see if the bridge has a VXLAN:
3853
3854 if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
3855 and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
3856 upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
3857
3858 but because the bridge is first in the /e/n/i ifupdown2 didn't
3859 call vxlan:get_dependent_ifacenames so VXLAN is not set on ifaceobj
3860
3861 :return:
d486dd0d 3862 """
223ba5af
JF
3863 if not ifaceobj_getfunc:
3864 return
d486dd0d 3865
223ba5af
JF
3866 if ifaceobj.link_kind & ifaceLinkKind.BRIDGE and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
3867 for port in self._get_bridge_port_list(ifaceobj) or []:
3868 for brport_ifaceobj in ifaceobj_getfunc(port):
3869 if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
3870 ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
3871 return
3872
3873 elif ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
3874 for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
3875 for bridge_ifaceobj in ifaceobj_getfunc(iface) or []:
3876 bridge_ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
d486dd0d
JF
3877
3878 _run_ops = {
3879 'pre-up': _up,
3880 'post-down': _down,
3881 'query-checkcurr': _query_check,
3882 'query-running': _query_running,
3883 'query': _query
3884 }
3885
3886 def get_ops(self):
3887 """ returns list of ops supported by this module """
3b01ed76 3888 return list(self._run_ops.keys())
d486dd0d 3889
d486dd0d
JF
3890 def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
3891 """ run bridge configuration on the interface object passed as
3892 argument. Can create bridge interfaces if they dont exist already
3893
3894 Args:
3895 **ifaceobj** (object): iface object
3896
3897 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
3898 'query-running'
3899
3900 Kwargs:
3901 **query_ifaceobj** (object): query check ifaceobject. This is only
3902 valid when op is 'query-checkcurr'. It is an object same as
3903 ifaceobj, but contains running attribute values and its config
3904 status. The modules can use it to return queried running state
3905 of interfaces. status is success if the running state is same
3906 as user required state in ifaceobj. error otherwise.
3907 """
3908 op_handler = self._run_ops.get(operation)
3909 if not op_handler:
3910 return
d486dd0d 3911
223ba5af 3912 if (not self.requirements.bridge_utils_is_installed
d486dd0d 3913 and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
223ba5af 3914 and self.bridge_utils_missing_warning):
d486dd0d
JF
3915 self.logger.warning('%s: missing - bridge operation may not work as expected. '
3916 'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd)
223ba5af
JF
3917 self.bridge_utils_missing_warning = False
3918
3919 # make sure BRIDGE_VXLAN is set if we have a vxlan port
3920 self.__re_evaluate_bridge_vxlan(ifaceobj, ifaceobj_getfunc)
d486dd0d
JF
3921
3922 if operation == 'query-checkcurr':
3923 op_handler(self, ifaceobj, query_ifaceobj,
3924 ifaceobj_getfunc=ifaceobj_getfunc)
3925 else:
3926 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)