]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/bridge.py
addons: bridge: warn users if bridge attributes are used under non-bridge stanza
[mirror_ifupdown2.git] / ifupdown2 / addons / bridge.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import re
8 import time
9 import itertools
10
11 from sets import Set
12 from collections import Counter
13
14 try:
15 import ifupdown2.ifupdown.exceptions as exceptions
16 import ifupdown2.ifupdown.policymanager as policymanager
17 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
18
19 from ifupdown2.nlmanager.nlmanager import Link
20
21 from ifupdown2.ifupdown.iface import *
22 from ifupdown2.ifupdown.utils import utils
23 from ifupdown2.ifupdown.netlink import netlink
24
25 from ifupdown2.ifupdownaddons.cache import *
26 from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
27 from ifupdown2.ifupdownaddons.modulebase import moduleBase
28 except ImportError:
29 import ifupdown.exceptions as exceptions
30 import ifupdown.policymanager as policymanager
31 import ifupdown.ifupdownflags as ifupdownflags
32
33 from nlmanager.nlmanager import Link
34
35 from ifupdown.iface import *
36 from ifupdown.utils import utils
37 from ifupdown.netlink import netlink
38
39 from ifupdownaddons.cache import *
40 from ifupdownaddons.LinkUtils import LinkUtils
41 from ifupdownaddons.modulebase import moduleBase
42
43
44 class bridgeFlags:
45 PORT_PROCESSED = 0x1
46 PORT_PROCESSED_OVERRIDE = 0x2
47
48
49 class bridge(moduleBase):
50 """ ifupdown2 addon module to configure linux bridges """
51
52 _modinfo = { 'mhelp' : 'Bridge configuration module. Supports both ' +
53 'vlan aware and non vlan aware bridges. For the vlan ' +
54 'aware bridge, the port specific attributes must be ' +
55 'specified under the port. And for vlan unaware bridge ' +
56 'port specific attributes must be specified under the ' +
57 'bridge.',
58 'attrs' : {
59 'bridge-vlan-aware' :
60 {'help' : 'vlan aware bridge. Setting this ' +
61 'attribute to yes enables vlan filtering' +
62 ' on the bridge',
63 'validvals' : ['yes', 'no'],
64 'example' : ['bridge-vlan-aware yes/no'],
65 'default': 'no'
66 },
67 'bridge-ports' :
68 {'help' : 'bridge ports',
69 'multivalue' : True,
70 'required' : True,
71 'validvals': ['<interface-list>'],
72 'example' : ['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 'bridge-stp' :
76 {'help': 'bridge-stp yes/no',
77 'example' : ['bridge-stp no'],
78 'validvals' : ['yes', 'on', 'off', 'no'],
79 'default' : 'no'},
80 'bridge-bridgeprio' :
81 {'help': 'bridge priority',
82 'validrange' : ['0', '65535'],
83 'example' : ['bridge-bridgeprio 32768'],
84 'default' : '32768'},
85 'bridge-ageing' :
86 {'help': 'bridge ageing',
87 'validrange' : ['0', '65535'],
88 'example' : ['bridge-ageing 300'],
89 'default' : '300'},
90 'bridge-fd' :
91 { 'help' : 'bridge forward delay',
92 'validrange' : ['0', '255'],
93 'example' : ['bridge-fd 15'],
94 'default' : '15'},
95 'bridge-gcint' :
96 # XXX: recheck values
97 { 'help' : 'bridge garbage collection interval in secs',
98 'validrange' : ['0', '255'],
99 'example' : ['bridge-gcint 4'],
100 'default' : '4',
101 'compat' : True,
102 'deprecated': True},
103 'bridge-hello' :
104 { 'help' : 'bridge set hello time',
105 'validrange' : ['0', '255'],
106 'example' : ['bridge-hello 2'],
107 'default' : '2'},
108 'bridge-maxage' :
109 { 'help' : 'bridge set maxage',
110 'validrange' : ['0', '255'],
111 'example' : ['bridge-maxage 20'],
112 'default' : '20'},
113 'bridge-pathcosts' :
114 { 'help' : 'bridge set port path costs',
115 'validvals': ['<interface-range-list>'],
116 'validrange' : ['0', '65535'],
117 'example' : ['under the port (for vlan aware bridge): bridge-pathcosts 100',
118 'under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100'],
119 'default' : '100'},
120 'bridge-portprios' :
121 { 'help' : 'bridge port prios',
122 'validvals': ['<interface-range-list>'],
123 'validrange' : ['0', '65535'],
124 'example' : ['under the port (for vlan aware bridge): bridge-portprios 32',
125 'under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32'],
126 'default' : '32'},
127 'bridge-mclmc' :
128 { 'help' : 'set multicast last member count',
129 'validrange' : ['0', '255'],
130 'example' : ['bridge-mclmc 2'],
131 'default' : '2'},
132 'bridge-mcrouter' :
133 { 'help' : 'set multicast router',
134 'validvals' : ['yes', 'no', '0', '1', '2'],
135 'example' : ['bridge-mcrouter 1'],
136 'default': 'yes'
137 },
138 'bridge-mcsnoop' :
139 { 'help' : 'set multicast snooping',
140 'validvals' : ['yes', 'no', '0', '1'],
141 'default' : 'yes',
142 'example' : ['bridge-mcsnoop yes']},
143 'bridge-mcsqc' :
144 { 'help' : 'set multicast startup query count',
145 'validrange' : ['0', '255'],
146 'default' : '2',
147 'example' : ['bridge-mcsqc 2']},
148 'bridge-mcqifaddr' :
149 { 'help' : 'set multicast query to use ifaddr',
150 'validvals' : ['yes', 'no', '0', '1'],
151 'default' : 'no',
152 'example' : ['bridge-mcqifaddr no']},
153 'bridge-mcquerier' :
154 { 'help' : 'set multicast querier',
155 'validvals' : ['yes', 'no', '0', '1'],
156 'default' : 'no',
157 'example' : ['bridge-mcquerier no']},
158 'bridge-hashel' :
159 { 'help' : 'set hash elasticity',
160 'validrange' : ['0', '4096'],
161 'default' : '4',
162 'example' : ['bridge-hashel 4096']},
163 'bridge-hashmax' :
164 { 'help' : 'set hash max',
165 'validrange' : ['0', '4096'],
166 'default' : '512',
167 'example' : ['bridge-hashmax 4096']},
168 'bridge-mclmi' :
169 { 'help' : 'set multicast last member interval (in secs)',
170 'validrange' : ['0', '255'],
171 'default' : '1',
172 'example' : ['bridge-mclmi 1']},
173 'bridge-mcmi' :
174 { 'help' : 'set multicast membership interval (in secs)',
175 'validrange' : ['0', '255'],
176 'default' : '260',
177 'example' : ['bridge-mcmi 260']},
178 'bridge-mcqpi' :
179 { 'help' : 'set multicast querier interval (in secs)',
180 'validrange' : ['0', '255'],
181 'default' : '255',
182 'example' : ['bridge-mcqpi 255']},
183 'bridge-mcqi' :
184 { 'help' : 'set multicast query interval (in secs)',
185 'validrange' : ['0', '255'],
186 'default' : '125',
187 'example' : ['bridge-mcqi 125']},
188 'bridge-mcqri' :
189 { 'help' : 'set multicast query response interval (in secs)',
190 'validrange' : ['0', '255'],
191 'default' : '10',
192 'example' : ['bridge-mcqri 10']},
193 'bridge-mcsqi' :
194 { 'help' : 'set multicast startup query interval (in secs)',
195 'validrange' : ['0', '255'],
196 'default' : '31',
197 'example' : ['bridge-mcsqi 31']},
198 'bridge-mcqv4src' :
199 { 'help' : 'set per VLAN v4 multicast querier source address',
200 'validvals' : ['<number-ipv4-list>', ],
201 'multivalue' : True,
202 'compat' : True,
203 'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
204 'bridge-portmcrouter':
205 {
206 'help': 'set port multicast routers',
207 'validvals': ['<interface-disabled-automatic-enabled>'],
208 'example': [
209 'under the port (for vlan aware bridge): bridge-portmcrouter 0',
210 'under the port (for vlan aware bridge): bridge-portmcrouter 1',
211 'under the port (for vlan aware bridge): bridge-portmcrouter 2',
212 'under the port (for vlan aware bridge): bridge-portmcrouter disabled',
213 'under the port (for vlan aware bridge): bridge-portmcrouter automatic',
214 'under the port (for vlan aware bridge): bridge-portmcrouter enabled',
215 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2',
216 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled',
217 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1',
218 ]
219 },
220 'bridge-portmcfl' :
221 { 'help' : 'port multicast fast leave.',
222 'validvals': ['<interface-yes-no-0-1-list>'],
223 'validrange' : ['yes', 'no', '0', '1'],
224 'default' : 'no',
225 'example' : ['under the port (for vlan aware bridge): bridge-portmcfl no',
226 'under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no']},
227 'bridge-waitport' :
228 { 'help' : 'wait for a max of time secs for the' +
229 ' specified ports to become available,' +
230 'if no ports are specified then those' +
231 ' specified on bridge-ports will be' +
232 ' used here. Specifying no ports here ' +
233 'should not be used if we are using ' +
234 'regex or \"all\" on bridge_ports,' +
235 'as it wouldnt work.',
236 'default' : '0',
237 'validvals': ['<number-interface-list>'],
238 'example' : ['bridge-waitport 4 swp1 swp2']},
239 'bridge-maxwait' :
240 { 'help' : 'forces to time seconds the maximum time ' +
241 'that the Debian bridge setup scripts will ' +
242 'wait for the bridge ports to get to the ' +
243 'forwarding status, doesn\'t allow factional ' +
244 'part. If it is equal to 0 then no waiting' +
245 ' is done',
246 'validrange' : ['0', '255'],
247 'default' : '0',
248 'example' : ['bridge-maxwait 3']},
249 'bridge-vids' :
250 { 'help' : 'bridge port vids. Can be specified ' +
251 'under the bridge or under the port. ' +
252 'If specified under the bridge the ports ' +
253 'inherit it unless overridden by a ' +
254 'bridge-vids attribute under the port',
255 'multivalue' : True,
256 'validvals': ['<number-comma-range-list>'],
257 'example' : ['bridge-vids 4000',
258 'bridge-vids 2000 2200-3000'],
259 'aliases': ['bridge-trunk']},
260 'bridge-pvid' :
261 { 'help' : 'bridge port pvid. Must be specified under' +
262 ' the bridge port',
263 'validrange' : ['0', '4096'],
264 'example' : ['bridge-pvid 1']},
265 'bridge-access' :
266 { 'help' : 'bridge port access vlan. Must be ' +
267 'specified under the bridge port',
268 'validrange' : ['1', '4094'],
269 'example' : ['bridge-access 300']},
270 'bridge-allow-untagged' :
271 { 'help' : 'indicate if the bridge port accepts ' +
272 'untagged packets or not. Must be ' +
273 'specified under the bridge port. ' +
274 'Default is \'yes\'',
275 'validvals' : ['yes', 'no'],
276 'example' : ['bridge-allow-untagged yes'],
277 'default' : 'yes'},
278 'bridge-port-vids' :
279 { 'help' : 'bridge vlans',
280 'compat': True,
281 'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
282 'bridge-port-pvids' :
283 { 'help' : 'bridge port vlans',
284 'compat': True,
285 'example' : ['bridge-port-pvids bond0=100 bond1=200']},
286 'bridge-learning' :
287 { 'help' : 'bridge port learning flag',
288 'validvals': ['on', 'off', '<interface-on-off-list>'],
289 'default': 'on',
290 'example' : ['bridge-learning off']},
291 'bridge-igmp-version' :
292 { 'help' : 'mcast igmp version',
293 'validvals': ['2', '3'],
294 'default' : '2',
295 'example' : ['bridge-igmp-version 2']},
296 'bridge-mld-version':
297 { 'help' : 'mcast mld version',
298 'validvals': ['1', '2'],
299 'default' : '1',
300 'example' : ['bridge-mld-version 1']},
301 'bridge-unicast-flood' :
302 { 'help' : 'bridge port unicast flood flag',
303 'validvals': ['on', 'off', '<interface-on-off-list>'],
304 'default': 'on',
305 'example' : ['under the port (for vlan aware bridge): bridge-unicast-flood on',
306 'under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on']},
307 'bridge-multicast-flood' :
308 { 'help' : 'bridge port multicast flood flag',
309 'validvals': ['on', 'off', '<interface-on-off-list>'],
310 'default': 'on',
311 'example' : ['under the port (for vlan aware bridge): bridge-multicast-flood on',
312 'under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on']},
313 'bridge-vlan-protocol' :
314 { 'help' : 'bridge vlan protocol',
315 'default' : '802.1q',
316 'validvals': ['802.1q', '802.1ad'],
317 'example' : ['bridge-vlan-protocol 802.1q']},
318 'bridge-vlan-stats' :
319 { 'help' : 'bridge vlan stats',
320 'default' : 'off',
321 'validvals': ['on', 'off'],
322 'example' : ['bridge-vlan-stats off']},
323 'bridge-arp-nd-suppress' :
324 { 'help' : 'bridge port arp nd suppress flag',
325 'validvals': ['on', 'off', '<interface-on-off-list>'],
326 'default': 'off',
327 'example' : ['under the port (for vlan aware bridge): bridge-arp-nd-suppress on',
328 'under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on']},
329 'bridge-mcstats' :
330 { 'help' : 'bridge multicast stats',
331 'default' : 'off',
332 'validvals': ['on', 'off'],
333 'example' : ['bridge-mcstats off']},
334 'bridge-l2protocol-tunnel': {
335 'help': 'layer 2 protocol tunneling',
336 'validvals': [ # XXX: lists all combinations, should move to
337 # a better representation
338 'all',
339 'cdp',
340 'cdp lacp',
341 'cdp lacp lldp',
342 'cdp lacp lldp pvst',
343 'cdp lacp lldp stp',
344 'cdp lacp pvst',
345 'cdp lacp pvst stp',
346 'cdp lacp stp',
347 'cdp lldp',
348 'cdp lldp pvst',
349 'cdp lldp pvst stp',
350 'cdp lldp stp',
351 'cdp pvst',
352 'cdp pvst stp',
353 'cdp stp',
354 'lacp',
355 'lacp lldp',
356 'lacp lldp pvst',
357 'lacp lldp pvst stp',
358 'lacp lldp stp',
359 'lacp pvst',
360 'lacp pvst stp',
361 'lacp stp',
362 'lldp',
363 'lldp pvst',
364 'lldp pvst stp',
365 'lldp stp',
366 'pvst',
367 'pvst stp',
368 'stp',
369 '<interface-l2protocol-tunnel-list>'],
370 'example': [
371 'under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all',
372 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst',
373 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst',
374 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp',
375 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel all'
376 ]
377 }
378 }}
379
380 # Netlink attributes not associated with ifupdown2
381 # attributes are left commented-out for a future use
382 # and kept in order :)
383 _ifla_br_attributes_map = (
384 # Link.IFLA_BR_UNSPEC,
385 ('bridge-fd', Link.IFLA_BR_FORWARD_DELAY),
386 ('bridge-hello', Link.IFLA_BR_HELLO_TIME),
387 ('bridge-maxage', Link.IFLA_BR_MAX_AGE),
388 ('bridge-ageing', Link.IFLA_BR_AGEING_TIME),
389 ('bridge-stp', Link.IFLA_BR_STP_STATE),
390 ('bridge-bridgeprio', Link.IFLA_BR_PRIORITY),
391 ('bridge-vlan-aware', Link.IFLA_BR_VLAN_FILTERING),
392 ('bridge-vlan-protocol', Link.IFLA_BR_VLAN_PROTOCOL),
393 # Link.IFLA_BR_GROUP_FWD_MASK,
394 # Link.IFLA_BR_ROOT_ID,
395 # Link.IFLA_BR_BRIDGE_ID,
396 # Link.IFLA_BR_ROOT_PORT,
397 # (Link.IFLA_BR_ROOT_PATH_COST,,
398 # Link.IFLA_BR_TOPOLOGY_CHANGE,
399 # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
400 # Link.IFLA_BR_HELLO_TIMER,
401 # Link.IFLA_BR_TCN_TIMER,
402 # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
403 # Link.IFLA_BR_GC_TIMER,
404 # Link.IFLA_BR_GROUP_ADDR,
405 # Link.IFLA_BR_FDB_FLUSH,
406 ('bridge-mcrouter', Link.IFLA_BR_MCAST_ROUTER),
407 #('bridge-mcsnoop', Link.IFLA_BR_MCAST_SNOOPING), # requires special handling so we won't loop on this attr
408 ('bridge-mcqifaddr', Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
409 ('bridge-mcquerier', Link.IFLA_BR_MCAST_QUERIER),
410 ('bridge-hashel', Link.IFLA_BR_MCAST_HASH_ELASTICITY),
411 ('bridge-hashmax', Link.IFLA_BR_MCAST_HASH_MAX),
412 ('bridge-mclmc', Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
413 ('bridge-mcsqc', Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
414 ('bridge-mclmi', Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
415 ('bridge-mcmi', Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
416 ('bridge-mcqpi', Link.IFLA_BR_MCAST_QUERIER_INTVL),
417 ('bridge-mcqi', Link.IFLA_BR_MCAST_QUERY_INTVL),
418 ('bridge-mcqri', Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
419 ('bridge-mcsqi', Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
420 # Link.IFLA_BR_NF_CALL_IPTABLES,
421 # Link.IFLA_BR_NF_CALL_IP6TABLES,
422 # Link.IFLA_BR_NF_CALL_ARPTABLES,
423 # Link.IFLA_BR_VLAN_DEFAULT_PVID,
424 # Link.IFLA_BR_PAD,
425 # (Link.IFLA_BR_VLAN_STATS_ENABLED, 'bridge-vlan-stats'), # already dealt with, in a separate loop
426 ('bridge-igmp-version', Link.IFLA_BR_MCAST_IGMP_VERSION, ),
427 ('bridge-mcstats', Link.IFLA_BR_MCAST_STATS_ENABLED),
428 ('bridge-mld-version', Link.IFLA_BR_MCAST_MLD_VERSION)
429 )
430 # 'bridge-vlan-stats & bridge-mcstat are commented out even though, today
431 # they are supported. It is done this way because this dictionary is used
432 # in a loop, but these attributes require additional work. Thus they are
433 # excluded from this loop without overhead.
434
435 # we are still using the old linkCache we need an easy way
436 # to use this cache with the new full-netlink approach
437 _ifla_br_attributes_old_cache_key_map = dict(
438 (
439 (Link.IFLA_BR_FORWARD_DELAY, 'fd'),
440 (Link.IFLA_BR_HELLO_TIME, 'hello'),
441 (Link.IFLA_BR_MAX_AGE, 'maxage'),
442 (Link.IFLA_BR_AGEING_TIME, 'ageing'),
443 (Link.IFLA_BR_STP_STATE, 'stp'),
444 (Link.IFLA_BR_PRIORITY, 'bridgeprio'),
445 (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering'),
446 (Link.IFLA_BR_VLAN_PROTOCOL, 'vlan-protocol'),
447 (Link.IFLA_BR_MCAST_ROUTER, 'mcrouter'),
448 (Link.IFLA_BR_MCAST_SNOOPING, 'mcsnoop'),
449 (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, 'mcqifaddr'),
450 (Link.IFLA_BR_MCAST_QUERIER, 'mcquerier'),
451 (Link.IFLA_BR_MCAST_HASH_ELASTICITY, 'hashel'),
452 (Link.IFLA_BR_MCAST_HASH_MAX, 'hashmax'),
453 (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, 'mclmc'),
454 (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, 'mcsqc'),
455 (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, 'mclmi'),
456 (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, 'mcmi'),
457 (Link.IFLA_BR_MCAST_QUERIER_INTVL, 'mcqpi'),
458 (Link.IFLA_BR_MCAST_QUERY_INTVL, 'mcqi'),
459 (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, 'mcqri'),
460 (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, 'mcsqi'),
461 (Link.IFLA_BR_VLAN_STATS_ENABLED, 'vlan-stats'),
462 (Link.IFLA_BR_MCAST_STATS_ENABLED, 'mcstats'),
463 (Link.IFLA_BR_MCAST_IGMP_VERSION, 'igmp-version'),
464 (Link.IFLA_BR_MCAST_MLD_VERSION, 'mld-version')
465 )
466 )
467
468 _ifla_br_attributes_translate_user_config_to_netlink_map = dict(
469 (
470 # Link.IFLA_BR_UNSPEC,
471 (Link.IFLA_BR_FORWARD_DELAY, lambda x: int(x) * 100),
472 (Link.IFLA_BR_HELLO_TIME, lambda x: int(x) * 100),
473 (Link.IFLA_BR_MAX_AGE, lambda x: int(x) * 100),
474 (Link.IFLA_BR_AGEING_TIME, lambda x: int(x) * 100),
475 # Link.IFLA_BR_STP_STATE, # STP is treated outside the loop
476 (Link.IFLA_BR_PRIORITY, int),
477 (Link.IFLA_BR_VLAN_FILTERING, utils.get_boolean_from_string),
478 (Link.IFLA_BR_VLAN_PROTOCOL, str),
479 # Link.IFLA_BR_GROUP_FWD_MASK,
480 # Link.IFLA_BR_ROOT_ID,
481 # Link.IFLA_BR_BRIDGE_ID,
482 # Link.IFLA_BR_ROOT_PORT,
483 # Link.IFLA_BR_ROOT_PATH_COST,
484 # Link.IFLA_BR_TOPOLOGY_CHANGE,
485 # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
486 # Link.IFLA_BR_HELLO_TIMER,
487 # Link.IFLA_BR_TCN_TIMER,
488 # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
489 # Link.IFLA_BR_GC_TIMER,
490 # Link.IFLA_BR_GROUP_ADDR,
491 # Link.IFLA_BR_FDB_FLUSH,
492 (Link.IFLA_BR_MCAST_ROUTER, utils.get_int_from_boolean_and_string),
493 (Link.IFLA_BR_MCAST_SNOOPING, utils.get_boolean_from_string),
494 (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, utils.get_boolean_from_string),
495 (Link.IFLA_BR_MCAST_QUERIER, utils.get_boolean_from_string),
496 (Link.IFLA_BR_MCAST_HASH_ELASTICITY, int),
497 (Link.IFLA_BR_MCAST_HASH_MAX, int),
498 (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, int),
499 (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, int),
500 (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, lambda x: int(x) * 100),
501 (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, lambda x: int(x) * 100),
502 (Link.IFLA_BR_MCAST_QUERIER_INTVL, lambda x: int(x) * 100),
503 (Link.IFLA_BR_MCAST_QUERY_INTVL, lambda x: int(x) * 100),
504 (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, lambda x: int(x) * 100),
505 (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, lambda x: int(x) * 100),
506 # Link.IFLA_BR_NF_CALL_IPTABLES,
507 # Link.IFLA_BR_NF_CALL_IP6TABLES,
508 # Link.IFLA_BR_NF_CALL_ARPTABLES,
509 # Link.IFLA_BR_VLAN_DEFAULT_PVID,
510 # Link.IFLA_BR_PAD,
511 (Link.IFLA_BR_VLAN_STATS_ENABLED, utils.get_boolean_from_string),
512 (Link.IFLA_BR_MCAST_IGMP_VERSION, int),
513 (Link.IFLA_BR_MCAST_STATS_ENABLED, utils.get_boolean_from_string),
514 (Link.IFLA_BR_MCAST_MLD_VERSION, int)
515 )
516 )
517
518 _ifla_brport_attributes_map = (
519 # Link.IFLA_BRPORT_UNSPEC,
520 # Link.IFLA_BRPORT_STATE,
521 ('bridge-portprios', Link.IFLA_BRPORT_PRIORITY),
522 ('bridge-pathcosts', Link.IFLA_BRPORT_COST),
523 # Link.IFLA_BRPORT_MODE,
524 # Link.IFLA_BRPORT_GUARD,
525 # Link.IFLA_BRPORT_PROTECT,
526 ('bridge-portmcfl', Link.IFLA_BRPORT_FAST_LEAVE),
527 ('bridge-learning', Link.IFLA_BRPORT_LEARNING),
528 ('bridge-unicast-flood', Link.IFLA_BRPORT_UNICAST_FLOOD),
529 # Link.IFLA_BRPORT_PROXYARP,
530 # Link.IFLA_BRPORT_LEARNING_SYNC,
531 # Link.IFLA_BRPORT_PROXYARP_WIFI,
532 # Link.IFLA_BRPORT_ROOT_ID,
533 # Link.IFLA_BRPORT_BRIDGE_ID,
534 # Link.IFLA_BRPORT_DESIGNATED_PORT,
535 # Link.IFLA_BRPORT_DESIGNATED_COST,
536 # Link.IFLA_BRPORT_ID,
537 # Link.IFLA_BRPORT_NO,
538 # Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
539 # Link.IFLA_BRPORT_CONFIG_PENDING,
540 # Link.IFLA_BRPORT_MESSAGE_AGE_TIMER,
541 # Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
542 # Link.IFLA_BRPORT_HOLD_TIMER,
543 # Link.IFLA_BRPORT_FLUSH,
544 ('bridge-portmcrouter', Link.IFLA_BRPORT_MULTICAST_ROUTER),
545 # Link.IFLA_BRPORT_PAD,
546 ('bridge-multicast-flood', Link.IFLA_BRPORT_MCAST_FLOOD),
547 # Link.IFLA_BRPORT_MCAST_TO_UCAST,
548 # Link.IFLA_BRPORT_VLAN_TUNNEL,
549 # Link.IFLA_BRPORT_BCAST_FLOOD
550 ('bridge-l2protocol-tunnel', Link.IFLA_BRPORT_GROUP_FWD_MASK),
551 # Link.IFLA_BRPORT_PEER_LINK,
552 # Link.IFLA_BRPORT_DUAL_LINK,
553 ('bridge-arp-nd-suppress', Link.IFLA_BRPORT_ARP_SUPPRESS),
554 )
555
556 _ifla_brport_multicast_router_dict_to_int = {
557 'disabled': 0,
558 '0': 0,
559 'no': 0,
560 'automatic': 1,
561 '1': 1,
562 'yes': 1,
563 'enabled': 2,
564 '2': 2,
565 }
566
567 # callable to translate <interface-yes-no-0-1-list> to netlink value
568 _ifla_brport_attributes_translate_user_config_to_netlink_map = dict(
569 (
570 (Link.IFLA_BRPORT_PRIORITY, int),
571 (Link.IFLA_BRPORT_COST, int),
572 (Link.IFLA_BRPORT_MULTICAST_ROUTER, lambda x: bridge._ifla_brport_multicast_router_dict_to_int.get(x, 0)),
573 (Link.IFLA_BRPORT_FAST_LEAVE, utils.get_boolean_from_string),
574 (Link.IFLA_BRPORT_LEARNING, utils.get_boolean_from_string),
575 (Link.IFLA_BRPORT_UNICAST_FLOOD, utils.get_boolean_from_string),
576 (Link.IFLA_BRPORT_MCAST_FLOOD, utils.get_boolean_from_string),
577 (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x),
578 (Link.IFLA_BRPORT_ARP_SUPPRESS, utils.get_boolean_from_string)
579 )
580 )
581
582 def __init__(self, *args, **kargs):
583 moduleBase.__init__(self, *args, **kargs)
584 self.ipcmd = None
585 self.name = self.__class__.__name__
586 self.brctlcmd = None
587 self._running_vidinfo = {}
588 self._running_vidinfo_valid = False
589 self._resv_vlan_range = self._get_reserved_vlan_range()
590 self.logger.debug('%s: using reserved vlan range %s' % (self.__class__.__name__, str(self._resv_vlan_range)))
591
592 self.default_stp_on = utils.get_boolean_from_string(
593 policymanager.policymanager_api.get_attr_default(
594 module_name=self.__class__.__name__,
595 attr='bridge-stp'
596 )
597 )
598
599 self.default_vlan_stats = policymanager.policymanager_api.get_attr_default(
600 module_name=self.__class__.__name__,
601 attr='bridge-vlan-stats'
602 )
603
604 self.warn_on_untagged_bridge_absence = utils.get_boolean_from_string(
605 policymanager.policymanager_api.get_module_globals(
606 module_name=self.__class__.__name__,
607 attr='warn_on_untagged_bridge_absence'
608 )
609 )
610 self.logger.debug('bridge: init: warn_on_untagged_bridge_absence=%s'
611 % self.warn_on_untagged_bridge_absence)
612
613 self._vxlan_bridge_default_igmp_snooping = policymanager.policymanager_api.get_module_globals(
614 self.__class__.__name__,
615 'vxlan_bridge_default_igmp_snooping'
616 )
617 self.logger.debug('bridge: init: vxlan_bridge_default_igmp_snooping=%s'
618 % self._vxlan_bridge_default_igmp_snooping)
619
620 self.arp_nd_suppress_only_on_vxlan = utils.get_boolean_from_string(
621 policymanager.policymanager_api.get_module_globals(
622 module_name=self.__class__.__name__,
623 attr='allow_arp_nd_suppress_only_on_vxlan'
624 )
625 )
626 self.logger.debug('bridge: init: arp_nd_suppress_only_on_vxlan=%s' % self.arp_nd_suppress_only_on_vxlan)
627
628 try:
629 self.bridge_allow_multiple_vlans = utils.get_boolean_from_string(
630 self.sysctl_get('net.bridge.bridge-allow-multiple-vlans')
631 )
632 except:
633 # Cumulus Linux specific variable. Failure probably means that
634 # ifupdown2 is running a a different system.
635 self.bridge_allow_multiple_vlans = True
636 self.logger.debug('bridge: init: multiple vlans allowed %s' % self.bridge_allow_multiple_vlans)
637
638 self.bridge_mac_iface_list = policymanager.policymanager_api.get_module_globals(self.__class__.__name__, 'bridge_mac_iface') or []
639 self.bridge_mac_iface = None, None # ifname, mac
640
641 self.bridge_set_static_mac_from_port = utils.get_boolean_from_string(
642 policymanager.policymanager_api.get_module_globals(
643 self.__class__.__name__, 'bridge_set_static_mac_from_port'
644 )
645 )
646
647 self.l2protocol_tunnel_callback = {
648 'all': self._l2protocol_tunnel_set_all,
649 'stp': self._l2protocol_tunnel_set_stp,
650 'cdp': self._l2protocol_tunnel_set_cdp,
651 'pvst': self._l2protocol_tunnel_set_pvst,
652 'lldp': self._l2protocol_tunnel_set_lldp,
653 'lacp': self._l2protocol_tunnel_set_lacp
654 }
655
656 self.query_check_l2protocol_tunnel_callback = {
657 'all': self._query_check_l2protocol_tunnel_all,
658 'stp': self._query_check_l2protocol_tunnel_stp,
659 'cdp': self._query_check_l2protocol_tunnel_cdp,
660 'pvst': self._query_check_l2protocol_tunnel_pvst,
661 'lldp': self._query_check_l2protocol_tunnel_lldp,
662 'lacp': self._query_check_l2protocol_tunnel_lacp
663 }
664
665 @staticmethod
666 def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
667 ifla_brport_group_maskhi |= 0x1
668 return ifla_brport_group_mask, ifla_brport_group_maskhi
669
670 @staticmethod
671 def _l2protocol_tunnel_set_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
672 ifla_brport_group_maskhi |= 0x2
673 return ifla_brport_group_mask, ifla_brport_group_maskhi
674
675 @staticmethod
676 def _l2protocol_tunnel_set_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
677 ifla_brport_group_mask |= 0x1
678 return ifla_brport_group_mask, ifla_brport_group_maskhi
679
680 @staticmethod
681 def _l2protocol_tunnel_set_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
682 ifla_brport_group_mask |= 0x4
683 return ifla_brport_group_mask, ifla_brport_group_maskhi
684
685 @staticmethod
686 def _l2protocol_tunnel_set_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
687 ifla_brport_group_mask |= 0x4000
688 return ifla_brport_group_mask, ifla_brport_group_maskhi
689
690 @staticmethod
691 def _l2protocol_tunnel_set_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
692 # returns new values for ifla_brport_group_mask and ifla_brport_group_maskhi
693 return 0x1 | 0x4 | 0x4000, 0x1 | 0x2
694
695 @staticmethod
696 def _query_check_l2protocol_tunnel_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
697 return ifla_brport_group_mask & 0x1
698
699 @staticmethod
700 def _query_check_l2protocol_tunnel_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
701 return ifla_brport_group_maskhi & 0x2
702
703 @staticmethod
704 def _query_check_l2protocol_tunnel_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
705 return ifla_brport_group_maskhi & 0x1
706
707 @staticmethod
708 def _query_check_l2protocol_tunnel_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
709 return ifla_brport_group_mask & 0x4000
710
711 @staticmethod
712 def _query_check_l2protocol_tunnel_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
713 return ifla_brport_group_mask & 0x4
714
715 @staticmethod
716 def _query_check_l2protocol_tunnel_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
717 return ifla_brport_group_mask == (0x1 | 0x4 | 0x4000) and ifla_brport_group_maskhi == (0x1 | 0x2)
718
719 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
720 retval = self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc)
721 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
722 if not self.check_bridge_port_vid_attrs(ifaceobj):
723 retval = False
724 c1 = self.syntax_check_vxlan_in_vlan_aware_br(ifaceobj, ifaceobj_getfunc)
725 c2 = self.syntax_check_bridge_allow_multiple_vlans(ifaceobj, ifaceobj_getfunc)
726 return retval and c1 #and c2
727
728 def syntax_check_bridge_allow_multiple_vlans(self, ifaceobj, ifaceobj_getfunc):
729 result = True
730 if not self.bridge_allow_multiple_vlans and ifaceobj.link_kind & ifaceLinkKind.BRIDGE and ifaceobj.lowerifaces:
731 vlan_id = None
732 for brport_name in ifaceobj.lowerifaces:
733 for obj in ifaceobj_getfunc(brport_name) or []:
734 if obj.link_kind & ifaceLinkKind.VLAN:
735 sub_intf_vlan_id = self._get_vlan_id(obj)
736 if vlan_id and vlan_id != sub_intf_vlan_id:
737 self.logger.error('%s: ignore %s: multiple vlans not allowed under bridge '
738 '(sysctl net.bridge.bridge-allow-multiple-vlans not set)'
739 % (ifaceobj.name, brport_name))
740 result = False
741 continue
742 vlan_id = sub_intf_vlan_id
743 return result
744
745 def check_bridge_port_vid_attrs(self, ifaceobj):
746 if (ifaceobj.get_attr_value('bridge-access') and
747 (self.get_ifaceobj_bridge_vids_value(ifaceobj) or
748 ifaceobj.get_attr_value('bridge-pvid'))):
749 self.logger.warn('%s: bridge-access given, bridge-vids and bridge-pvid '
750 'will be ignored' % ifaceobj.name)
751 return False
752 return True
753
754 def check_bridge_vlan_aware_port(self, ifaceobj, ifaceobj_getfunc):
755 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
756 ports = self._get_bridge_port_list(ifaceobj)
757 if not ports:
758 return True
759 result = True
760 for port_name in ports:
761 port_obj_l = ifaceobj_getfunc(port_name)
762 if port_obj_l and port_obj_l[0].link_kind & ifaceLinkKind.VLAN:
763 self.logger.error('%s: %s: vlan sub-interface is not '
764 'supported in a vlan-aware bridge'
765 % (ifaceobj.name, port_name))
766 result = False
767 if (port_obj_l and
768 port_obj_l[0].get_attr_value('bridge-arp-nd-suppress') and
769 self.arp_nd_suppress_only_on_vxlan and
770 not port_obj_l[0].link_kind & ifaceLinkKind.VXLAN):
771 self.log_error('\'bridge-arp-nd-suppress\' is not '
772 'supported on a non-vxlan port %s'
773 %port_obj_l[0].name)
774 result = False
775 return result
776 return True
777
778 def _error_vxlan_in_vlan_aware_br(self, ifaceobj, bridgename):
779 self.log_error('`bridge-access` attribute is mandatory when vxlan '
780 'device (%s) is part of vlan aware bridge (%s)'
781 % (ifaceobj.name, bridgename), ifaceobj)
782
783 def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
784 if not ifaceobj_getfunc:
785 return True
786 if (ifaceobj.link_kind & ifaceLinkKind.VXLAN
787 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
788 if ifaceobj.get_attr_value('bridge-access'):
789 return True
790 for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
791 ifaceobj_upper_list = ifaceobj_getfunc(iface)
792 if not ifaceobj_upper_list:
793 continue
794 ifaceobj_upper = ifaceobj_upper_list[0]
795 bridge_vids = self._get_bridge_vids(iface, ifaceobj_getfunc)
796 if ifaceobj_upper.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
797 vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
798 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
799 if (not vids
800 or not pvid
801 or not self._compare_vids(bridge_vids,
802 vids,
803 pvid=pvid)):
804 self._error_vxlan_in_vlan_aware_br(ifaceobj,
805 ifaceobj_upper.name)
806 return False
807 return True
808
809 @staticmethod
810 def _is_bridge(ifaceobj):
811 return (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
812 ifaceobj.get_attr_value_first('bridge-ports') or
813 ifaceobj.get_attr_value_first('bridge-vlan-aware'))
814
815 def _get_ifaceobj_bridge_ports(self, ifaceobj):
816 bridge_ports = []
817
818 for brport in ifaceobj.get_attr_value('bridge-ports') or []:
819 if brport != 'none':
820 bridge_ports.extend(brport.split())
821
822 return ' '.join(bridge_ports)
823
824 def _is_bridge_port(self, ifaceobj):
825 if self.brctlcmd.is_bridge_port(ifaceobj.name):
826 return True
827 return False
828
829 def check_valid_bridge(self, ifaceobj, ifname):
830 if LinkUtils.link_exists(ifname) and not LinkUtils.is_bridge(ifname):
831 self.log_error('misconfiguration of bridge attribute(s) on existing non-bridge interface (%s)' % ifname, ifaceobj=ifaceobj)
832 return False
833 return True
834
835 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
836 if not self._is_bridge(ifaceobj) or not self.check_valid_bridge(ifaceobj, ifaceobj.name):
837 return None
838 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
839 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
840 ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
841 # for special vlan aware bridges, we need to add another bit
842 if utils.get_boolean_from_string(ifaceobj.get_attr_value_first('bridge-vlan-aware')):
843 ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
844 ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
845 ifaceobj.role |= ifaceRole.MASTER
846 ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
847 return self.parse_port_list(ifaceobj.name,
848 self._get_ifaceobj_bridge_ports(ifaceobj),
849 ifacenames_all)
850
851 def get_dependent_ifacenames_running(self, ifaceobj):
852 self._init_command_handlers()
853 if not self.brctlcmd.bridge_exists(ifaceobj.name):
854 return None
855 return self.brctlcmd.get_bridge_ports(ifaceobj.name)
856
857 def _get_bridge_port_list(self, ifaceobj):
858
859 # port list is also available in the previously
860 # parsed dependent list. Use that if available, instead
861 # of parsing port expr again
862 port_list = ifaceobj.lowerifaces
863 if port_list:
864 return port_list
865 ports = self._get_ifaceobj_bridge_ports(ifaceobj)
866 if ports:
867 return self.parse_port_list(ifaceobj.name, ports)
868 else:
869 return None
870
871 def _get_bridge_port_list_user_ordered(self, ifaceobj):
872 # When enslaving bridge-ports we need to return the exact user
873 # configured bridge ports list (bridge will inherit the mac of the
874 # first device.
875 ports = self._get_ifaceobj_bridge_ports(ifaceobj)
876 return self.parse_port_list(ifaceobj.name, ports) if ports else None
877
878 def _process_bridge_waitport(self, ifaceobj, portlist):
879 waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
880 if not waitport_value: return
881 try:
882 waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
883 if not waitportvals: return
884 try:
885 waitporttime = int(waitportvals[0])
886 except:
887 self.log_warn('%s: invalid waitport value \'%s\''
888 %(ifaceobj.name, waitportvals[0]))
889 return
890 if waitporttime <= 0: return
891 try:
892 waitportlist = self.parse_port_list(ifaceobj.name,
893 waitportvals[1])
894 except IndexError, e:
895 # ignore error and use all bridge ports
896 waitportlist = portlist
897 pass
898 if not waitportlist: return
899 self.logger.info('%s: waiting for ports %s to exist ...'
900 %(ifaceobj.name, str(waitportlist)))
901 starttime = time.time()
902 while ((time.time() - starttime) < waitporttime):
903 if all([False for p in waitportlist
904 if not self.ipcmd.link_exists(p)]):
905 break;
906 time.sleep(1)
907 except Exception, e:
908 self.log_warn('%s: unable to process waitport: %s'
909 %(ifaceobj.name, str(e)))
910
911 def _enable_disable_ipv6(self, port, enable='1'):
912 try:
913 self.write_file('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % port, enable)
914 except Exception, e:
915 self.logger.info(str(e))
916
917 def handle_ipv6(self, ports, state, ifaceobj=None):
918 if (ifaceobj and
919 (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN) and
920 not ifaceobj.get_attr_value('address')):
921 self._enable_disable_ipv6(ifaceobj.name, state)
922 for p in ports:
923 self._enable_disable_ipv6(p, state)
924
925 def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports):
926 """ pretty print bridge port add errors.
927 since the commands are batched and the kernel only returns error
928 codes, this function tries to interpret some error codes
929 and prints clearer errors """
930
931 if re.search('RTNETLINK answers: Invalid argument', errstr):
932 # Cumulus Linux specific error checks
933 try:
934 if self.sysctl_get('net.bridge.bridge-allow-multiple-vlans') == '0':
935 vlanid = None
936 for bport in bridgeports:
937 currvlanid = self._get_vlan_id_from_ifacename(bport)
938 if vlanid:
939 if currvlanid != vlanid:
940 self.log_error('%s: ' %bridgeifaceobj.name +
941 'net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed', bridgeifaceobj)
942 break
943 if currvlanid:
944 vlanid = currvlanid
945 except Exception as e:
946 errstr += '\n%s' % str(e)
947 self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj)
948
949 def _add_ports(self, ifaceobj, ifaceobj_getfunc):
950 bridgeports = self._get_bridge_port_list(ifaceobj)
951 runningbridgeports = []
952
953 self.ipcmd.batch_start()
954 self._process_bridge_waitport(ifaceobj, bridgeports)
955 self.ipcmd.batch_start()
956 # Delete active ports not in the new port list
957 if not ifupdownflags.flags.PERFMODE:
958 runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
959 if runningbridgeports:
960 for bport in runningbridgeports:
961 if not bridgeports or bport not in bridgeports:
962 self.ipcmd.link_set(bport, 'nomaster')
963 # set admin DOWN on all removed ports
964 # that don't have config outside bridge
965 if not ifaceobj_getfunc(bport):
966 netlink.link_set_updown(bport, "down")
967 # enable ipv6 for ports that were removed
968 self.handle_ipv6([bport], '0')
969 else:
970 runningbridgeports = []
971 if not bridgeports:
972 self.ipcmd.batch_commit()
973 return []
974 err = 0
975 ports = 0
976 newbridgeports = Set(bridgeports).difference(Set(runningbridgeports))
977 newly_enslaved_ports = []
978
979 newbridgeports_ordered = []
980 for br_port in self._get_bridge_port_list_user_ordered(ifaceobj):
981 if br_port in newbridgeports:
982 newbridgeports_ordered.append(br_port)
983
984 for bridgeport in newbridgeports_ordered:
985 try:
986 if (not ifupdownflags.flags.DRYRUN and
987 not self.ipcmd.link_exists(bridgeport)):
988 self.log_error('%s: bridge port %s does not exist'
989 %(ifaceobj.name, bridgeport), ifaceobj)
990 err += 1
991 continue
992 hwaddress = self.ipcmd.link_get_hwaddress(bridgeport)
993 if not self._valid_ethaddr(hwaddress):
994 self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
995 bridgeport) + 'invalid ether addr %s'
996 %hwaddress)
997 continue
998 self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
999 newly_enslaved_ports.append(bridgeport)
1000 self.handle_ipv6([bridgeport], '1')
1001 self.ipcmd.addr_flush(bridgeport)
1002 ports += 1
1003 if ports == 250:
1004 ports = 0
1005 self.ipcmd.batch_commit()
1006 self.ipcmd.batch_start()
1007 except Exception, e:
1008 self.logger.error(str(e))
1009 pass
1010 try:
1011 self.ipcmd.batch_commit()
1012 except Exception, e:
1013 self._pretty_print_add_ports_error(str(e), ifaceobj,
1014 bridgeports)
1015 pass
1016
1017 if err:
1018 self.log_error('bridge configuration failed (missing ports)')
1019
1020 return newly_enslaved_ports
1021
1022 def _process_bridge_maxwait(self, ifaceobj, portlist):
1023 maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
1024 if not maxwait: return
1025 try:
1026 maxwait = int(maxwait)
1027 except:
1028 self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
1029 maxwait))
1030 return
1031 if not maxwait: return
1032 self.logger.info('%s: waiting for ports to go to fowarding state ..'
1033 %ifaceobj.name)
1034 try:
1035 starttime = time.time()
1036 while ((time.time() - starttime) < maxwait):
1037 if all([False for p in portlist
1038 if self.read_file_oneline(
1039 '/sys/class/net/%s/brif/%s/state'
1040 %(ifaceobj.name, p)) != '3']):
1041 break;
1042 time.sleep(1)
1043 except Exception, e:
1044 self.log_warn('%s: unable to process maxwait: %s'
1045 %(ifaceobj.name, str(e)))
1046
1047 def _ints_to_ranges(self, ints):
1048 for a, b in itertools.groupby(enumerate(ints), lambda (x, y): y - x):
1049 b = list(b)
1050 yield b[0][1], b[-1][1]
1051
1052 def _ranges_to_ints(self, rangelist):
1053 """ returns expanded list of integers given set of string ranges
1054 example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
1055 """
1056 result = []
1057 try:
1058 for part in rangelist:
1059 if '-' in part:
1060 a, b = part.split('-')
1061 a, b = int(a), int(b)
1062 result.extend(range(a, b + 1))
1063 else:
1064 a = int(part)
1065 result.append(a)
1066 except:
1067 self.logger.warn('unable to parse vids \'%s\''
1068 %''.join(rangelist))
1069 pass
1070 return result
1071
1072 def _compress_into_ranges(self, vids_ints):
1073 return ['%d' %start if start == end else '%d-%d' %(start, end)
1074 for start, end in self._ints_to_ranges(vids_ints)]
1075
1076 def _diff_vids(self, vids1_ints, vids2_ints):
1077 return Set(vids2_ints).difference(vids1_ints), Set(vids1_ints).difference(vids2_ints)
1078
1079 def _compare_vids(self, vids1, vids2, pvid=None):
1080 """ Returns true if the vids are same else return false """
1081
1082 vids1_ints = self._ranges_to_ints(vids1)
1083 vids2_ints = self._ranges_to_ints(vids2)
1084 set_diff = Set(vids1_ints).symmetric_difference(vids2_ints)
1085 if pvid and int(pvid) in set_diff:
1086 set_diff.remove(int(pvid))
1087 if set_diff:
1088 return False
1089 else:
1090 return True
1091
1092 def _set_bridge_mcqv4src_compat(self, ifaceobj):
1093 #
1094 # Sets old style igmp querier
1095 #
1096 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
1097 if attrval:
1098 running_mcqv4src = {}
1099 if not ifupdownflags.flags.PERFMODE:
1100 running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(ifaceobj.name)
1101 mcqs = {}
1102 srclist = attrval.split()
1103 for s in srclist:
1104 k, v = s.split('=')
1105 mcqs[k] = v
1106
1107 k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
1108 for v in k_to_del:
1109 self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
1110 for v in mcqs.keys():
1111 self.brctlcmd.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
1112 elif not ifupdownflags.flags.PERFMODE:
1113 running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(ifaceobj.name)
1114 if running_mcqv4src:
1115 for v in running_mcqv4src.keys():
1116 self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
1117
1118 def _get_running_vidinfo(self):
1119 if self._running_vidinfo_valid:
1120 return self._running_vidinfo
1121 self._running_vidinfo = {}
1122
1123 # Removed check for PERFMODE. Need the get in all cases
1124 # including reboot, so that we can configure the pvid correctly.
1125 self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all_json()
1126 self._running_vidinfo_valid = True
1127 return self._running_vidinfo
1128
1129 def _set_bridge_vidinfo_compat(self, ifaceobj):
1130 #
1131 # Supports old style vlan vid info format
1132 # for compatibility
1133 #
1134 bridge_port_pvids = ifaceobj.get_attr_value_first('bridge-port-pvids')
1135 bridge_port_vids = ifaceobj.get_attr_value_first('bridge-port-vids')
1136 if not bridge_port_pvids and not bridge_port_vids:
1137 return
1138
1139 # Handle bridge vlan attrs
1140 # Install pvids
1141 if bridge_port_pvids:
1142 portlist = self.parse_port_list(ifaceobj.name, bridge_port_pvids)
1143 if not portlist:
1144 self.log_warn('%s: could not parse \'%s %s\''
1145 %(ifaceobj.name, 'bridge-port-pvids',
1146 bridge_port_pvids))
1147 return
1148 for p in portlist:
1149 try:
1150 (port, pvid) = p.split('=')
1151 pvid = int(pvid)
1152 running_pvid = self._get_running_pvid(port)
1153 if running_pvid:
1154 if running_pvid == pvid:
1155 continue
1156 else:
1157 self.ipcmd.bridge_port_pvid_del(port, running_pvid)
1158 self.ipcmd.bridge_port_pvid_add(port, pvid)
1159 except Exception, e:
1160 self.log_warn('%s: failed to set pvid `%s` (%s)'
1161 %(ifaceobj.name, p, str(e)))
1162
1163 # install port vids
1164 if bridge_port_vids:
1165 portlist = self.parse_port_list(ifaceobj.name, bridge_port_vids)
1166 if not portlist:
1167 self.log_warn('%s: could not parse \'%s %s\'' %(ifaceobj.name,
1168 'bridge-port-vids', bridge_port_vids))
1169 return
1170 for p in portlist:
1171 try:
1172 (port, val) = p.split('=')
1173 vids = val.split(',')
1174 vids_int = self._ranges_to_ints(vids)
1175 running_vids = self.ipcmd.bridge_vlan_get_vids(port)
1176 if running_vids:
1177 (vids_to_del, vids_to_add) = \
1178 self._diff_vids(vids_int, running_vids)
1179 if vids_to_del:
1180 self.ipcmd.bridge_port_vids_del(port,
1181 self._compress_into_ranges(vids_to_del))
1182 if vids_to_add:
1183 self.ipcmd.bridge_port_vids_add(port,
1184 self._compress_into_ranges(vids_to_add))
1185 else:
1186 self.ipcmd.bridge_port_vids_add(port, vids_int)
1187 except Exception, e:
1188 self.log_warn('%s: failed to set vid `%s` (%s)'
1189 %(ifaceobj.name, p, str(e)))
1190
1191 def _is_running_stp_state_on(self, bridgename):
1192 """ Returns True if running stp state is on, else False """
1193
1194 stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
1195 try:
1196 running_stp_state = self.read_file_oneline(stp_state_file)
1197 return running_stp_state and running_stp_state != '0'
1198 except:
1199 return False
1200
1201 def _is_config_stp_state_on(self, ifaceobj):
1202 """ Returns true if user specified stp state is on, else False """
1203
1204 stp_attr = ifaceobj.get_attr_value_first('bridge-stp')
1205 if not stp_attr:
1206 return self.default_stp_on
1207 return utils.get_boolean_from_string(stp_attr)
1208
1209 def get_bridge_mcsnoop_value(self, ifaceobj):
1210 mcsnoop = ifaceobj.get_attr_value_first('bridge-mcsnoop')
1211 if not mcsnoop and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
1212 return self._vxlan_bridge_default_igmp_snooping
1213 return mcsnoop
1214
1215 def fill_ifla_info_data_with_ifla_br_attribute(self,
1216 ifla_info_data,
1217 link_just_created,
1218 ifname,
1219 nl_attr,
1220 attr_name,
1221 user_config):
1222 try:
1223 translate_func = self._ifla_br_attributes_translate_user_config_to_netlink_map.get(nl_attr)
1224
1225 if not callable(translate_func):
1226 return
1227
1228 if not user_config:
1229 user_config = policymanager.policymanager_api.get_iface_default(
1230 module_name=self.__class__.__name__,
1231 ifname=ifname,
1232 attr=attr_name
1233 )
1234
1235 old_cache_key = self._ifla_br_attributes_old_cache_key_map.get(nl_attr)
1236 if old_cache_key and not link_just_created:
1237 cached_value = self.brctlcmd.link_cache_get([ifname, 'linkinfo', old_cache_key])
1238 if not cached_value:
1239 # the link already exists but we don't have any value
1240 # cached for this attr, it probably means that the
1241 # capability is not available on this system (i.e old kernel)
1242 self.logger.debug('%s: ignoring %s %s: capability '
1243 'probably not supported on this system'
1244 % (ifname, attr_name, user_config))
1245 return
1246 # we need to convert the cache value to "netlink" format
1247 cached_value = translate_func(cached_value.lower())
1248 else:
1249 cached_value = None
1250
1251 if not user_config and not link_just_created and cached_value is not None:
1252 # there is no user configuration for this attribute
1253 # if the bridge existed before we need to check if
1254 # this attribute needs to be reset to default value
1255 default_value = self.get_attr_default_value(attr_name)
1256
1257 if default_value:
1258 # the attribute has a default value, we need to convert it to
1259 # netlink format to compare it with the cache value
1260 default_value_nl = translate_func(default_value) # default_value.lower()
1261
1262 if default_value_nl != cached_value:
1263 # the running value difers from the default value
1264 # but the user didn't specify any config
1265 # resetting attribute to default
1266 ifla_info_data[nl_attr] = default_value_nl
1267 self.logger.info('%s: reset %s to default: %s' % (ifname, attr_name, default_value))
1268 elif user_config:
1269 user_config_nl = translate_func(user_config) # user_config.lower()
1270
1271 if user_config_nl != cached_value:
1272 ifla_info_data[nl_attr] = user_config_nl
1273
1274 if cached_value is not None:
1275 self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
1276 else:
1277 self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
1278 except Exception as e:
1279 self.logger.warning('%s: %s: %s' % (ifname, attr_name, str(e)))
1280
1281 def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_aware):
1282 ifla_info_data = dict()
1283 ifname = ifaceobj.name
1284
1285 self.logger.info('%s: apply bridge settings' % ifname)
1286
1287 for attr_name, nl_attr in self._ifla_br_attributes_map:
1288 self.fill_ifla_info_data_with_ifla_br_attribute(
1289 ifla_info_data=ifla_info_data,
1290 link_just_created=link_just_created,
1291 ifname=ifname,
1292 nl_attr=nl_attr,
1293 attr_name=attr_name,
1294 user_config=ifaceobj.get_attr_value_first(attr_name)
1295 )
1296
1297 # bridge-mcsnoop
1298 self.fill_ifla_info_data_with_ifla_br_attribute(
1299 ifla_info_data=ifla_info_data,
1300 link_just_created=link_just_created,
1301 ifname=ifname,
1302 nl_attr=Link.IFLA_BR_MCAST_SNOOPING,
1303 attr_name='bridge-mcsnoop',
1304 user_config=self.get_bridge_mcsnoop_value(ifaceobj)
1305 )
1306
1307 # bridge-vlan-stats
1308 if bridge_vlan_aware:
1309 self.fill_ifla_info_data_with_ifla_br_attribute(
1310 ifla_info_data=ifla_info_data,
1311 link_just_created=link_just_created,
1312 ifname=ifname,
1313 nl_attr=Link.IFLA_BR_VLAN_STATS_ENABLED,
1314 attr_name='bridge-vlan-stats',
1315 user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats
1316 )
1317
1318 try:
1319 if self._is_config_stp_state_on(ifaceobj):
1320 if not self._is_running_stp_state_on(ifname):
1321 ifla_info_data[Link.IFLA_BR_STP_STATE] = 1
1322 self.logger.info('%s: stp state reset, reapplying port settings' % ifname)
1323 ifaceobj.module_flags[ifaceobj.name] = \
1324 ifaceobj.module_flags.setdefault(self.name, 0) | \
1325 bridgeFlags.PORT_PROCESSED_OVERRIDE
1326 else:
1327 # If stp not specified and running stp state on, set it to off
1328 if self._is_running_stp_state_on(ifname):
1329 self.logger.info('%s: bridge-stp not specified but running: turning stp off')
1330 ifla_info_data[Link.IFLA_BR_STP_STATE] = 0
1331 except Exception as e:
1332 self.logger.warning('%s: bridge stp: %s' % (ifname, str(e)))
1333
1334 if ifla_info_data:
1335 netlink.link_add_set(ifname=ifname, kind='bridge', ifla_info_data=ifla_info_data, link_exists=True)
1336
1337 def _check_vids(self, ifaceobj, vids):
1338 ret = True
1339 for v in vids:
1340 try:
1341 if '-' in v:
1342 va, vb = v.split('-')
1343 va, vb = int(va), int(vb)
1344 self._handle_reserved_vlan(va, ifaceobj.name, end=vb)
1345 else:
1346 va = int(v)
1347 self._handle_reserved_vlan(va, ifaceobj.name)
1348 except exceptions.ReservedVlanException as e:
1349 raise e
1350 except Exception:
1351 self.logger.warn('%s: unable to parse vid \'%s\''
1352 %(ifaceobj.name, v))
1353 return ret
1354
1355 def _get_running_pvid(self, ifacename):
1356 pvid = 0
1357
1358 running_vidinfo = self._get_running_vidinfo()
1359 for vinfo in running_vidinfo.get(ifacename, {}):
1360 v = vinfo.get('vlan')
1361 pvid = v if 'PVID' in vinfo.get('flags', []) else 0
1362 if pvid:
1363 return pvid
1364 return pvid
1365
1366 def _get_running_vids_n_pvid_str(self, ifacename):
1367 vids = []
1368 pvid = None
1369
1370 (vids, pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(ifacename)
1371
1372 if vids:
1373 ret_vids = self._compress_into_ranges(vids)
1374 else:
1375 ret_vids = None
1376
1377 if pvid:
1378 ret_pvid = '%s' %pvid
1379 else:
1380 ret_pvid = None
1381 return (ret_vids, ret_pvid)
1382
1383 def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid,
1384 isbridge):
1385 """ This method is a combination of methods _apply_bridge_vids and
1386 _apply_bridge_port_pvids above. A combined function is
1387 found necessary to do the deletes first and the adds later
1388 because kernel does honor vid info flags during deletes.
1389
1390 """
1391 if not isbridge and bportifaceobj.link_kind & ifaceLinkKind.VXLAN:
1392 if not vids or not pvid or len(vids) > 1 or vids[0] != pvid:
1393 self._error_vxlan_in_vlan_aware_br(bportifaceobj,
1394 bportifaceobj.upperifaces[0])
1395 return
1396
1397 vids_int = self._ranges_to_ints(vids)
1398 try:
1399 pvid_int = int(pvid) if pvid else 0
1400 except Exception:
1401 self.logger.warn('%s: unable to parse pvid \'%s\''
1402 %(bportifaceobj.name, pvid))
1403 pvid_int = 0
1404 pass
1405
1406 vids_to_del = []
1407 vids_to_add = vids_int
1408 pvid_to_del = None
1409 pvid_to_add = pvid_int
1410
1411 try:
1412 if not self._check_vids(bportifaceobj, vids):
1413 return
1414
1415 (running_vids, running_pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(
1416 bportifaceobj.name)
1417
1418 if not running_vids and not running_pvid:
1419 # There cannot be a no running pvid.
1420 # It might just not be in our cache:
1421 # this can happen if at the time we were
1422 # creating the bridge vlan cache, the port
1423 # was not part of the bridge. And we need
1424 # to make sure both vids and pvid is not in
1425 # the cache, to declare that our cache may
1426 # be stale.
1427 running_pvid = 1
1428 running_vids = [1]
1429
1430 if running_vids:
1431 (vids_to_del, vids_to_add) = \
1432 self._diff_vids(vids_to_add, running_vids)
1433
1434 if running_pvid:
1435 if running_pvid != pvid_int and running_pvid != 0:
1436 pvid_to_del = running_pvid
1437
1438 if (pvid_to_del and (pvid_to_del in vids_int) and
1439 (pvid_to_del not in vids_to_add)):
1440 # kernel deletes dont take into account
1441 # bridge vid flags and its possible that
1442 # the pvid deletes we do end up deleting
1443 # the vids. Be proactive and add the pvid
1444 # to the vid add list if it is in the vids
1445 # and not already part of vids_to_add.
1446 # This helps with a small corner case:
1447 # - running
1448 # pvid 100
1449 # vid 101 102
1450 # - new change is going to move the state to
1451 # pvid 101
1452 # vid 100 102
1453 vids_to_add.add(pvid_to_del)
1454 except exceptions.ReservedVlanException as e:
1455 raise e
1456 except Exception, e:
1457 self.log_error('%s: failed to process vids/pvids'
1458 %bportifaceobj.name + ' vids = %s' %str(vids) +
1459 'pvid = %s ' %pvid + '(%s)' %str(e),
1460 bportifaceobj, raise_error=False)
1461 try:
1462 if vids_to_del:
1463 if pvid_to_add in vids_to_del:
1464 vids_to_del.remove(pvid_to_add)
1465 self.ipcmd.bridge_vids_del(bportifaceobj.name,
1466 self._compress_into_ranges(
1467 vids_to_del), isbridge)
1468 except Exception, e:
1469 self.log_warn('%s: failed to del vid `%s` (%s)'
1470 %(bportifaceobj.name, str(vids_to_del), str(e)))
1471
1472 try:
1473 if pvid_to_del:
1474 self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
1475 pvid_to_del)
1476 except Exception, e:
1477 self.log_warn('%s: failed to del pvid `%s` (%s)'
1478 %(bportifaceobj.name, pvid_to_del, str(e)))
1479
1480 try:
1481 if vids_to_add:
1482 self.ipcmd.bridge_vids_add(bportifaceobj.name,
1483 self._compress_into_ranges(
1484 vids_to_add), isbridge)
1485 except Exception, e:
1486 self.log_error('%s: failed to set vid `%s` (%s)'
1487 %(bportifaceobj.name, str(vids_to_add),
1488 str(e)), bportifaceobj, raise_error=False)
1489
1490 try:
1491 if pvid_to_add and pvid_to_add != running_pvid:
1492 self.ipcmd.bridge_port_pvid_add(bportifaceobj.name,
1493 pvid_to_add)
1494 except Exception, e:
1495 self.log_error('%s: failed to set pvid `%s` (%s)'
1496 %(bportifaceobj.name, pvid_to_add, str(e)),
1497 bportifaceobj)
1498
1499 def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
1500 bridge_vids=None,
1501 bridge_pvid=None):
1502 vids = None
1503 pvids = None
1504 vids_final = []
1505 pvid_final = None
1506 bport_access = bportifaceobj.get_attr_value_first('bridge-access')
1507 if bport_access:
1508 vids = re.split(r'[\s\t]\s*', bport_access)
1509 pvids = vids
1510 allow_untagged = 'yes'
1511 self.check_bridge_port_vid_attrs(bportifaceobj)
1512 else:
1513 allow_untagged = bportifaceobj.get_attr_value_first('bridge-allow-untagged') or 'yes'
1514
1515 bport_vids = self.get_ifaceobj_bridge_vids_value(bportifaceobj)
1516 if bport_vids:
1517 vids = re.split(r'[\s\t,]\s*', bport_vids)
1518
1519 bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
1520 if bport_pvids:
1521 pvids = re.split(r'[\s\t]\s*', bport_pvids)
1522
1523 if vids:
1524 vids_final = vids
1525 elif bridge_vids:
1526 vids_final = bridge_vids
1527
1528 if allow_untagged == 'yes':
1529 if pvids:
1530 pvid_final = pvids[0]
1531 elif bridge_pvid:
1532 pvid_final = bridge_pvid
1533 else:
1534 pvid_final = '1'
1535 else:
1536 pvid_final = None
1537
1538 self._apply_bridge_vids_and_pvid(bportifaceobj, vids_final,
1539 pvid_final, False)
1540
1541 def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware):
1542 err = False
1543
1544 if (ifaceobj.get_attr_value_first('bridge-port-vids') and
1545 ifaceobj.get_attr_value_first('bridge-port-pvids')):
1546 # Old style bridge port vid info
1547 # skip new style setting on ports
1548 return
1549 self.logger.info('%s: applying bridge configuration '
1550 %ifaceobj.name + 'specific to ports')
1551
1552 bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
1553 if bridge_vids:
1554 bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
1555 else:
1556 bridge_vids = None
1557
1558 bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
1559 if bridge_pvid:
1560 bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
1561 else:
1562 bridge_pvid = None
1563
1564 if (ifaceobj.module_flags.get(self.name, 0x0) &
1565 bridgeFlags.PORT_PROCESSED_OVERRIDE):
1566 port_processed_override = True
1567 else:
1568 port_processed_override = False
1569
1570 bridgeports = self._get_bridge_port_list(ifaceobj)
1571 if not bridgeports:
1572 self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
1573 return
1574 self.ipcmd.batch_start()
1575 for bport in bridgeports:
1576 # Use the brctlcmd bulk set method: first build a dictionary
1577 # and then call set
1578 if not self.ipcmd.bridge_port_exists(ifaceobj.name, bport):
1579 self.logger.info('%s: skipping bridge config' %ifaceobj.name +
1580 ' for port %s (missing port)' %bport)
1581 continue
1582 self.logger.info('%s: processing bridge config for port %s'
1583 %(ifaceobj.name, bport))
1584 bportifaceobjlist = ifaceobj_getfunc(bport)
1585 if not bportifaceobjlist:
1586 continue
1587 for bportifaceobj in bportifaceobjlist:
1588 # Dont process bridge port if it already has been processed
1589 # and there is no override on port_processed
1590 if (not port_processed_override and
1591 (bportifaceobj.module_flags.get(self.name,0x0) &
1592 bridgeFlags.PORT_PROCESSED)):
1593 continue
1594 try:
1595 # Add attributes specific to the vlan aware bridge
1596 if bridge_vlan_aware:
1597 self._apply_bridge_vlan_aware_port_settings_all(
1598 bportifaceobj, bridge_vids, bridge_pvid)
1599 elif self.warn_on_untagged_bridge_absence:
1600 self._check_untagged_bridge(ifaceobj.name, bportifaceobj, ifaceobj_getfunc)
1601 except exceptions.ReservedVlanException as e:
1602 raise e
1603 except Exception, e:
1604 err = True
1605 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
1606 pass
1607 self.ipcmd.bridge_batch_commit()
1608 if err:
1609 raise Exception('%s: errors applying port settings' %ifaceobj.name)
1610
1611 def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc):
1612 if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN:
1613 lower_ifaceobj_list = ifaceobj_getfunc(bridgeportifaceobj.lowerifaces[0])
1614 if lower_ifaceobj_list and lower_ifaceobj_list[0] and \
1615 not lower_ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
1616 self.logger.warn('%s: untagged bridge not found. Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.' % bridgename)
1617 self.warn_on_untagged_bridge_absence = False
1618
1619 def bridge_port_get_bridge_name(self, ifaceobj):
1620 bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
1621 if not bridgename:
1622 # bridge port is not enslaved to a bridge we need to find
1623 # the bridge in it's upper ifaces then enslave it
1624 for u in ifaceobj.upperifaces:
1625 if self.ipcmd.is_bridge(u):
1626 return True, u
1627 return False, None
1628 # return should_enslave port, bridgename
1629 return False, bridgename
1630
1631 def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port):
1632 if should_enslave_port:
1633 netlink.link_set_master(ifaceobj.name, bridge_name)
1634 self.handle_ipv6([ifaceobj.name], '1')
1635
1636 bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
1637 bridge_pvid = self._get_bridge_pvid(bridge_name, ifaceobj_getfunc)
1638 try:
1639 self._apply_bridge_vlan_aware_port_settings_all(ifaceobj, bridge_vids, bridge_pvid)
1640 except Exception as e:
1641 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
1642 return
1643
1644 def up_bridge_port(self, ifaceobj, ifaceobj_getfunc):
1645 should_enslave_port, bridge_name = self.bridge_port_get_bridge_name(ifaceobj)
1646
1647 if not bridge_name:
1648 # bridge doesn't exist
1649 return
1650
1651 vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridge_name)
1652 if vlan_aware_bridge:
1653 self.up_bridge_port_vlan_aware_bridge(ifaceobj,
1654 ifaceobj_getfunc,
1655 bridge_name,
1656 should_enslave_port)
1657
1658 bridge_ifaceobj = ifaceobj_getfunc(bridge_name)[0]
1659
1660 self.up_apply_brports_attributes(target_ports=[ifaceobj.name],
1661 ifaceobj=bridge_ifaceobj,
1662 ifaceobj_getfunc=ifaceobj_getfunc,
1663 bridge_vlan_aware=vlan_aware_bridge)
1664
1665 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED
1666
1667 def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_exists):
1668 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
1669 if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
1670 return False
1671 if link_exists:
1672 ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
1673 return True
1674 return False
1675
1676 @staticmethod
1677 def parse_interface_list_value(user_config):
1678 config = dict()
1679 for entry in user_config.split():
1680 ifname, value = entry.split('=')
1681 config[ifname] = value
1682 return config
1683
1684 def sync_bridge_learning_to_vxlan_brport(self, bridge_name, bridge_vlan_aware, brport_ifaceobj,
1685 brport_name, brport_ifla_info_slave_data, brport_learning):
1686 """
1687 brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
1688 and
1689 brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
1690
1691 Checks are not performed in this function and must be verified
1692 before. This is done this way to avoid calling this method on
1693 non vlan & bridge port interfaces thus wasting a bit less time
1694 """
1695
1696 kind = None
1697 ifla_info_data = {}
1698
1699 brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first('vxlan-learning')
1700 # if the user explicitly defined vxlan-learning we need to honor his config
1701 # and not sync vxlan-learning with bridge-learning
1702
1703 brport_vxlan_learning = self.ipcmd.get_vxlandev_learning(brport_name)
1704
1705 # if BRIDGE_LEARNING is in the desired configuration
1706 # and differs from the running vxlan configuration
1707 if brport_learning is not None and brport_learning != brport_vxlan_learning and not brport_vxlan_learning_config:
1708 kind = 'vxlan'
1709 ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_learning}
1710 self.logger.info('%s: %s: vxlan learning and bridge learning out of sync: set %s'
1711 % (bridge_name, brport_name, brport_learning))
1712
1713 elif brport_learning is None and bridge_vlan_aware:
1714 # is bridge-learning is not configured but the bridge is vlan-aware
1715
1716 running_value = self.ipcmd.get_brport_learning_bool(brport_name)
1717 default_value = utils.get_boolean_from_string(self.get_mod_subattr('bridge-learning', 'default'))
1718
1719 if default_value != running_value:
1720 brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] = default_value
1721
1722 if not brport_vxlan_learning_config:
1723 kind = 'vxlan'
1724 ifla_info_data = {Link.IFLA_VXLAN_LEARNING: default_value}
1725 self.logger.info('%s: %s: reset brport learning to %s and sync vxlan learning'
1726 % (bridge_name, brport_name, default_value))
1727
1728 # if kind and ifla_info_data are set they will be added to the
1729 # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING
1730 return kind, ifla_info_data
1731
1732 def check_vxlan_brport_arp_suppress(self, ifaceobj, bridge_vlan_aware, brport_ifaceobj, brport_name, user_config):
1733 if user_config:
1734 if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
1735 self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
1736 'is not supported on a non-vxlan port'
1737 % (ifaceobj.name, brport_name))
1738 raise Exception()
1739 elif (bridge_vlan_aware and
1740 (not self.arp_nd_suppress_only_on_vxlan or
1741 (self.arp_nd_suppress_only_on_vxlan and
1742 brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN))):
1743 return self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
1744 return None
1745
1746 def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
1747 ifname = ifaceobj.name
1748
1749 try:
1750 brports_ifla_info_slave_data = dict()
1751 brport_ifaceobj_dict = dict()
1752
1753 running_brports = self.brctlcmd.get_bridge_ports(ifname)
1754
1755 if target_ports:
1756 new_targets = []
1757 for brport_name in target_ports:
1758 if brport_name not in running_brports:
1759 self.logger.info('%s: not enslaved to bridge %s: ignored for now' % (brport_name, ifname))
1760 else:
1761 new_targets.append(brport_name)
1762 running_brports = new_targets
1763
1764 self.logger.info('%s: applying bridge port configuration: %s' % (ifname, running_brports))
1765
1766 # If target_ports is specified we want to configure only this
1767 # sub-list of port we need to check if these ports are already
1768 # enslaved, if not they will be ignored.
1769 # If target_ports is not populated we will apply the brport
1770 # attributes on all running brport.
1771
1772 for port in running_brports:
1773 brport_list = ifaceobj_getfunc(port)
1774 if brport_list:
1775 brport_ifaceobj_dict[port] = brport_list[0]
1776 brports_ifla_info_slave_data[port] = dict()
1777
1778 bridge_ports_learning = {}
1779
1780 # we iterate through all IFLA_BRPORT supported attributes
1781 for attr_name, nl_attr in self._ifla_brport_attributes_map:
1782 br_config = ifaceobj.get_attr_value_first(attr_name)
1783 translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr)
1784
1785 if not translate_func:
1786 # if no translation function is found,
1787 # we ignore this attribute and continue
1788 continue
1789
1790 if not br_config:
1791 # user didn't specify any value for this attribute
1792 # looking at policy overrides
1793 br_config = policymanager.policymanager_api.get_iface_default(
1794 module_name=self.__class__.__name__,
1795 ifname=ifname,
1796 attr=attr_name
1797 )
1798
1799 if br_config:
1800 #if bridge_vlan_aware:
1801 # self.logger.info('%s: is a vlan-aware bridge, "%s %s" '
1802 # 'should be configured under the ports'
1803 # % (ifname, attr_name, br_config))
1804
1805 # convert the <interface-yes-no-0-1-list> and <interface-range-list> value to subdict
1806 # brport_name: { attr: value }
1807 # example:
1808 # bridge-portprios swp1=5 swp2=32
1809 # swp1: { bridge-portprios: 5 } swp2: { bridge-portprios: 32}
1810 if '=' in br_config:
1811 try:
1812 br_config = self.parse_interface_list_value(br_config)
1813 except:
1814 self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config))
1815 continue
1816
1817 for brport_ifaceobj in brport_ifaceobj_dict.values():
1818 brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
1819 brport_name = brport_ifaceobj.name
1820
1821 if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
1822 # if the port has just been enslaved, info_slave_data is not cached yet
1823 cached_value = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', nl_attr])
1824 else:
1825 cached_value = None
1826
1827 if not brport_config:
1828 # if a brport attribute was specified under the bridge and not under the port
1829 # we assign the bridge value to the port. If an attribute is both defined under
1830 # the bridge and the brport we keep the value of the port and ignore the br val.
1831 if type(br_config) == dict:
1832 # if the attribute value was in the format interface-list-value swp1=XX swp2=YY
1833 # br_config is a dictionary, example:
1834 # bridge-portprios swp1=5 swp2=32 = {swp1: 5, swp2: 32}
1835 brport_config = br_config.get(brport_name)
1836 else:
1837 brport_config = br_config
1838
1839 if not brport_config:
1840 brport_config = policymanager.policymanager_api.get_iface_default(
1841 module_name=self.__class__.__name__,
1842 ifname=brport_name,
1843 attr=attr_name
1844 )
1845
1846 user_config = brport_config
1847
1848 # attribute specific work
1849 # This shouldn't be here but we don't really have a choice otherwise this
1850 # will require too much code duplication and will make the code very complex
1851 if nl_attr == Link.IFLA_BRPORT_ARP_SUPPRESS:
1852 try:
1853 arp_suppress = self.check_vxlan_brport_arp_suppress(ifaceobj,
1854 bridge_vlan_aware,
1855 brport_ifaceobj,
1856 brport_name,
1857 user_config)
1858 if arp_suppress:
1859 user_config = arp_suppress
1860 except:
1861 continue
1862 elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK:
1863 # special handking for group_fwd_mask because Cisco proprietary
1864 # protocol needs to be set via a private netlink attribute
1865 self.ifla_brport_group_fwd_mask(ifname, brport_name,
1866 brports_ifla_info_slave_data,
1867 user_config, cached_value)
1868 continue
1869
1870 #if brport_config:
1871 # if not bridge_vlan_aware:
1872 # self.logger.info('%s: %s: is not a vlan-aware bridge, "%s %s" '
1873 # 'should be configured under the bridge'
1874 # % (ifname, brport_name,
1875 # attr_name, brport_config))
1876
1877 if user_config:
1878 user_config_nl = translate_func(user_config)
1879 # check config value against running value
1880 if user_config_nl != cached_value:
1881 brports_ifla_info_slave_data[brport_name][nl_attr] = user_config_nl
1882 self.logger.info('%s: %s: set %s %s' % (ifname, brport_name, attr_name, user_config))
1883 self.logger.debug('(cache %s)' % cached_value)
1884
1885 if nl_attr == Link.IFLA_BRPORT_LEARNING:
1886 # for vxlan-learning sync purposes we need to save the user config for each brports.
1887 # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
1888 # IFLA_BRPORT_LEARNING if the user value is already configured and running
1889 # nevertheless we still need to check if the vxlan-learning is rightly synced with
1890 # the brport since it might go out of sync for X and Y reasons.
1891 bridge_ports_learning[brport_name] = user_config_nl
1892
1893 elif cached_value is not None:
1894 # no config found, do we need to reset to default?
1895 default = self.get_attr_default_value(attr_name)
1896 if default:
1897 default_netlink = translate_func(default)
1898
1899 if (nl_attr == Link.IFLA_BRPORT_LEARNING
1900 and not ifupdownflags.flags.PERFMODE
1901 and brport_name not in newly_enslaved_ports):
1902 try:
1903 if self.ipcmd.get_brport_peer_link(brport_name):
1904 if default_netlink != cached_value:
1905 self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
1906 % (ifname, brport_name))
1907 continue
1908 bridge_ports_learning[brport_name] = default_netlink
1909 except Exception as e:
1910 self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
1911
1912 if default_netlink != cached_value:
1913 self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s'
1914 % (ifname, brport_name, attr_name, default))
1915 self.logger.debug('(cache %s)' % cached_value)
1916 brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink
1917
1918 # applying bridge port configuration via netlink
1919 for brport_name, brport_ifla_info_slave_data in brports_ifla_info_slave_data.items():
1920
1921 brport_ifaceobj = brport_ifaceobj_dict.get(brport_name)
1922 if (brport_ifaceobj
1923 and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
1924 and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
1925 # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val
1926 # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1}
1927 kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name,
1928 bridge_vlan_aware,
1929 brport_ifaceobj,
1930 brport_name,
1931 brport_ifla_info_slave_data,
1932 bridge_ports_learning.get(brport_name))
1933 else:
1934 kind = None
1935 ifla_info_data = {}
1936
1937 if brport_ifla_info_slave_data or ifla_info_data:
1938 try:
1939 netlink.link_add_set(ifname=brport_name,
1940 kind=kind,
1941 ifla_info_data=ifla_info_data,
1942 slave_kind='bridge',
1943 ifla_info_slave_data=brport_ifla_info_slave_data)
1944 except Exception as e:
1945 self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e)))
1946
1947 self._set_bridge_vidinfo_compat(ifaceobj)
1948 self._set_bridge_mcqv4src_compat(ifaceobj)
1949 self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
1950
1951 except Exception as e:
1952 self.log_error(str(e), ifaceobj)
1953
1954 def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask):
1955 """
1956 Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI
1957 Since this is the only ifupdown2 attribute dealing with more than 1 netlink
1958 field we need to have special handling for that.
1959 """
1960 ifla_brport_group_fwd_mask = 0
1961 ifla_brport_group_fwd_maskhi = 0
1962 if user_config:
1963 for group in re.split(',|\s*', user_config):
1964 if not group:
1965 continue
1966
1967 callback = self.l2protocol_tunnel_callback.get(group)
1968
1969 if not callable(callback):
1970 self.logger.warning('%s: %s: bridge-l2protocol-tunnel ignoring invalid parameter \'%s\'' % (ifname, brport_name, group))
1971 else:
1972 ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi)
1973
1974 # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above
1975 cached_ifla_brport_group_fwd_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
1976
1977 log_mask_change = True
1978 # if user specify bridge-l2protocol-tunnel stp cdp
1979 # we need to set both MASK and MASKHI but we only want to log once
1980
1981 if cached_ifla_brport_group_fwd_mask is None:
1982 cached_ifla_brport_group_fwd_mask = 0
1983 if cached_ifla_brport_group_fwd_maskhi is None:
1984 cached_ifla_brport_group_fwd_maskhi = 0
1985
1986 # if the cache value is None it means that the kernel doesn't support this attribute
1987 # or that the cache is stale, we dumped this intf before it was enslaved in the bridge
1988
1989 if ifla_brport_group_fwd_mask != cached_ifla_brport_group_fwd_mask:
1990 if log_mask_change:
1991 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
1992 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_mask)
1993 log_mask_change = False
1994 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASK] = ifla_brport_group_fwd_mask
1995
1996 if ifla_brport_group_fwd_maskhi != cached_ifla_brport_group_fwd_maskhi:
1997 if log_mask_change:
1998 self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
1999 self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_maskhi)
2000 brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASKHI] = ifla_brport_group_fwd_maskhi
2001
2002 def up_bridge(self, ifaceobj, ifaceobj_getfunc):
2003 ifname = ifaceobj.name
2004
2005 if ifupdownflags.flags.PERFMODE:
2006 link_just_created = True
2007 link_exists = False
2008 else:
2009 link_exists = self.ipcmd.link_exists(ifaceobj.name)
2010 link_just_created = not link_exists
2011
2012 if not link_exists:
2013 netlink.link_add_bridge(ifname)
2014 else:
2015 self.logger.info('%s: bridge already exists' % ifname)
2016
2017 bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, not link_just_created)
2018
2019 self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)
2020
2021 try:
2022 newly_enslaved_ports = self._add_ports(ifaceobj, ifaceobj_getfunc)
2023 self.up_apply_brports_attributes(ifaceobj, ifaceobj_getfunc, bridge_vlan_aware,
2024 newly_enslaved_ports=newly_enslaved_ports)
2025 except Exception as e:
2026 self.logger.warning('%s: apply bridge ports settings: %s' % (ifname, str(e)))
2027
2028 running_ports = ''
2029 try:
2030 running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
2031 if not running_ports:
2032 return
2033 self.handle_ipv6([], '1', ifaceobj=ifaceobj)
2034 self._apply_bridge_port_settings_all(ifaceobj,
2035 ifaceobj_getfunc=ifaceobj_getfunc,
2036 bridge_vlan_aware=bridge_vlan_aware)
2037 except exceptions.ReservedVlanException as e:
2038 raise e
2039 except Exception as e:
2040 self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e)))
2041 finally:
2042 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
2043 for p in running_ports:
2044 if (ifaceobj_getfunc(p)[0].link_privflags &
2045 ifaceLinkPrivFlags.KEEP_LINK_DOWN):
2046 netlink.link_set_updown(p, "down")
2047 continue
2048 try:
2049 netlink.link_set_updown(p, "up")
2050 except Exception, e:
2051 self.logger.debug('%s: %s: link set up (%s)'
2052 % (ifaceobj.name, p, str(e)))
2053 pass
2054
2055 try:
2056 self._up_bridge_mac(ifaceobj, ifaceobj_getfunc)
2057 except Exception as e:
2058 self.logger.warning('%s: setting bridge mac address: %s' % (ifaceobj.name, str(e)))
2059
2060 def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc):
2061 if self.bridge_mac_iface and self.bridge_mac_iface[0] and self.bridge_mac_iface[1]:
2062 return self.bridge_mac_iface
2063
2064 if self.bridge_mac_iface_list:
2065 self.logger.debug('bridge mac iface list: %s' % self.bridge_mac_iface_list)
2066
2067 for bridge_mac_intf in self.bridge_mac_iface_list:
2068 ifaceobj_list = ifaceobj_getfunc(bridge_mac_intf)
2069 iface_mac = None
2070
2071 if ifaceobj_list:
2072 for obj in ifaceobj_list:
2073 iface_user_configured_hwaddress = utils.strip_hwaddress(obj.get_attr_value_first('hwaddress'))
2074 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2075 if iface_user_configured_hwaddress:
2076 iface_mac = iface_user_configured_hwaddress
2077
2078 if not iface_mac and not self.ipcmd.link_exists(bridge_mac_intf):
2079 continue
2080
2081 if not iface_mac:
2082 iface_mac = self.ipcmd.cache_get('link', [bridge_mac_intf, 'hwaddress'])
2083 # if hwaddress attribute is not configured we use the running mac addr
2084
2085 self.bridge_mac_iface = (bridge_mac_intf, iface_mac)
2086 return self.bridge_mac_iface
2087 elif self.bridge_set_static_mac_from_port:
2088 # no policy was provided, we need to get the first physdev or bond ports
2089 # and use its hwaddress to set the bridge mac
2090 for port in self._get_bridge_port_list_user_ordered(ifaceobj) or []:
2091 # iterate through the bridge-port list
2092 for port_obj in ifaceobj_getfunc(port) or []:
2093 # check if the port is a physdev (link_kind is null) or a bon
2094 if port_obj.link_kind != ifaceLinkKind.VXLAN:
2095 iface_user_configured_hwaddress = utils.strip_hwaddress(port_obj.get_attr_value_first('hwaddress'))
2096 # if user did configured 'hwaddress' we need to use this value instead of the cached value.
2097 if iface_user_configured_hwaddress:
2098 iface_mac = iface_user_configured_hwaddress.lower()
2099 # we need to "normalize" the user provided MAC so it can match with
2100 # what we have in the cache (data retrieved via a netlink dump by
2101 # nlmanager). nlmanager return all macs in lower-case
2102 else:
2103 iface_mac = self.ipcmd.link_get_hwaddress(port)
2104
2105 if iface_mac:
2106 self.bridge_mac_iface = (port, iface_mac)
2107 return self.bridge_mac_iface
2108
2109 return None, None
2110
2111 def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac):
2112 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'):
2113 self.ipcmd.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
2114
2115 def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc):
2116 """
2117 We have a day one bridge mac changing problem with changing ports
2118 (basically bridge mac changes when the port it inherited the mac from
2119 gets de-enslaved).
2120
2121 We have discussed this problem many times before and tabled it.
2122 The issue has aggravated with vxlan bridge ports having auto-generated
2123 random macs...which change on every reboot.
2124
2125 ifupdown2 extract from policy files an iface to select a mac from and
2126 configure it automatically.
2127 """
2128 if ifaceobj.get_attr_value('hwaddress'):
2129 # if the user configured a static hwaddress
2130 # there is no need to assign one
2131 return
2132
2133 ifname = ifaceobj.name
2134 mac_intf, bridge_mac = self._get_bridge_mac(ifaceobj, ifname, ifaceobj_getfunc)
2135 self.logger.debug("%s: _get_bridge_mac returned (%s, %s)"
2136 %(ifname, mac_intf, bridge_mac))
2137
2138 if bridge_mac:
2139 # if an interface is configured with the following attribute:
2140 # hwaddress 08:00:27:42:42:4
2141 # the cache_check won't match because nlmanager return "08:00:27:42:42:04"
2142 # from the kernel. The only way to counter that is to convert all mac to int
2143 # and compare the ints, it will increase perfs and be safer.
2144 cached_value = self.ipcmd.cache_get('link', [ifname, 'hwaddress'])
2145 self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
2146 if cached_value and cached_value == bridge_mac:
2147 # the bridge mac is already set to the bridge_mac_intf's mac
2148 return
2149
2150 self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf))
2151 try:
2152 self.ipcmd.link_set(ifname, 'address', value=bridge_mac, force=True)
2153 except Exception as e:
2154 self.logger.info('%s: %s' % (ifname, str(e)))
2155 # log info this error because the user didn't explicitly configured this
2156 else:
2157 self._add_bridge_mac_to_fdb(ifaceobj, self.ipcmd.link_get_hwaddress(ifname))
2158
2159 def _up(self, ifaceobj, ifaceobj_getfunc=None):
2160 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
2161 self.up_bridge_port(ifaceobj, ifaceobj_getfunc)
2162
2163 elif ifaceobj.link_kind & ifaceLinkKind.BRIDGE:
2164 self.up_bridge(ifaceobj, ifaceobj_getfunc)
2165
2166 else:
2167 bridge_attributes = self._modinfo.get('attrs', {}).keys()
2168
2169 for ifaceobj_config_attr in ifaceobj.config.keys():
2170 if ifaceobj_config_attr in bridge_attributes:
2171 self.logger.warning('%s: invalid use of bridge attribute (%s) on non-bridge stanza'
2172 % (ifaceobj.name, ifaceobj_config_attr))
2173
2174 def _down(self, ifaceobj, ifaceobj_getfunc=None):
2175 if not self._is_bridge(ifaceobj):
2176 return
2177 ifname = ifaceobj.name
2178 if not self.ipcmd.link_exists(ifname):
2179 return
2180 try:
2181 running_ports = self.brctlcmd.get_bridge_ports(ifname)
2182 if running_ports:
2183 self.handle_ipv6(running_ports, '0')
2184 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
2185 map(lambda p: netlink.link_set_updown(p, 'down'), running_ports)
2186 except Exception as e:
2187 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
2188 try:
2189 netlink.link_del(ifname)
2190 except Exception as e:
2191 ifaceobj.set_status(ifaceStatus.ERROR)
2192 self.logger.error(str(e))
2193 # netlink exception already contains the ifname
2194
2195 def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
2196 running_attrs = {}
2197 if ports:
2198 running_bridge_port_vids = ''
2199 for p in ports:
2200 try:
2201 running_vids = self._get_runing_vids(p)
2202 if running_vids:
2203 running_bridge_port_vids += ' %s=%s' %(p,
2204 ','.join(running_vids))
2205 except Exception:
2206 pass
2207 running_attrs['bridge-port-vids'] = running_bridge_port_vids
2208
2209 running_bridge_port_pvid = ''
2210 for p in ports:
2211 try:
2212 running_pvid = self._get_runing_pvid(p)
2213 if running_pvid:
2214 running_bridge_port_pvid += ' %s=%s' %(p,
2215 running_pvid)
2216 except Exception:
2217 pass
2218 running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
2219
2220 running_bridge_vids = self.ipcmd.bridge_vlan_get_vids(ifaceobjrunning.name)
2221 if running_bridge_vids:
2222 running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
2223 return running_attrs
2224
2225 def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
2226 bridgeports=None):
2227 running_attrs = {}
2228
2229 # 'bridge-vids' under the bridge is all about 'vids' on the port.
2230 # so query the ports
2231 running_bridgeport_vids = []
2232 running_bridgeport_pvids = []
2233 for bport in bridgeports:
2234 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2235 if vids:
2236 running_bridgeport_vids.append(' '.join(vids))
2237 if pvid:
2238 running_bridgeport_pvids.append(pvid)
2239
2240 bridge_vids = None
2241 if running_bridgeport_vids:
2242 (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
2243 if freq == len(bridgeports):
2244 running_attrs['bridge-vids'] = vidval
2245 bridge_vids = vidval.split()
2246
2247 bridge_pvid = None
2248 if running_bridgeport_pvids:
2249 (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
2250 if freq == len(bridgeports) and vidval != '1':
2251 running_attrs['bridge-pvid'] = vidval
2252 bridge_pvid = vidval.split()[0]
2253
2254 # Go through all bridge ports and find their vids
2255 for bport in bridgeports:
2256 bportifaceobj = ifaceobj_getfunc(bport)
2257 if not bportifaceobj:
2258 continue
2259 bport_vids = []
2260 bport_pvid = None
2261 (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
2262 if vids and vids != bridge_vids:
2263 bport_vids = vids
2264 if pvid and pvid != bridge_pvid:
2265 bport_pvid = pvid
2266 if bport_vids and bport_pvid in bport_vids:
2267 bport_vids.remove(bport_pvid)
2268 if (not bport_vids and bport_pvid and bport_pvid != '1'):
2269 bportifaceobj[0].replace_config('bridge-access', bport_pvid)
2270 bportifaceobj[0].delete_config('bridge-pvid')
2271 bportifaceobj[0].delete_config('bridge-vids')
2272 else:
2273 if bport_pvid and bport_pvid != '1':
2274 bportifaceobj[0].replace_config('bridge-pvid', bport_pvid)
2275 else:
2276 # delete any stale bridge-vids under ports
2277 bportifaceobj[0].delete_config('bridge-pvid')
2278 if bport_vids:
2279 bportifaceobj[0].replace_config('bridge-vids',
2280 ' '.join(bport_vids))
2281 else:
2282 # delete any stale bridge-vids under ports
2283 bportifaceobj[0].delete_config('bridge-vids')
2284 return running_attrs
2285
2286 def _query_running_mcqv4src(self, ifaceobjrunning):
2287 running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(ifaceobjrunning.name)
2288 mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
2289 mcqs.sort()
2290 mcq = ' '.join(mcqs)
2291 return mcq
2292
2293 def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
2294 bridge_vlan_aware=False):
2295 bridgeattrdict = {}
2296 userspace_stp = 0
2297 ports = None
2298 skip_kernel_stp_attrs = 0
2299
2300 try:
2301 if self.systcl_get_net_bridge_stp_user_space() == '1':
2302 userspace_stp = 1
2303 except Exception as e:
2304 self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
2305
2306 tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
2307 if not tmpbridgeattrdict:
2308 self.logger.warn('%s: unable to get bridge attrs'
2309 %ifaceobjrunning.name)
2310 return bridgeattrdict
2311
2312 # Fill bridge_ports and bridge stp attributes first
2313 ports = tmpbridgeattrdict.get('ports')
2314 if ports:
2315 bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())]
2316 stp = tmpbridgeattrdict.get('stp', 'no')
2317 if stp != self.get_mod_subattr('bridge-stp', 'default'):
2318 bridgeattrdict['bridge-stp'] = [stp]
2319
2320 if stp == 'yes' and userspace_stp:
2321 skip_kernel_stp_attrs = 1
2322
2323 vlan_stats = utils.get_onff_from_onezero(
2324 tmpbridgeattrdict.get('vlan-stats', None))
2325 if (vlan_stats and
2326 vlan_stats != self.get_mod_subattr('bridge-vlan-stats', 'default')):
2327 bridgeattrdict['bridge-vlan-stats'] = [vlan_stats]
2328
2329 bool2str = {'0': 'no', '1': 'yes'}
2330 # pick all other attributes
2331 for k,v in tmpbridgeattrdict.items():
2332 if not v:
2333 continue
2334 if k == 'ports' or k == 'stp':
2335 continue
2336
2337 if skip_kernel_stp_attrs and k[:2] != 'mc':
2338 # only include igmp attributes if kernel stp is off
2339 continue
2340 attrname = 'bridge-' + k
2341 mod_default = self.get_mod_subattr(attrname, 'default')
2342 if v != mod_default:
2343 # convert '0|1' running values to 'no|yes'
2344 if v in bool2str.keys() and bool2str[v] == mod_default:
2345 continue
2346 bridgeattrdict[attrname] = [v]
2347
2348 if bridge_vlan_aware:
2349 if not ports:
2350 ports = {}
2351 bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
2352 ifaceobj_getfunc,
2353 ports.keys())
2354 else:
2355 bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
2356 ports)
2357 if bridgevidinfo:
2358 bridgeattrdict.update({k : [v] for k, v in bridgevidinfo.items()
2359 if v})
2360
2361 mcq = self._query_running_mcqv4src(ifaceobjrunning)
2362 if mcq:
2363 bridgeattrdict['bridge-mcqv4src'] = [mcq]
2364
2365 if skip_kernel_stp_attrs:
2366 return bridgeattrdict
2367
2368 # Do this only for vlan-UNAWARE-bridge
2369 if ports and not bridge_vlan_aware:
2370 portconfig = {'bridge-pathcosts' : '',
2371 'bridge-portprios' : '',
2372 'bridge-learning' : '',
2373 'bridge-unicast-flood' : '',
2374 'bridge-multicast-flood' : '',
2375 'bridge-arp-nd-suppress' : '',
2376 }
2377 for p, v in ports.items():
2378 v = self.brctlcmd.bridge_get_pathcost(ifaceobjrunning.name, p)
2379 if v and v != self.get_mod_subattr('bridge-pathcosts',
2380 'default'):
2381 portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
2382
2383 v = self.brctlcmd.bridge_get_portprio(ifaceobjrunning.name, p)
2384 if v and v != self.get_mod_subattr('bridge-portprios',
2385 'default'):
2386 portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
2387
2388 v = utils.get_onff_from_onezero(
2389 self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
2390 p, 'learning'))
2391 if (v and
2392 v != self.get_mod_subattr('bridge-learning', 'default')):
2393 portconfig['bridge-learning'] += ' %s=%s' %(p, v)
2394
2395 v = utils.get_onff_from_onezero(
2396 self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
2397 p, 'unicast-flood'))
2398 if (v and
2399 v != self.get_mod_subattr('bridge-unicast-flood',
2400 'default')):
2401 portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v)
2402
2403 v = utils.get_onff_from_onezero(
2404 self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
2405 p, 'multicast-flood'))
2406 if (v and
2407 v != self.get_mod_subattr('bridge-multicast-flood',
2408 'default')):
2409 portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v)
2410
2411 v = utils.get_onff_from_onezero(
2412 self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
2413 p, 'arp-nd-suppress'))
2414 if (v and
2415 v != self.get_mod_subattr('bridge-arp-nd-suppress',
2416 'default')):
2417 portconfig['bridge-arp-nd-suppress'] += ' %s=%s' %(p, v)
2418
2419 bridgeattrdict.update({k : [v] for k, v in portconfig.items()
2420 if v})
2421
2422 return bridgeattrdict
2423
2424 def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
2425 running_mcqs = self._query_running_mcqv4src(ifaceobj)
2426 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
2427 if attrval:
2428 mcqs = attrval.split()
2429 mcqs.sort()
2430 mcqsout = ' '.join(mcqs)
2431 ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
2432 running_mcqs, 1 if running_mcqs != mcqsout else 0)
2433
2434 def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr):
2435 err = 0
2436 attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
2437 if attrval:
2438 running_bridge_port_vids = ''
2439 portlist = self.parse_port_list(ifaceobj.name, attrval)
2440 if not portlist:
2441 self.log_warn('%s: could not parse \'bridge-port-vids %s\''
2442 %(ifaceobj.name, attrval))
2443 return
2444 err = 0
2445 for p in portlist:
2446 try:
2447 (port, val) = p.split('=')
2448 vids = val.split(',')
2449 running_vids = self.ipcmd.bridge_vlan_get_vids(port)
2450 if running_vids:
2451 if not self._compare_vids(vids, running_vids):
2452 err += 1
2453 running_bridge_port_vids += ' %s=%s' %(port,
2454 ','.join(running_vids))
2455 else:
2456 running_bridge_port_vids += ' %s' %p
2457 else:
2458 err += 1
2459 except Exception, e:
2460 self.log_warn('%s: failure checking vid %s (%s)'
2461 %(ifaceobj.name, p, str(e)))
2462 if err:
2463 ifaceobjcurr.update_config_with_status('bridge-port-vids',
2464 running_bridge_port_vids, 1)
2465 else:
2466 ifaceobjcurr.update_config_with_status('bridge-port-vids',
2467 attrval, 0)
2468
2469 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
2470 if attrval:
2471 portlist = self.parse_port_list(ifaceobj.name, attrval)
2472 if not portlist:
2473 self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
2474 %(ifaceobj.name, attrval))
2475 return
2476 running_bridge_port_pvids = ''
2477 err = 0
2478 for p in portlist:
2479 try:
2480 (port, pvid) = p.split('=')
2481 running_pvid = self.ipcmd.bridge_vlan_get_vids(port)
2482 if running_pvid and running_pvid == pvid:
2483 running_bridge_port_pvids += ' %s' %p
2484 else:
2485 err += 1
2486 running_bridge_port_pvids += ' %s=%s' %(port,
2487 running_pvid)
2488 except Exception, e:
2489 self.log_warn('%s: failure checking pvid %s (%s)'
2490 %(ifaceobj.name, pvid, str(e)))
2491 if err:
2492 ifaceobjcurr.update_config_with_status('bridge-port-pvids',
2493 running_bridge_port_pvids, 1)
2494 else:
2495 ifaceobjcurr.update_config_with_status('bridge-port-pvids',
2496 running_bridge_port_pvids, 0)
2497
2498 vids = self.get_ifaceobj_bridge_vids(ifaceobj)
2499 if vids[1]:
2500 ifaceobjcurr.update_config_with_status(vids[0], vids[1], -1)
2501
2502 def _query_check_snooping_wdefault(self, ifaceobj):
2503 if (ifupdownflags.flags.WITHDEFAULTS
2504 and not self._vxlan_bridge_default_igmp_snooping
2505 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
2506 ifaceobj.replace_config('bridge-mcsnoop', 'no')
2507
2508 def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
2509 ifaceobj_getfunc=None):
2510 if not self._is_bridge(ifaceobj):
2511 return
2512 if not self.brctlcmd.bridge_exists(ifaceobj.name):
2513 self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
2514 return
2515
2516 self._query_check_snooping_wdefault(ifaceobj)
2517
2518 ifaceattrs = self.dict_key_subset(ifaceobj.config,
2519 self.get_mod_attrs())
2520 #Add default attributes if --with-defaults is set
2521 if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in ifaceattrs:
2522 ifaceattrs.append('bridge-stp')
2523 if not ifaceattrs:
2524 return
2525 try:
2526 runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name)
2527 if not runningattrs:
2528 self.logger.debug('%s: bridge: unable to get bridge attrs'
2529 %ifaceobj.name)
2530 runningattrs = {}
2531 except Exception, e:
2532 self.logger.warn(str(e))
2533 runningattrs = {}
2534
2535 self._query_check_support_yesno_attrs(runningattrs, ifaceobj)
2536
2537 filterattrs = ['bridge-vids', 'bridge-trunk', 'bridge-port-vids',
2538 'bridge-port-pvids']
2539
2540 diff = Set(ifaceattrs).difference(filterattrs)
2541
2542 if 'bridge-l2protocol-tunnel' in diff:
2543 diff.remove('bridge-l2protocol-tunnel')
2544 # bridge-l2protocol-tunnel requires separate handling
2545
2546 if 'bridge-ports' in diff:
2547 self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, runningattrs.get('ports', {}).keys(), ifaceobj_getfunc)
2548 diff.remove('bridge-ports')
2549
2550 for k in diff:
2551 # get the corresponding ifaceobj attr
2552 v = ifaceobj.get_attr_value_first(k)
2553 if not v:
2554 if ifupdownflags.flags.WITHDEFAULTS and k == 'bridge-stp':
2555 v = 'on' if self.default_stp_on else 'off'
2556 else:
2557 continue
2558 rv = runningattrs.get(k[7:])
2559 if k == 'bridge-mcqv4src':
2560 continue
2561 if k == 'bridge-maxwait' or k == 'bridge-waitport':
2562 ifaceobjcurr.update_config_with_status(k, v, 0)
2563 continue
2564 if k == 'bridge-vlan-aware':
2565 rv = self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)
2566 if (rv and v == 'yes') or (not rv and v == 'no'):
2567 ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
2568 v, 0)
2569 else:
2570 ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
2571 v, 1)
2572 elif k == 'bridge-stp':
2573 # special case stp compare because it may
2574 # contain more than one valid values
2575 stp_on_vals = ['on', 'yes']
2576 stp_off_vals = ['off', 'no']
2577 if ((v in stp_on_vals and rv in stp_on_vals) or
2578 (v in stp_off_vals and rv in stp_off_vals)):
2579 ifaceobjcurr.update_config_with_status('bridge-stp',
2580 rv, 0)
2581 else:
2582 ifaceobjcurr.update_config_with_status('bridge-stp',
2583 rv, 1)
2584 elif k in ['bridge-pathcosts',
2585 'bridge-portprios',
2586 'bridge-portmcrouter',
2587 'bridge-portmcfl',
2588 'bridge-learning',
2589 'bridge-unicast-flood',
2590 'bridge-multicast-flood',
2591 'bridge-arp-nd-suppress',
2592 ]:
2593 if k == 'bridge-arp-nd-suppress':
2594 brctlcmdattrname = k[7:]
2595 else:
2596 brctlcmdattrname = k[7:].rstrip('s')
2597 # for port attributes, the attributes are in a list
2598 # <portname>=<portattrvalue>
2599 status = 0
2600 currstr = ''
2601 vlist = self.parse_port_list(ifaceobj.name, v)
2602 if not vlist:
2603 continue
2604 for vlistitem in vlist:
2605 try:
2606 (p, v) = vlistitem.split('=')
2607 if k in ['bridge-learning',
2608 'bridge-unicast-flood',
2609 'bridge-multicast-flood',
2610 'bridge-arp-nd-suppress',
2611 ]:
2612 currv = utils.get_onoff_bool(
2613 self.brctlcmd.get_bridgeport_attr(
2614 ifaceobj.name, p,
2615 brctlcmdattrname))
2616 else:
2617 currv = self.brctlcmd.get_bridgeport_attr(
2618 ifaceobj.name, p,
2619 brctlcmdattrname)
2620 if currv:
2621 currstr += ' %s=%s' %(p, currv)
2622 else:
2623 currstr += ' %s=%s' %(p, 'None')
2624
2625 if k == 'bridge-portmcrouter':
2626 if self._ifla_brport_multicast_router_dict_to_int.get(v) != int(currv):
2627 status = 1
2628 elif currv != v:
2629 status = 1
2630 except Exception, e:
2631 self.log_warn(str(e))
2632 pass
2633 ifaceobjcurr.update_config_with_status(k, currstr, status)
2634 elif k == 'bridge-vlan-stats' or k == 'bridge-mcstats':
2635 rv = utils.get_onff_from_onezero(rv)
2636 if v != rv:
2637 ifaceobjcurr.update_config_with_status(k, rv, 1)
2638 else:
2639 ifaceobjcurr.update_config_with_status(k, rv, 0)
2640 elif not rv:
2641 if k == 'bridge-pvid' or k == 'bridge-vids' or k == 'bridge-trunk' or k == 'bridge-allow-untagged':
2642 # bridge-pvid and bridge-vids on a bridge does
2643 # not correspond directly to a running config
2644 # on the bridge. They correspond to default
2645 # values for the bridge ports. And they are
2646 # already checked against running config of the
2647 # bridge port and reported against a bridge port.
2648 # So, ignore these attributes under the bridge.
2649 # Use '2' for ignore today. XXX: '2' will be
2650 # mapped to a defined value in subsequent patches.
2651 ifaceobjcurr.update_config_with_status(k, v, 2)
2652 else:
2653 ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
2654 continue
2655 elif v.upper() != rv.upper():
2656 ifaceobjcurr.update_config_with_status(k, rv, 1)
2657 else:
2658 ifaceobjcurr.update_config_with_status(k, rv, 0)
2659
2660 self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr)
2661
2662 self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
2663 self._query_check_l2protocol_tunnel_on_bridge(ifaceobj, ifaceobjcurr, runningattrs)
2664
2665 def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc):
2666 bridge_all_ports = []
2667 for obj in ifaceobj_getfunc(ifaceobj.name) or []:
2668 bridge_all_ports.extend(self._get_bridge_port_list(obj) or [])
2669
2670 if not running_port_list and not bridge_all_ports:
2671 return
2672
2673 ports_list_status = 0 if not set(running_port_list).symmetric_difference(bridge_all_ports) else 1
2674
2675 try:
2676 port_list = self._get_ifaceobj_bridge_ports(ifaceobj).split()
2677 # we want to display the same bridge-ports list as provided
2678 # in the interfaces file but if this list contains regexes or
2679 # globs, for now, we won't try to change it.
2680 if 'regex' in port_list or 'glob' in port_list:
2681 port_list = running_port_list
2682 else:
2683 ordered = []
2684 for i in range(0, len(port_list)):
2685 if port_list[i] in running_port_list:
2686 ordered.append(port_list[i])
2687 port_list = ordered
2688 except:
2689 port_list = running_port_list
2690 ifaceobjcurr.update_config_with_status('bridge-ports', (' '.join(port_list) if port_list else ''), ports_list_status)
2691
2692 def get_ifaceobj_bridge_vids(self, ifaceobj):
2693 vids = ('bridge-vids', ifaceobj.get_attr_value_first('bridge-vids'))
2694 if not vids[1]:
2695 vids = ('bridge-trunk', ifaceobj.get_attr_value_first('bridge-trunk'))
2696 return vids
2697
2698 def get_ifaceobj_bridge_vids_value(self, ifaceobj):
2699 return self.get_ifaceobj_bridge_vids(ifaceobj)[1]
2700
2701 def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
2702 ifaceobjs = ifaceobj_getfunc(bridgename)
2703 for ifaceobj in ifaceobjs:
2704 vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
2705 if vids: return re.split(r'[\s\t,]\s*', vids)
2706 return None
2707
2708 def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
2709 ifaceobjs = ifaceobj_getfunc(bridgename)
2710 pvid = None
2711 for ifaceobj in ifaceobjs:
2712 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
2713 if pvid:
2714 break
2715 return pvid
2716
2717 def _get_bridge_name(self, ifaceobj):
2718 return self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
2719
2720 def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr,
2721 ifaceobj_getfunc, bridgename):
2722 attr_name = 'bridge-access'
2723 vid = ifaceobj.get_attr_value_first(attr_name)
2724 if vid:
2725 (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
2726 ifaceobj.name)
2727 if (not running_pvid or running_pvid != vid or
2728 (running_vids and running_vids[0] != vid)):
2729 ifaceobjcurr.update_config_with_status(attr_name,
2730 running_pvid, 1)
2731 else:
2732 ifaceobjcurr.update_config_with_status(attr_name, vid, 0)
2733 return
2734
2735 (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
2736 ifaceobj.name)
2737 attr_name = 'bridge-pvid'
2738 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
2739 if pvid:
2740 if running_pvid and running_pvid == pvid:
2741 ifaceobjcurr.update_config_with_status(attr_name,
2742 running_pvid, 0)
2743 else:
2744 ifaceobjcurr.update_config_with_status(attr_name,
2745 running_pvid, 1)
2746 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
2747 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
2748 (ifaceobj.flags & iface.OLDEST_SIBLING))):
2749 # if the interface has multiple iface sections,
2750 # we check the below only for the oldest sibling
2751 # or the last iface section
2752 pvid = self._get_bridge_pvid(bridgename, ifaceobj_getfunc)
2753 if pvid:
2754 if not running_pvid or running_pvid != pvid:
2755 ifaceobjcurr.status = ifaceStatus.ERROR
2756 ifaceobjcurr.status_str = 'bridge pvid error'
2757 elif not running_pvid or running_pvid != '1':
2758 ifaceobjcurr.status = ifaceStatus.ERROR
2759 ifaceobjcurr.status_str = 'bridge pvid error'
2760
2761 attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
2762 if vids:
2763 vids = re.split(r'[\s\t]\s*', vids)
2764 if not running_vids or not self._compare_vids(vids, running_vids,
2765 running_pvid):
2766 ifaceobjcurr.update_config_with_status(attr_name,
2767 ' '.join(running_vids), 1)
2768 else:
2769 ifaceobjcurr.update_config_with_status(attr_name,
2770 ' '.join(vids), 0)
2771 elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
2772 ((ifaceobj.flags & iface.HAS_SIBLINGS) and
2773 (ifaceobj.flags & iface.OLDEST_SIBLING))):
2774 # if the interface has multiple iface sections,
2775 # we check the below only for the oldest sibling
2776 # or the last iface section
2777
2778 # check if it matches the bridge vids
2779 bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc)
2780 if (bridge_vids and (not running_vids or
2781 not self._compare_vids(bridge_vids, running_vids, running_pvid))):
2782 ifaceobjcurr.status = ifaceStatus.ERROR
2783 ifaceobjcurr.status_str = 'bridge vid error'
2784
2785 def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
2786 ifaceobj_getfunc):
2787 if not self._is_bridge_port(ifaceobj):
2788 # Mark all bridge attributes as failed
2789 ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
2790 ['bridge-vids', 'bridge-trunk', 'bridge-pvid', 'bridge-access',
2791 'bridge-pathcosts', 'bridge-portprios',
2792 'bridge-portmcrouter',
2793 'bridge-learning',
2794 'bridge-portmcfl', 'bridge-unicast-flood',
2795 'bridge-multicast-flood',
2796 'bridge-arp-nd-suppress', 'bridge-l2protocol-tunnel'
2797 ], 1)
2798 return
2799 bridgename = self._get_bridge_name(ifaceobj)
2800 if not bridgename:
2801 self.logger.warn('%s: unable to determine bridge name'
2802 %ifaceobj.name)
2803 return
2804
2805 if self.ipcmd.bridge_is_vlan_aware(bridgename):
2806 self._query_check_bridge_port_vidinfo(ifaceobj, ifaceobjcurr,
2807 ifaceobj_getfunc,
2808 bridgename)
2809 for attr, dstattr in {'bridge-pathcosts' : 'pathcost',
2810 'bridge-portprios' : 'portprio',
2811 'bridge-portmcrouter' : 'portmcrouter',
2812 'bridge-portmcfl' : 'portmcfl',
2813 'bridge-learning' : 'learning',
2814 'bridge-unicast-flood' : 'unicast-flood',
2815 'bridge-multicast-flood' : 'multicast-flood',
2816 'bridge-arp-nd-suppress' : 'arp-nd-suppress',
2817 }.items():
2818 attrval = ifaceobj.get_attr_value_first(attr)
2819 if not attrval:
2820 continue
2821
2822 try:
2823 running_attrval = self.brctlcmd.get_bridgeport_attr(
2824 bridgename, ifaceobj.name, dstattr)
2825
2826 if dstattr == 'portmcfl':
2827 if not utils.is_binary_bool(attrval) and running_attrval:
2828 running_attrval = utils.get_yesno_boolean(
2829 utils.get_boolean_from_string(running_attrval))
2830 elif dstattr == 'portmcrouter':
2831 if self._ifla_brport_multicast_router_dict_to_int.get(attrval) == int(running_attrval):
2832 ifaceobjcurr.update_config_with_status(attr, attrval, 0)
2833 else:
2834 ifaceobjcurr.update_config_with_status(attr, attrval, 1)
2835 continue
2836 elif dstattr in ['learning',
2837 'unicast-flood',
2838 'multicast-flood',
2839 'arp-nd-suppress',
2840 ]:
2841 if not utils.is_binary_bool(attrval) and running_attrval:
2842 running_attrval = utils.get_onff_from_onezero(
2843 running_attrval)
2844
2845 if running_attrval != attrval:
2846 ifaceobjcurr.update_config_with_status(attr,
2847 running_attrval, 1)
2848 else:
2849 ifaceobjcurr.update_config_with_status(attr,
2850 running_attrval, 0)
2851 except Exception, e:
2852 self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
2853
2854 self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
2855
2856 def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr):
2857 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
2858
2859 if user_config_l2protocol_tunnel:
2860 result = 0
2861 try:
2862 self._query_check_l2protocol_tunnel(ifaceobj.name, user_config_l2protocol_tunnel)
2863 except Exception as e:
2864 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
2865 result = 1
2866 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
2867
2868 def _query_check_l2protocol_tunnel_on_bridge(self, ifaceobj, ifaceobjcurr, bridge_running_attrs):
2869 """
2870 In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport
2871 We need to make sure that all ports comply with the mask given under the bridge
2872 """
2873 user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
2874
2875 if user_config_l2protocol_tunnel:
2876 if '=' in user_config_l2protocol_tunnel:
2877 try:
2878 config_per_port_dict = self.parse_interface_list_value(user_config_l2protocol_tunnel)
2879 brport_list = config_per_port_dict.keys()
2880 except:
2881 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, 1)
2882 return
2883 else:
2884 config_per_port_dict = {}
2885 brport_list = bridge_running_attrs.get('ports', {}).keys()
2886 result = 1
2887 try:
2888 for brport_name in brport_list:
2889 self._query_check_l2protocol_tunnel(
2890 brport_name,
2891 config_per_port_dict.get(brport_name) if config_per_port_dict else user_config_l2protocol_tunnel
2892 )
2893 result = 0
2894 except Exception as e:
2895 self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
2896 result = 1
2897 ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
2898
2899 def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel):
2900 cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
2901 cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
2902
2903 for protocol in re.split(',|\s*', user_config_l2protocol_tunnel):
2904 callback = self.query_check_l2protocol_tunnel_callback.get(protocol)
2905
2906 if callable(callback):
2907 if not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
2908 raise Exception('%s: bridge-l2protocol-tunnel: protocol \'%s\' not present (cached value: %d | %d)'
2909 % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi))
2910
2911 def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None):
2912 cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
2913 cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
2914 running_protocols = []
2915 for protocol_name, callback in self.query_check_l2protocol_tunnel_callback.items():
2916 if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
2917 running_protocols = self.query_check_l2protocol_tunnel_callback.keys()
2918 running_protocols.remove('all')
2919 break
2920 elif callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
2921 running_protocols.append(protocol_name)
2922 if running_protocols:
2923 if brport_ifaceobj:
2924 brport_ifaceobj.update_config('bridge-l2protocol-tunnel', ' '.join(running_protocols))
2925 elif bridge_ifaceobj:
2926 current_config = bridge_ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
2927
2928 if current_config:
2929 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s %s=%s' % (current_config, brport_name, ','.join(running_protocols)))
2930 else:
2931 bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s=%s' % (brport_name, ','.join(running_protocols)))
2932
2933 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
2934 if self._is_bridge(ifaceobj):
2935 self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
2936 else:
2937 self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
2938 ifaceobj_getfunc)
2939
2940 def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
2941 if self.ipcmd.bridge_is_vlan_aware(ifaceobjrunning.name):
2942 ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
2943 ifaceobjrunning.update_config_dict(self._query_running_attrs(
2944 ifaceobjrunning,
2945 ifaceobj_getfunc,
2946 bridge_vlan_aware=True))
2947 else:
2948 ifaceobjrunning.update_config_dict(self._query_running_attrs(
2949 ifaceobjrunning, None))
2950
2951 def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
2952 if self.systcl_get_net_bridge_stp_user_space() == '1':
2953 return
2954
2955 v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
2956 if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
2957 ifaceobjrunning.update_config('bridge-pathcosts', v)
2958
2959 v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
2960 if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
2961 ifaceobjrunning.update_config('bridge-portprios', v)
2962
2963 def _query_running_bridge_port(self, ifaceobjrunning,
2964 ifaceobj_getfunc=None):
2965
2966 bridgename = self.ipcmd.bridge_port_get_bridge_name(
2967 ifaceobjrunning.name)
2968 bridge_vids = None
2969 bridge_pvid = None
2970 if not bridgename:
2971 self.logger.warn('%s: unable to find bridgename'
2972 %ifaceobjrunning.name)
2973 return
2974
2975 if not self.ipcmd.bridge_is_vlan_aware(bridgename):
2976 try:
2977 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0])
2978 except Exception as e:
2979 self.logger.debug('%s: q_query_running_bridge_l2protocol_tunnel: %s' % (ifaceobjrunning.name, str(e)))
2980 return
2981
2982 self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, brport_ifaceobj=ifaceobjrunning)
2983
2984 (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str(
2985 ifaceobjrunning.name)
2986 if bridge_port_vids and bridge_port_pvid in bridge_port_vids:
2987 bridge_port_vids.remove(bridge_port_pvid)
2988
2989 bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
2990 if bridgeifaceobjlist:
2991 bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
2992 bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
2993
2994 if not bridge_port_vids and bridge_port_pvid:
2995 # must be an access port
2996 if bridge_port_pvid != '1':
2997 ifaceobjrunning.update_config('bridge-access',
2998 bridge_port_pvid)
2999 else:
3000 if bridge_port_vids:
3001 if (not bridge_vids or bridge_port_vids != bridge_vids):
3002 ifaceobjrunning.update_config('bridge-vids',
3003 ' '.join(bridge_port_vids))
3004 if bridge_port_pvid and bridge_port_pvid != '1':
3005 if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
3006 ifaceobjrunning.update_config('bridge-pvid',
3007 bridge_port_pvid)
3008
3009 v = utils.get_onff_from_onezero(
3010 self.brctlcmd.get_bridgeport_attr(bridgename,
3011 ifaceobjrunning.name,
3012 'learning'))
3013 if v and v != self.get_mod_subattr('bridge-learning', 'default'):
3014 ifaceobjrunning.update_config('bridge-learning', v)
3015
3016 v = utils.get_onff_from_onezero(
3017 self.brctlcmd.get_bridgeport_attr(bridgename,
3018 ifaceobjrunning.name,
3019 'unicast-flood'))
3020 if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'):
3021 ifaceobjrunning.update_config('bridge-unicast-flood', v)
3022
3023 v = utils.get_onff_from_onezero(
3024 self.brctlcmd.get_bridgeport_attr(bridgename,
3025 ifaceobjrunning.name,
3026 'multicast-flood'))
3027 if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'):
3028 ifaceobjrunning.update_config('bridge-multicast-flood', v)
3029
3030 v = utils.get_onff_from_onezero(
3031 self.brctlcmd.get_bridgeport_attr(bridgename,
3032 ifaceobjrunning.name,
3033 'arp-nd-suppress'))
3034 # Display running 'arp-nd-suppress' only on vxlan ports
3035 # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes'
3036 # otherwise, display on all bridge-ports
3037
3038 bportifaceobj = ifaceobj_getfunc(ifaceobjrunning.name)[0]
3039 if (v and
3040 v != self.get_mod_subattr('bridge-arp-nd-suppress', 'default') and
3041 (not self.arp_nd_suppress_only_on_vxlan or
3042 (self.arp_nd_suppress_only_on_vxlan and
3043 bportifaceobj.link_kind & ifaceLinkKind.VXLAN))):
3044 ifaceobjrunning.update_config('bridge-arp-nd-suppress', v)
3045
3046 self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
3047
3048 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
3049 try:
3050 if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
3051 self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
3052 elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
3053 self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
3054 except Exception as e:
3055 raise Exception('%s: %s' % (ifaceobjrunning.name, str(e)))
3056
3057 def _query(self, ifaceobj, **kwargs):
3058 """ add default policy attributes supported by the module """
3059 if (not (ifaceobj.link_kind & ifaceLinkKind.BRIDGE) or
3060 ifaceobj.get_attr_value_first('bridge-stp')):
3061 return
3062 if self.default_stp_on:
3063 ifaceobj.update_config('bridge-stp', 'yes')
3064
3065 def _query_check_support_yesno_attrs(self, runningattrs, ifaceobj):
3066 for attrl in [['mcqifaddr', 'bridge-mcqifaddr'],
3067 ['mcquerier', 'bridge-mcquerier'],
3068 ['mcsnoop', 'bridge-mcsnoop']]:
3069 value = ifaceobj.get_attr_value_first(attrl[1])
3070 if value and not utils.is_binary_bool(value):
3071 if attrl[0] in runningattrs:
3072 bool = utils.get_boolean_from_string(runningattrs[attrl[0]])
3073 runningattrs[attrl[0]] = utils.get_yesno_boolean(bool)
3074
3075 self._query_check_mcrouter(ifaceobj, runningattrs)
3076 self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'portmcfl', ifaceobj.get_attr_value_first('bridge-portmcfl'))
3077 self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'learning', ifaceobj.get_attr_value_first('bridge-learning'))
3078 self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'unicast-flood', ifaceobj.get_attr_value_first('bridge-unicast-flood'))
3079 self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'multicast-flood', ifaceobj.get_attr_value_first('bridge-multicast-flood'))
3080 self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'arp-nd-suppress', ifaceobj.get_attr_value_first('bridge-arp-nd-suppress'))
3081
3082 def _query_check_mcrouter(self, ifaceobj, running_attrs):
3083 """
3084 bridge-mcrouter and bridge-portmcrouter supports: yes-no-0-1-2
3085 """
3086 if 'mcrouter' in running_attrs:
3087 value = ifaceobj.get_attr_value_first('bridge-mcrouter')
3088 if value:
3089 try:
3090 int(value)
3091 except:
3092 running_attrs['mcrouter'] = 'yes' if utils.get_boolean_from_string(running_attrs['mcrouter']) else 'no'
3093
3094 def _query_check_support_yesno_attr_port(self, runningattrs, ifaceobj, attr, attrval):
3095 if attrval:
3096 portlist = self.parse_port_list(ifaceobj.name, attrval)
3097 if portlist:
3098 to_convert = []
3099 for p in portlist:
3100 (port, val) = p.split('=')
3101 if not utils.is_binary_bool(val):
3102 to_convert.append(port)
3103 for port in to_convert:
3104 runningattrs['ports'][port][attr] = utils.get_yesno_boolean(
3105 utils.get_boolean_from_string(runningattrs['ports'][port][attr]))
3106
3107 _run_ops = {
3108 'pre-up': _up,
3109 'post-down': _down,
3110 'query-checkcurr': _query_check,
3111 'query-running': _query_running,
3112 'query': _query
3113 }
3114
3115 def get_ops(self):
3116 """ returns list of ops supported by this module """
3117 return self._run_ops.keys()
3118
3119 def _init_command_handlers(self):
3120 if not self.ipcmd:
3121 self.ipcmd = self.brctlcmd = LinkUtils()
3122
3123 def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
3124 """ run bridge configuration on the interface object passed as
3125 argument. Can create bridge interfaces if they dont exist already
3126
3127 Args:
3128 **ifaceobj** (object): iface object
3129
3130 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
3131 'query-running'
3132
3133 Kwargs:
3134 **query_ifaceobj** (object): query check ifaceobject. This is only
3135 valid when op is 'query-checkcurr'. It is an object same as
3136 ifaceobj, but contains running attribute values and its config
3137 status. The modules can use it to return queried running state
3138 of interfaces. status is success if the running state is same
3139 as user required state in ifaceobj. error otherwise.
3140 """
3141 op_handler = self._run_ops.get(operation)
3142 if not op_handler:
3143 return
3144 self._init_command_handlers()
3145
3146 if (not LinkUtils.bridge_utils_is_installed
3147 and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
3148 and LinkUtils.bridge_utils_missing_warning):
3149 self.logger.warning('%s: missing - bridge operation may not work as expected. '
3150 'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd)
3151 LinkUtils.bridge_utils_missing_warning = False
3152
3153 if operation == 'query-checkcurr':
3154 op_handler(self, ifaceobj, query_ifaceobj,
3155 ifaceobj_getfunc=ifaceobj_getfunc)
3156 else:
3157 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)