3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
8 from ifupdown
.iface
import *
9 from ifupdownaddons
.modulebase
import moduleBase
10 from ifupdownaddons
.bridgeutils
import brctl
11 from ifupdownaddons
.iproute2
import iproute2
16 class bridge(moduleBase
):
17 """ ifupdown2 addon module to configure linux bridges """
19 _modinfo
= { 'mhelp' : 'Bridge configuration module. Supports both ' +
20 'vlan aware and non vlan aware bridges. For the vlan ' +
21 'aware bridge, the port specific attributes must be ' +
22 'specified under the port. And for vlan unaware bridge ' +
23 'port specific attributes must be specified under the ' +
27 {'help' : 'vlan aware bridge. Setting this ' +
28 'attribute to yes enables vlan filtering' +
30 'example' : ['bridge-vlan-aware yes/no']},
32 {'help' : 'bridge ports',
34 'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
35 'bridge-ports glob swp1-3.100',
36 'bridge-ports regex (swp[1|2|3].100)']},
38 {'help': 'bridge-stp yes/no',
39 'example' : ['bridge-stp no'],
40 'validvals' : ['yes', 'on', 'off', 'no'],
43 {'help': 'bridge priority',
44 'example' : ['bridge-bridgeprio 32768'],
47 {'help': 'bridge ageing',
48 'example' : ['bridge-ageing 300'],
51 { 'help' : 'bridge forward delay',
52 'example' : ['bridge-fd 15'],
56 { 'help' : 'bridge garbage collection interval in secs',
57 'example' : ['bridge-gcint 4'],
60 { 'help' : 'bridge set hello time',
61 'example' : ['bridge-hello 2'],
64 { 'help' : 'bridge set maxage',
65 'example' : ['bridge-maxage 20'],
68 { 'help' : 'bridge set port path costs',
69 'example' : ['bridge-pathcosts swp1=100 swp2=100'],
72 { 'help' : 'bridge port prios',
73 'example' : ['bridge-portprios swp1=32 swp2=32'],
76 { 'help' : 'set multicast last member count',
77 'example' : ['bridge-mclmc 2'],
80 { 'help' : 'set multicast router',
82 'example' : ['bridge-mcrouter 1']},
84 { 'help' : 'set multicast snooping',
86 'example' : ['bridge-mcsnoop 1']},
88 { 'help' : 'set multicast startup query count',
90 'example' : ['bridge-mcsqc 2']},
92 { 'help' : 'set multicast query to use ifaddr',
94 'example' : ['bridge-mcqifaddr 0']},
96 { 'help' : 'set multicast querier',
98 'example' : ['bridge-mcquerier 0']},
100 { 'help' : 'set hash elasticity',
102 'example' : ['bridge-hashel 4096']},
104 { 'help' : 'set hash max',
106 'example' : ['bridge-hashmax 4096']},
108 { 'help' : 'set multicast last member interval (in secs)',
110 'example' : ['bridge-mclmi 1']},
112 { 'help' : 'set multicast membership interval (in secs)',
114 'example' : ['bridge-mcmi 260']},
116 { 'help' : 'set multicast querier interval (in secs)',
118 'example' : ['bridge-mcqpi 255']},
120 { 'help' : 'set multicast query interval (in secs)',
122 'example' : ['bridge-mcqi 125']},
124 { 'help' : 'set multicast query response interval (in secs)',
126 'example' : ['bridge-mcqri 10']},
128 { 'help' : 'set multicast startup query interval (in secs)',
130 'example' : ['bridge-mcsqi 31']},
132 { 'help' : 'set per VLAN v4 multicast querier source address',
134 'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
135 'bridge-portmcrouter' :
136 { 'help' : 'set port multicast routers',
138 'example' : ['under the bridge: bridge-portmcrouter swp1=1 swp2=1',
139 'under the port: bridge-portmcrouter 1']},
141 { 'help' : 'port multicast fast leave.',
143 'example' : ['under the bridge: bridge-portmcfl swp1=0 swp2=0',
144 'under the port: bridge-portmcfl 0']},
146 { 'help' : 'wait for a max of time secs for the' +
147 ' specified ports to become available,' +
148 'if no ports are specified then those' +
149 ' specified on bridge-ports will be' +
150 ' used here. Specifying no ports here ' +
151 'should not be used if we are using ' +
152 'regex or \"all\" on bridge_ports,' +
153 'as it wouldnt work.',
155 'example' : ['bridge-waitport 4 swp1 swp2']},
157 { 'help' : 'forces to time seconds the maximum time ' +
158 'that the Debian bridge setup scripts will ' +
159 'wait for the bridge ports to get to the ' +
160 'forwarding status, doesn\'t allow factional ' +
161 'part. If it is equal to 0 then no waiting' +
164 'example' : ['bridge-maxwait 3']},
166 { 'help' : 'bridge port vids. Can be specified ' +
167 'under the bridge or under the port. ' +
168 'If specified under the bridge the ports ' +
169 'inherit it unless overridden by a ' +
170 'bridge-vids attribuet under the port',
171 'example' : ['bridge-vids 4000',
172 'bridge-vids 2000 2200-3000']},
174 { 'help' : 'bridge port pvid. Must be specified under' +
176 'example' : ['bridge-pvid 1']},
178 { 'help' : 'bridge port access vlan. Must be ' +
179 'specified under the bridge port',
180 'example' : ['bridge-access 300']},
182 { 'help' : 'bridge vlans',
184 'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
185 'bridge-port-pvids' :
186 { 'help' : 'bridge port vlans',
188 'example' : ['bridge-port-pvids bond0=100 bond1=200']},
191 # declare some ifaceobj priv_flags.
192 # XXX: This assumes that the priv_flags is owned by this module
194 _BRIDGE_PORT_PROCESSED
= 0x1
196 def __init__(self
, *args
, **kargs
):
197 moduleBase
.__init
__(self
, *args
, **kargs
)
200 self
._running
_vidinfo
= {}
201 self
._running
_vidinfo
_valid
= False
202 self
._resv
_vlan
_range
= self
._get
_reserved
_vlan
_range
()
203 self
.logger
.debug('%s: using reserved vlan range %s'
204 %(self
.__class
__.__name
__, str(self
._resv
_vlan
_range
)))
206 def _is_bridge(self
, ifaceobj
):
207 if ifaceobj
.get_attr_value_first('bridge-ports'):
211 def _is_bridge_port(self
, ifaceobj
):
212 if self
.brctlcmd
.is_bridge_port(ifaceobj
.name
):
216 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
217 if not self
._is
_bridge
(ifaceobj
):
219 return self
.parse_port_list(ifaceobj
.get_attr_value_first(
220 'bridge-ports'), ifacenames_all
)
222 def get_dependent_ifacenames_running(self
, ifaceobj
):
223 self
._init
_command
_handlers
()
224 if not self
.brctlcmd
.bridge_exists(ifaceobj
.name
):
226 return self
.brctlcmd
.get_bridge_ports(ifaceobj
.name
)
228 def _get_bridge_port_list(self
, ifaceobj
):
230 # port list is also available in the previously
231 # parsed dependent list. Use that if available, instead
232 # of parsing port expr again
233 port_list
= ifaceobj
.lowerifaces
236 ports
= ifaceobj
.get_attr_value_first('bridge-ports')
238 return self
.parse_port_list(ports
)
242 def _process_bridge_waitport(self
, ifaceobj
, portlist
):
243 waitport_value
= ifaceobj
.get_attr_value_first('bridge-waitport')
244 if not waitport_value
: return
246 waitportvals
= re
.split(r
'[\s\t]\s*', waitport_value
, 1)
247 if not waitportvals
: return
249 waitporttime
= int(waitportvals
[0])
251 self
.log_warn('%s: invalid waitport value \'%s\''
252 %(ifaceobj
.name
, waitporttime
))
254 if waitporttime
<= 0: return
256 waitportlist
= self
.parse_port_list(waitportvals
[1])
257 except IndexError, e
:
258 # ignore error and use all bridge ports
259 waitportlist
= portlist
261 if not waitportlist
: return
262 self
.logger
.info('%s: waiting for ports %s to exist ...'
263 %(ifaceobj
.name
, str(waitportlist
)))
264 starttime
= time
.time()
265 while ((time
.time() - starttime
) < waitporttime
):
266 if all([False for p
in waitportlist
267 if not self
.ipcmd
.link_exists(p
)]):
271 self
.log_warn('%s: unable to process waitport: %s'
272 %(ifaceobj
.name
, str(e
)))
274 def _add_ports(self
, ifaceobj
):
275 bridgeports
= self
._get
_bridge
_port
_list
(ifaceobj
)
276 runningbridgeports
= []
278 self
._process
_bridge
_waitport
(ifaceobj
, bridgeports
)
279 # Delete active ports not in the new port list
280 if not self
.PERFMODE
:
281 runningbridgeports
= self
.brctlcmd
.get_bridge_ports(ifaceobj
.name
)
282 if runningbridgeports
:
283 [self
.ipcmd
.link_set(bport
, 'nomaster')
284 for bport
in runningbridgeports
285 if not bridgeports
or bport
not in bridgeports
]
287 runningbridgeports
= []
291 for bridgeport
in Set(bridgeports
).difference(Set(runningbridgeports
)):
293 if not self
.DRYRUN
and not self
.ipcmd
.link_exists(bridgeport
):
294 self
.log_warn('%s: bridge port %s does not exist'
295 %(ifaceobj
.name
, bridgeport
))
298 self
.ipcmd
.link_set(bridgeport
, 'master', ifaceobj
.name
)
299 self
.write_file('/proc/sys/net/ipv6/conf/%s' %bridgeport
+
300 '/disable_ipv6', '1')
301 self
.ipcmd
.addr_flush(bridgeport
)
303 self
.log_error(str(e
))
305 self
.log_error('bridge configuration failed (missing ports)')
307 def _process_bridge_maxwait(self
, ifaceobj
, portlist
):
308 maxwait
= ifaceobj
.get_attr_value_first('bridge-maxwait')
309 if not maxwait
: return
311 maxwait
= int(maxwait
)
313 self
.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj
.name
,
316 if not maxwait
: return
317 self
.logger
.info('%s: waiting for ports to go to fowarding state ..'
320 starttime
= time
.time()
321 while ((time
.time() - starttime
) < maxwait
):
322 if all([False for p
in portlist
323 if self
.read_file_oneline(
324 '/sys/class/net/%s/brif/%s/state'
325 %(ifaceobj
.name
, p
)) != '3']):
329 self
.log_warn('%s: unable to process maxwait: %s'
330 %(ifaceobj
.name
, str(e
)))
332 def _ints_to_ranges(self
, ints
):
333 for a
, b
in itertools
.groupby(enumerate(ints
), lambda (x
, y
): y
- x
):
335 yield b
[0][1], b
[-1][1]
337 def _ranges_to_ints(self
, rangelist
):
338 """ returns expanded list of integers given set of string ranges
339 example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
342 for part
in rangelist
:
344 a
, b
= part
.split('-')
345 a
, b
= int(a
), int(b
)
346 result
.extend(range(a
, b
+ 1))
352 def _diff_vids(self
, vids1
, vids2
):
356 vids1_ints
= self
._ranges
_to
_ints
(vids1
)
357 vids2_ints
= self
._ranges
_to
_ints
(vids2
)
358 vids1_diff
= Set(vids1_ints
).difference(vids2_ints
)
359 vids2_diff
= Set(vids2_ints
).difference(vids1_ints
)
361 vids_to_add
= ['%d' %start
if start
== end
else '%d-%d' %(start
, end
)
362 for start
, end
in self
._ints
_to
_ranges
(vids1_diff
)]
364 vids_to_del
= ['%d' %start
if start
== end
else '%d-%d' %(start
, end
)
365 for start
, end
in self
._ints
_to
_ranges
(vids2_diff
)]
366 return (vids_to_del
, vids_to_add
)
368 def _compare_vids(self
, vids1
, vids2
):
369 """ Returns true if the vids are same else return false """
371 vids1_ints
= self
._ranges
_to
_ints
(vids1
)
372 vids2_ints
= self
._ranges
_to
_ints
(vids2
)
373 if Set(vids1_ints
).symmetric_difference(vids2_ints
):
378 def _set_bridge_mcqv4src_compat(self
, ifaceobj
):
380 # Sets old style igmp querier
382 attrval
= ifaceobj
.get_attr_value_first('bridge-mcqv4src')
384 running_mcqv4src
= {}
385 if not self
.PERFMODE
:
386 running_mcqv4src
= self
.brctlcmd
.get_mcqv4src(ifaceobj
.name
)
388 srclist
= attrval
.split()
393 k_to_del
= Set(running_mcqv4src
.keys()).difference(mcqs
.keys())
395 self
.brctlcmd
.del_mcqv4src(ifaceobj
.name
, v
)
396 for v
in mcqs
.keys():
397 self
.brctlcmd
.set_mcqv4src(ifaceobj
.name
, v
, mcqs
[v
])
399 def _get_running_vidinfo(self
):
400 if self
._running
_vidinfo
_valid
:
401 return self
._running
_vidinfo
402 self
._running
_vidinfo
= {}
403 if not self
.PERFMODE
:
404 self
._running
_vidinfo
= self
.ipcmd
.bridge_port_vids_get_all()
405 self
._running
_vidinfo
_valid
= True
406 return self
._running
_vidinfo
408 def _flush_running_vidinfo(self
):
409 self
._running
_vidinfo
= {}
410 self
._running
_vidinfo
_valid
= False
412 def _set_bridge_vidinfo_compat(self
, ifaceobj
):
414 # Supports old style vlan vid info format
418 # Handle bridge vlan attrs
419 running_vidinfo
= self
._get
_running
_vidinfo
()
422 attrval
= ifaceobj
.get_attr_value_first('bridge-port-pvids')
424 portlist
= self
.parse_port_list(attrval
)
426 self
.log_warn('%s: could not parse \'%s %s\''
427 %(ifaceobj
.name
, attrname
, attrval
))
431 (port
, pvid
) = p
.split('=')
432 running_pvid
= running_vidinfo
.get(port
, {}).get('pvid')
434 if running_pvid
== pvid
:
437 self
.ipcmd
.bridge_port_pvid_del(port
, running_pvid
)
438 self
.ipcmd
.bridge_port_pvid_add(port
, pvid
)
440 self
.log_warn('%s: failed to set pvid `%s` (%s)'
441 %(ifaceobj
.name
, p
, str(e
)))
444 attrval
= ifaceobj
.get_attr_value_first('bridge-port-vids')
446 portlist
= self
.parse_port_list(attrval
)
448 self
.log_warn('%s: could not parse \'%s %s\''
449 %(ifaceobj
.name
, attrname
, attrval
))
453 (port
, val
) = p
.split('=')
454 vids
= val
.split(',')
455 if running_vidinfo
.get(port
):
456 (vids_to_del
, vids_to_add
) = \
457 self
._diff
_vids
(vids
,
458 running_vidinfo
.get(port
).get('vlan'))
460 self
.ipcmd
.bridge_port_vids_del(port
, vids_to_del
)
462 self
.ipcmd
.bridge_port_vids_add(port
, vids_to_add
)
464 self
.ipcmd
.bridge_port_vids_add(port
, vids
)
466 self
.log_warn('%s: failed to set vid `%s` (%s)'
467 %(ifaceobj
.name
, p
, str(e
)))
470 # XXX: Commenting out this code for now because it was decided
471 # that this is not needed
472 #attrval = ifaceobj.get_attr_value_first('bridge-vids')
474 # vids = re.split(r'[\s\t]\s*', attrval)
475 # if running_vidinfo.get(ifaceobj.name):
476 # (vids_to_del, vids_to_add) = \
477 # self._diff_vids(vids,
478 # running_vidinfo.get(ifaceobj.name).get('vlan'))
480 # self.ipcmd.bridge_vids_del(ifaceobj.name, vids_to_del)
482 # self.ipcmd.bridge_vids_add(ifaceobj.name, vids_to_add)
484 # self.ipcmd.bridge_vids_add(ifaceobj.name, vids)
486 # running_vids = running_vidinfo.get(ifaceobj.name)
488 # self.ipcmd.bridge_vids_del(ifaceobj.name, running_vids)
490 def _apply_bridge_settings(self
, ifaceobj
):
492 stp
= ifaceobj
.get_attr_value_first('bridge-stp')
494 self
.brctlcmd
.set_stp(ifaceobj
.name
, stp
)
495 # Use the brctlcmd bulk set method: first build a dictionary
497 bridgeattrs
= { k
:v
for k
,v
in
499 ifaceobj
.get_attr_value_first('bridge-ageing'),
501 ifaceobj
.get_attr_value_first(
502 'bridge-bridgeprio'),
504 ifaceobj
.get_attr_value_first('bridge-fd'),
506 ifaceobj
.get_attr_value_first('bridge-gcint'),
508 ifaceobj
.get_attr_value_first('bridge-hello'),
510 ifaceobj
.get_attr_value_first('bridge-maxage'),
512 ifaceobj
.get_attr_value_first('bridge-mclmc'),
514 ifaceobj
.get_attr_value_first(
517 ifaceobj
.get_attr_value_first('bridge-mcsnoop'),
519 ifaceobj
.get_attr_value_first('bridge-mcsqc'),
521 ifaceobj
.get_attr_value_first(
524 ifaceobj
.get_attr_value_first(
527 ifaceobj
.get_attr_value_first('bridge-hashel'),
529 ifaceobj
.get_attr_value_first('bridge-hashmax'),
531 ifaceobj
.get_attr_value_first('bridge-mclmi'),
533 ifaceobj
.get_attr_value_first('bridge-mcmi'),
535 ifaceobj
.get_attr_value_first('bridge-mcqpi'),
537 ifaceobj
.get_attr_value_first('bridge-mcqi'),
539 ifaceobj
.get_attr_value_first('bridge-mcqri'),
541 ifaceobj
.get_attr_value_first('bridge-mcsqi')
545 self
.brctlcmd
.set_bridge_attrs(ifaceobj
.name
, bridgeattrs
)
547 for attrname
, dstattrname
in {'bridge-pathcosts' : 'pathcost',
548 'bridge-portprios' : 'portprio',
549 'bridge-portmcrouter' : 'portmcrouter',
550 'bridge-portmcfl' : 'portmcfl'}.items():
551 attrval
= ifaceobj
.get_attr_value_first(attrname
)
554 portlist
= self
.parse_port_list(attrval
)
556 self
.log_warn('%s: could not parse \'%s %s\''
557 %(ifaceobj
.name
, attrname
, attrval
))
561 (port
, val
) = p
.split('=')
562 if not portattrs
.get(port
):
564 portattrs
[port
].update({dstattrname
: val
})
566 self
.log_warn('%s: could not parse %s (%s)'
567 %(ifaceobj
.name
, attrname
, str(e
)))
568 for port
, attrdict
in portattrs
.iteritems():
569 self
.brctlcmd
.set_bridgeport_attrs(ifaceobj
.name
, port
,
571 self
._set
_bridge
_vidinfo
_compat
(ifaceobj
)
573 self
._set
_bridge
_mcqv
4src
_compat
(ifaceobj
)
575 self
._process
_bridge
_maxwait
(ifaceobj
,
576 self
._get
_bridge
_port
_list
(ifaceobj
))
578 self
.log_warn(str(e
))
580 def _apply_bridge_vids(self
, bportifaceobj
, vids
, running_vids
, isbridge
):
583 (vids_to_del
, vids_to_add
) = \
584 self
._diff
_vids
(vids
, running_vids
)
586 self
.ipcmd
.bridge_vids_del(bportifaceobj
.name
,
587 vids_to_del
, isbridge
)
589 self
.ipcmd
.bridge_vids_add(bportifaceobj
.name
,
590 vids_to_add
, isbridge
)
592 self
.ipcmd
.bridge_vids_add(bportifaceobj
.name
, vids
, isbridge
)
594 self
.log_warn('%s: failed to set vid `%s` (%s)'
595 %(bportifaceobj
.name
, str(vids
), str(e
)))
597 def _apply_bridge_port_pvids(self
, bportifaceobj
, pvid
, running_pvid
):
601 if running_pvid
!= pvid
:
602 self
.ipcmd
.bridge_port_pvid_del(bportifaceobj
.name
,
604 self
.ipcmd
.bridge_port_pvid_add(bportifaceobj
.name
, pvid
)
606 self
.ipcmd
.bridge_port_pvid_add(bportifaceobj
.name
, pvid
)
608 self
.log_warn('%s: failed to set pvid `%s` (%s)'
609 %(bportifaceobj
.name
, pvid
, str(e
)))
611 def _apply_bridge_vlan_aware_port_settings_all(self
, bportifaceobj
,
614 running_vidinfo
= self
._get
_running
_vidinfo
()
617 bport_access
= bportifaceobj
.get_attr_value_first('bridge-access')
619 vids
= re
.split(r
'[\s\t]\s*', bport_access
)
622 bport_vids
= bportifaceobj
.get_attr_value_first('bridge-vids')
624 vids
= re
.split(r
'[\s\t,]\s*', bport_vids
)
626 bport_pvids
= bportifaceobj
.get_attr_value_first('bridge-pvid')
628 pvids
= re
.split(r
'[\s\t]\s*', bport_pvids
)
631 self
._apply
_bridge
_port
_pvids
(bportifaceobj
, pvids
[0],
632 running_vidinfo
.get(bportifaceobj
.name
, {}).get('pvid'))
634 self
._apply
_bridge
_port
_pvids
(bportifaceobj
,
635 bridge_pvid
, running_vidinfo
.get(bportifaceobj
.name
,
637 # XXX: default pvid is already one
639 # self._apply_bridge_port_pvids(bportifaceobj,
640 # '1', running_vidinfo.get(bportifaceobj.name,
644 self
._apply
_bridge
_vids
(bportifaceobj
, vids
,
645 running_vidinfo
.get(bportifaceobj
.name
,
646 {}).get('vlan'), False)
648 self
._apply
_bridge
_vids
(bportifaceobj
,
649 bridge_vids
, running_vidinfo
.get(
650 bportifaceobj
.name
, {}).get('vlan'), False)
653 def _apply_bridge_port_settings(self
, bportifaceobj
, bridgename
=None,
654 bridgeifaceobj
=None):
655 if not bridgename
and bridgeifaceobj
:
656 bridgename
= bridgeifaceobj
.name
657 # Set other stp and igmp attributes
659 for attrname
, dstattrname
in {
660 'bridge-pathcosts' : 'pathcost',
661 'bridge-portprios' : 'portprio',
662 'bridge-portmcrouter' : 'portmcrouter',
663 'bridge-portmcfl' : 'portmcfl'}.items():
664 attrval
= bportifaceobj
.get_attr_value_first(attrname
)
666 # Check if bridge has that attribute
668 # attrval = bridgeifaceobj.get_attr_value_first(attrname)
673 portattrs
[dstattrname
] = attrval
675 self
.brctlcmd
.set_bridgeport_attrs(bridgename
,
676 bportifaceobj
.name
, portattrs
)
678 self
.log_warn(str(e
))
680 def _apply_bridge_port_settings_all(self
, ifaceobj
,
681 ifaceobj_getfunc
=None):
682 bridge_vlan_aware
= ifaceobj
.get_attr_value_first(
684 if bridge_vlan_aware
and bridge_vlan_aware
== 'yes':
685 bridge_vlan_aware
= True
687 bridge_vlan_aware
= False
689 if (ifaceobj
.get_attr_value_first('bridge-port-vids') and
690 ifaceobj
.get_attr_value_first('bridge-port-pvids')):
691 # Old style bridge port vid info
692 # skip new style setting on ports
694 self
.logger
.info('%s: applying bridge configuration '
695 %ifaceobj
.name
+ 'specific to ports')
697 bridge_vids
= ifaceobj
.get_attr_value_first('bridge-vids')
699 bridge_vids
= re
.split(r
'[\s\t,]\s*', bridge_vids
)
703 bridge_pvid
= ifaceobj
.get_attr_value_first('bridge-pvid')
705 bridge_pvid
= re
.split(r
'[\s\t]\s*', bridge_pvid
)
709 bridgeports
= self
._get
_bridge
_port
_list
(ifaceobj
)
710 for bport
in bridgeports
:
711 # Use the brctlcmd bulk set method: first build a dictionary
713 if not self
.ipcmd
.bridge_port_exists(ifaceobj
.name
, bport
):
714 self
.logger
.info('%s: skipping bridge config' %ifaceobj
.name
+
715 ' for port %s (missing port)' %bport
)
717 self
.logger
.info('%s: processing bridge config for port %s'
718 %(ifaceobj
.name
, bport
))
719 bportifaceobjlist
= ifaceobj_getfunc(bport
)
720 if not bportifaceobjlist
:
722 for bportifaceobj
in bportifaceobjlist
:
723 # Dont process bridge port if it already has been processed
724 if bportifaceobj
.priv_flags
& self
._BRIDGE
_PORT
_PROCESSED
:
727 # Add attributes specific to the vlan aware bridge
728 if bridge_vlan_aware
:
729 self
._apply
_bridge
_vlan
_aware
_port
_settings
_all
(
730 bportifaceobj
, bridge_vids
, bridge_pvid
)
731 self
._apply
_bridge
_port
_settings
(bportifaceobj
,
732 bridgeifaceobj
=ifaceobj
)
734 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
735 # Check if bridge port
736 bridgename
= self
.ipcmd
.bridge_port_get_bridge_name(ifaceobj
.name
)
738 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
739 bridge_vids
= self
._get
_bridge
_vids
(bridgename
,
741 bridge_pvid
= self
._get
_bridge
_pvid
(bridgename
,
743 self
._apply
_bridge
_vlan
_aware
_port
_settings
_all
(ifaceobj
,
746 self
._apply
_bridge
_port
_settings
(ifaceobj
, bridgename
=bridgename
)
747 ifaceobj
.priv_flags |
= self
._BRIDGE
_PORT
_PROCESSED
749 if not self
._is
_bridge
(ifaceobj
):
754 self
.ipcmd
.batch_start()
755 if not self
.PERFMODE
:
756 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
757 self
.ipcmd
.link_create(ifaceobj
.name
, 'bridge')
759 self
.ipcmd
.link_create(ifaceobj
.name
, 'bridge')
761 self
._add
_ports
(ifaceobj
)
767 self
.ipcmd
.batch_commit()
768 self
._apply
_bridge
_settings
(ifaceobj
)
769 self
._apply
_bridge
_port
_settings
_all
(ifaceobj
,
770 ifaceobj_getfunc
=ifaceobj_getfunc
)
771 #self._flush_running_vidinfo()
773 self
.log_error(str(e
))
775 raise Exception(porterrstr
)
777 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
779 if ifaceobj
.get_attr_value_first('bridge-ports'):
780 ports
= self
.brctlcmd
.get_bridge_ports(ifaceobj
.name
)
783 proc_file
= ('/proc/sys/net/ipv6/conf/%s' %p
+
785 self
.write_file(proc_file
, '0')
786 self
.brctlcmd
.delete_bridge(ifaceobj
.name
)
788 self
.log_error(str(e
))
790 def _query_running_vidinfo_compat(self
, ifaceobjrunning
, ports
):
792 running_vidinfo
= self
._get
_running
_vidinfo
()
794 running_bridge_port_vids
= ''
797 running_vids
= running_vidinfo
.get(p
, {}).get('vlan')
799 running_bridge_port_vids
+= ' %s=%s' %(p
,
800 ','.join(running_vids
))
803 running_attrs
['bridge-port-vids'] = running_bridge_port_vids
805 running_bridge_port_pvids
= ''
808 running_pvids
= running_vidinfo
.get(p
, {}).get('pvid')
810 running_bridge_port_pvids
+= ' %s=%s' %(p
,
814 running_attrs
['bridge-port-pvids'] = running_bridge_port_pvids
816 running_bridge_vids
= running_vidinfo
.get(ifaceobjrunning
.name
,
818 if running_bridge_vids
:
819 running_attrs
['bridge-vids'] = ','.join(running_bridge_vids
)
822 def _query_running_vidinfo(self
, ifaceobjrunning
):
824 running_vidinfo
= self
._get
_running
_vidinfo
()
825 running_bridge_vids
= running_vidinfo
.get(ifaceobjrunning
.name
,
827 if running_bridge_vids
:
828 running_attrs
['bridge-vids'] = ','.join(running_bridge_vids
)
831 def _query_running_mcqv4src(self
, ifaceobjrunning
):
832 running_mcqv4src
= self
.brctlcmd
.get_mcqv4src(ifaceobjrunning
.name
)
833 mcqs
= ['%s=%s' %(v
, i
) for v
, i
in running_mcqv4src
.items()]
838 def _query_running_attrs(self
, ifaceobjrunning
, bridge_vlan_aware
=False):
842 skip_kernel_stp_attrs
= 0
844 if self
.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
847 tmpbridgeattrdict
= self
.brctlcmd
.get_bridge_attrs(ifaceobjrunning
.name
)
848 if not tmpbridgeattrdict
:
849 self
.logger
.warn('%s: unable to get bridge attrs'
850 %ifaceobjrunning
.name
)
851 return bridgeattrdict
853 # Fill bridge_ports and bridge stp attributes first
854 ports
= tmpbridgeattrdict
.get('ports')
856 bridgeattrdict
['bridge-ports'] = [' '.join(ports
.keys())]
857 stp
= tmpbridgeattrdict
.get('stp', 'no')
858 if stp
!= self
.get_mod_subattr('bridge-stp', 'default'):
859 bridgeattrdict
['bridge-stp'] = [stp
]
861 if stp
== 'yes' and userspace_stp
:
862 skip_kernel_stp_attrs
= 1
864 # pick all other attributes
865 for k
,v
in tmpbridgeattrdict
.items():
868 if k
== 'ports' or k
== 'stp':
871 if skip_kernel_stp_attrs
and k
[:2] != 'mc':
872 # only include igmp attributes if kernel stp is off
874 attrname
= 'bridge-' + k
875 if v
!= self
.get_mod_subattr(attrname
, 'default'):
876 bridgeattrdict
[attrname
] = [v
]
878 if bridge_vlan_aware
:
879 bridgevidinfo
= self
._query
_running
_vidinfo
(ifaceobjrunning
)
881 bridgevidinfo
= self
._query
_running
_vidinfo
_compat
(ifaceobjrunning
,
884 bridgeattrdict
.update({k
: [v
] for k
, v
in bridgevidinfo
.items()
887 mcq
= self
._query
_running
_mcqv
4src
(ifaceobjrunning
)
889 bridgeattrdict
['bridge-mcqv4src'] = [mcq
]
891 if skip_kernel_stp_attrs
:
892 return bridgeattrdict
895 portconfig
= {'bridge-pathcosts' : '',
896 'bridge-portprios' : ''}
897 for p
, v
in ports
.items():
898 v
= self
.brctlcmd
.get_pathcost(ifaceobjrunning
.name
, p
)
899 if v
and v
!= self
.get_mod_subattr('bridge-pathcosts',
901 portconfig
['bridge-pathcosts'] += ' %s=%s' %(p
, v
)
903 v
= self
.brctlcmd
.get_portprio(ifaceobjrunning
.name
, p
)
904 if v
and v
!= self
.get_mod_subattr('bridge-portprios',
906 portconfig
['bridge-portprios'] += ' %s=%s' %(p
, v
)
908 bridgeattrdict
.update({k
: [v
] for k
, v
in portconfig
.items()
911 return bridgeattrdict
913 def _query_check_mcqv4src(self
, ifaceobj
, ifaceobjcurr
):
914 running_mcqs
= self
._query
_running
_mcqv
4src
(ifaceobj
)
915 attrval
= ifaceobj
.get_attr_value_first('bridge-mcqv4src')
917 mcqs
= attrval
.split()
919 mcqsout
= ' '.join(mcqs
)
920 ifaceobjcurr
.update_config_with_status('bridge-mcqv4src',
921 running_mcqs
, 1 if running_mcqs
!= mcqsout
else 0)
923 def _query_check_bridge_vidinfo(self
, ifaceobj
, ifaceobjcurr
):
925 running_vidinfo
= self
._get
_running
_vidinfo
()
926 attrval
= ifaceobj
.get_attr_value_first('bridge-port-vids')
928 running_bridge_port_vids
= ''
929 portlist
= self
.parse_port_list(attrval
)
931 self
.log_warn('%s: could not parse \'%s %s\''
932 %(ifaceobj
.name
, attrname
, attrval
))
937 (port
, val
) = p
.split('=')
938 vids
= val
.split(',')
939 running_vids
= running_vidinfo
.get(port
, {}).get('vlan')
941 if not self
._compare
_vids
(vids
, running_vids
):
943 running_bridge_port_vids
+= ' %s=%s' %(port
,
944 ','.join(running_vids
))
946 running_bridge_port_vids
+= ' %s' %p
950 self
.log_warn('%s: failure checking vid %s (%s)'
951 %(ifaceobj
.name
, p
, str(e
)))
953 ifaceobjcurr
.update_config_with_status('bridge-port-vids',
954 running_bridge_port_vids
, 1)
956 ifaceobjcurr
.update_config_with_status('bridge-port-vids',
959 attrval
= ifaceobj
.get_attr_value_first('bridge-port-pvids')
961 portlist
= self
.parse_port_list(attrval
)
963 self
.log_warn('%s: could not parse \'%s %s\''
964 %(ifaceobj
.name
, attrname
, attrval
))
966 running_bridge_port_pvids
= ''
970 (port
, pvid
) = p
.split('=')
971 running_pvid
= running_vidinfo
.get(port
, {}).get('pvid')
972 if running_pvid
and running_pvid
== pvid
:
973 running_bridge_port_pvids
+= ' %s' %p
976 running_bridge_port_pvids
+= ' %s=%s' %(port
,
979 self
.log_warn('%s: failure checking pvid %s (%s)'
980 %(ifaceobj
.name
, pvid
, str(e
)))
982 ifaceobjcurr
.update_config_with_status('bridge-port-pvids',
983 running_bridge_port_pvids
, 1)
985 ifaceobjcurr
.update_config_with_status('bridge-port-pvids',
986 running_bridge_port_pvids
, 0)
988 # XXX: No need to check for bridge-vids on the bridge
989 # This is used by the ports. The vids on the bridge
990 # come from the vlan interfaces on the bridge.
992 attrval
= ifaceobj
.get_attr_value_first('bridge-vids')
994 # vids = re.split(r'[\s\t]\s*', attrval)
995 # running_vids = running_vidinfo.get(ifaceobj.name, {}).get('vlan')
997 # if self._compare_vids(vids, running_vids):
998 # ifaceobjcurr.update_config_with_status('bridge-vids',
1001 # ifaceobjcurr.update_config_with_status('bridge-vids',
1002 # ','.join(running_vids), 1)
1004 # ifaceobjcurr.update_config_with_status('bridge-vids', attrval,
1007 ifaceobjcurr
.update_config_with_status('bridge-vids', attrval
, -1)
1009 def _query_check_bridge(self
, ifaceobj
, ifaceobjcurr
,
1010 ifaceobj_getfunc
=None):
1011 if not self
._is
_bridge
(ifaceobj
):
1013 if not self
.brctlcmd
.bridge_exists(ifaceobj
.name
):
1014 self
.logger
.info('%s: bridge: does not exist' %(ifaceobj
.name
))
1015 ifaceobjcurr
.status
= ifaceStatus
.NOTFOUND
1018 ifaceattrs
= self
.dict_key_subset(ifaceobj
.config
,
1019 self
.get_mod_attrs())
1023 runningattrs
= self
.brctlcmd
.get_bridge_attrs(ifaceobj
.name
)
1024 if not runningattrs
:
1025 self
.logger
.debug('%s: bridge: unable to get bridge attrs'
1028 except Exception, e
:
1029 self
.logger
.warn(str(e
))
1031 filterattrs
= ['bridge-vids', 'bridge-port-vids',
1032 'bridge-port-pvids']
1033 for k
in Set(ifaceattrs
).difference(filterattrs
):
1034 # get the corresponding ifaceobj attr
1035 v
= ifaceobj
.get_attr_value_first(k
)
1038 rv
= runningattrs
.get(k
[7:])
1039 if k
== 'bridge-mcqv4src':
1041 if k
== 'bridge-vlan-aware' and v
== 'yes':
1042 if self
.ipcmd
.bridge_is_vlan_aware(ifaceobj
.name
):
1043 ifaceobjcurr
.update_config_with_status('bridge-vlan-aware',
1046 ifaceobjcurr
.update_config_with_status('bridge-vlan-aware',
1048 elif k
== 'bridge-stp':
1049 # special case stp compare because it may
1050 # contain more than one valid values
1051 stp_on_vals
= ['on', 'yes']
1052 stp_off_vals
= ['off']
1053 if ((v
in stp_on_vals
and rv
in stp_on_vals
) or
1054 (v
in stp_off_vals
and rv
in stp_off_vals
)):
1055 ifaceobjcurr
.update_config_with_status('bridge-stp',
1058 ifaceobjcurr
.update_config_with_status('bridge-stp',
1060 elif k
== 'bridge-ports':
1061 # special case ports because it can contain regex or glob
1062 running_port_list
= rv
.keys() if rv
else []
1063 bridge_port_list
= self
._get
_bridge
_port
_list
(ifaceobj
)
1064 if not running_port_list
and not bridge_port_list
:
1067 if running_port_list
and bridge_port_list
:
1068 difference
= set(running_port_list
1069 ).symmetric_difference(bridge_port_list
)
1072 ifaceobjcurr
.update_config_with_status('bridge-ports',
1073 ' '.join(running_port_list
)
1074 if running_port_list
else '', portliststatus
)
1075 elif (k
== 'bridge-pathcosts' or
1076 k
== 'bridge-portprios' or k
== 'bridge-portmcrouter'
1077 or k
== 'bridge-portmcfl'):
1078 brctlcmdattrname
= k
[11:].rstrip('s')
1079 # for port attributes, the attributes are in a list
1080 # <portname>=<portattrvalue>
1083 vlist
= self
.parse_port_list(v
)
1086 for vlistitem
in vlist
:
1088 (p
, v
) = vlistitem
.split('=')
1089 currv
= self
.brctlcmd
.get_bridgeport_attr(
1093 currstr
+= ' %s=%s' %(p
, currv
)
1095 currstr
+= ' %s=%s' %(p
, 'None')
1098 except Exception, e
:
1099 self
.log_warn(str(e
))
1101 ifaceobjcurr
.update_config_with_status(k
, currstr
, status
)
1103 ifaceobjcurr
.update_config_with_status(k
, 'notfound', 1)
1106 ifaceobjcurr
.update_config_with_status(k
, rv
, 1)
1108 ifaceobjcurr
.update_config_with_status(k
, rv
, 0)
1110 self
._query
_check
_bridge
_vidinfo
(ifaceobj
, ifaceobjcurr
)
1112 self
._query
_check
_mcqv
4src
(ifaceobj
, ifaceobjcurr
)
1114 def _get_bridge_vids(self
, bridgename
, ifaceobj_getfunc
):
1115 ifaceobjs
= ifaceobj_getfunc(bridgename
)
1116 for ifaceobj
in ifaceobjs
:
1117 vids
= ifaceobj
.get_attr_value_first('bridge-vids')
1118 if vids
: return re
.split(r
'[\s\t,]\s*', vids
)
1121 def _get_bridge_pvid(self
, bridgename
, ifaceobj_getfunc
):
1122 ifaceobjs
= ifaceobj_getfunc(bridgename
)
1124 for ifaceobj
in ifaceobjs
:
1125 pvid
= ifaceobj
.get_attr_value_first('bridge-pvid')
1128 def _get_bridge_name(self
, ifaceobj
):
1129 return self
.ipcmd
.bridge_port_get_bridge_name(ifaceobj
.name
)
1131 def _query_check_bridge_port_vidinfo(self
, ifaceobj
, ifaceobjcurr
,
1132 ifaceobj_getfunc
, bridgename
):
1133 running_vidinfo
= self
._get
_running
_vidinfo
()
1135 attr_name
= 'bridge-access'
1136 vids
= ifaceobj
.get_attr_value_first(attr_name
)
1138 running_pvids
= running_vidinfo
.get(ifaceobj
.name
,
1140 running_vids
= running_vidinfo
.get(ifaceobj
.name
,
1142 if (not running_pvids
or running_pvids
!= vids
or
1144 ifaceobjcurr
.update_config_with_status(attr_name
,
1147 ifaceobjcurr
.update_config_with_status(attr_name
, vids
, 0)
1150 attr_name
= 'bridge-vids'
1151 vids
= ifaceobj
.get_attr_value_first(attr_name
)
1153 vids
= re
.split(r
'[\s\t]\s*', vids
)
1154 running_vids
= running_vidinfo
.get(ifaceobj
.name
,
1156 if not running_vids
or not self
._compare
_vids
(vids
, running_vids
):
1157 ifaceobjcurr
.update_config_with_status(attr_name
,
1158 ' '.join(running_vids
), 1)
1160 ifaceobjcurr
.update_config_with_status(attr_name
,
1161 ' '.join(running_vids
), 0)
1163 # check if it matches the bridge vids
1164 bridge_vids
= self
._get
_bridge
_vids
(bridgename
, ifaceobj_getfunc
)
1165 running_vids
= running_vidinfo
.get(ifaceobj
.name
,
1167 if (bridge_vids
and (not running_vids
or
1168 not self
._compare
_vids
(bridge_vids
, running_vids
))):
1169 ifaceobjcurr
.status
= ifaceStatus
.ERROR
1170 ifaceobjcurr
.status_str
= 'bridge vid error'
1172 running_pvid
= running_vidinfo
.get(ifaceobj
.name
,
1174 attr_name
= 'bridge-pvid'
1175 pvid
= ifaceobj
.get_attr_value_first(attr_name
)
1177 if running_pvid
and running_pvid
== pvid
:
1178 ifaceobjcurr
.update_config_with_status(attr_name
,
1181 ifaceobjcurr
.update_config_with_status(attr_name
,
1183 elif not running_pvid
or running_pvid
!= '1':
1184 ifaceobjcurr
.status
= ifaceStatus
.ERROR
1185 ifaceobjcurr
.status_str
= 'bridge pvid error'
1187 def _query_check_bridge_port(self
, ifaceobj
, ifaceobjcurr
,
1189 if not self
._is
_bridge
_port
(ifaceobj
):
1190 # Mark all bridge attributes as failed
1191 ifaceobj
.check_n_update_config_with_status_many(
1192 ['bridge-vids', 'bridge-pvid', 'bridge-access',
1193 'bridge-pathcosts', 'bridge-portprios',
1194 'bridge-portmcrouter',
1195 'bridge-portmcfl'], 0)
1197 bridgename
= self
._get
_bridge
_name
(ifaceobj
)
1199 self
.logger
.warn('%s: unable to determine bridge name'
1203 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
1204 self
._query
_check
_bridge
_port
_vidinfo
(ifaceobj
, ifaceobjcurr
,
1207 for attr
, dstattr
in {'bridge-pathcosts' : 'pathcost',
1208 'bridge-portprios' : 'priority',
1209 'bridge-portmcrouter' : 'mcrouter',
1210 'bridge-portmcfl' : 'mcfl' }.items():
1211 attrval
= ifaceobj
.get_attr_value_first(attr
)
1216 running_attrval
= self
.brctlcmd
.get_bridgeport_attr(
1217 bridgename
, ifaceobj
.name
, dstattr
)
1218 if running_attrval
!= attrval
:
1219 ifaceobjcurr
.update_config_with_status(attr
,
1222 ifaceobjcurr
.update_config_with_status(attr
,
1224 except Exception, e
:
1225 self
.log_warn('%s: %s' %(ifaceobj
.name
, str(e
)))
1227 def _query_check(self
, ifaceobj
, ifaceobjcurr
, ifaceobj_getfunc
=None):
1228 if self
._is
_bridge
(ifaceobj
):
1229 self
._query
_check
_bridge
(ifaceobj
, ifaceobjcurr
)
1231 self
._query
_check
_bridge
_port
(ifaceobj
, ifaceobjcurr
,
1234 def _query_running_bridge(self
, ifaceobjrunning
):
1235 if self
.ipcmd
.bridge_is_vlan_aware(ifaceobjrunning
.name
):
1236 ifaceobjrunning
.update_config('bridge-vlan-aware', 'yes')
1237 ifaceobjrunning
.update_config_dict(self
._query
_running
_attrs
(
1239 bridge_vlan_aware
=True))
1241 ifaceobjrunning
.update_config_dict(self
._query
_running
_attrs
(
1244 def _query_running_bridge_port_attrs(self
, ifaceobjrunning
, bridgename
):
1245 if self
.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
1248 v
= self
.brctlcmd
.get_pathcost(bridgename
, ifaceobjrunning
.name
)
1249 if v
and v
!= self
.get_mod_subattr('bridge-pathcosts', 'default'):
1250 ifaceobjrunning
.update_config('bridge-pathcosts', v
)
1252 v
= self
.brctlcmd
.get_pathcost(bridgename
, ifaceobjrunning
.name
)
1253 if v
and v
!= self
.get_mod_subattr('bridge-portprios', 'default'):
1254 ifaceobjrunning
.update_config('bridge-portprios', v
)
1256 def _query_running_bridge_port(self
, ifaceobjrunning
,
1257 ifaceobj_getfunc
=None):
1258 bridgename
= self
.ipcmd
.bridge_port_get_bridge_name(
1259 ifaceobjrunning
.name
)
1261 self
.logger
.warn('%s: unable to find bridgename'
1262 %ifaceobjrunning
.name
)
1264 if not self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
1267 running_vidinfo
= self
._get
_running
_vidinfo
()
1269 bridge_vids
= running_vidinfo
.get(bridgename
, {}).get('vlan')
1271 bridge_port_vids
= running_vidinfo
.get(ifaceobjrunning
.name
,
1273 bridge_port_pvid
= running_vidinfo
.get(ifaceobjrunning
.name
,
1276 if not bridge_port_vids
and bridge_port_pvid
:
1277 # must be an access port
1278 ifaceobjrunning
.update_config('bridge-access',
1281 if bridge_port_vids
:
1282 if bridge_vids
and bridge_port_vids
!= bridge_vids
:
1283 ifaceobjrunning
.update_config('bridge-vids',
1284 ' '.join(bridge_port_vids
))
1286 if bridge_port_pvid
and bridge_port_pvid
!= '1':
1287 ifaceobjrunning
.update_config('bridge-pvid',
1290 self
._query
_running
_bridge
_port
_attrs
(ifaceobjrunning
, bridgename
)
1293 def _query_running(self
, ifaceobjrunning
, **extra_args
):
1294 if self
.brctlcmd
.bridge_exists(ifaceobjrunning
.name
):
1295 self
._query
_running
_bridge
(ifaceobjrunning
)
1296 elif self
.brctlcmd
.is_bridge_port(ifaceobjrunning
.name
):
1297 self
._query
_running
_bridge
_port
(ifaceobjrunning
)
1299 _run_ops
= {'pre-up' : _up
,
1300 'post-down' : _down
,
1301 'query-checkcurr' : _query_check
,
1302 'query-running' : _query_running
}
1305 """ returns list of ops supported by this module """
1306 return self
._run
_ops
.keys()
1308 def _init_command_handlers(self
):
1309 flags
= self
.get_flags()
1311 self
.ipcmd
= iproute2(**flags
)
1312 if not self
.brctlcmd
:
1313 self
.brctlcmd
= brctl(**flags
)
1315 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1316 ifaceobj_getfunc
=None):
1317 """ run bridge configuration on the interface object passed as
1318 argument. Can create bridge interfaces if they dont exist already
1321 **ifaceobj** (object): iface object
1323 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1327 **query_ifaceobj** (object): query check ifaceobject. This is only
1328 valid when op is 'query-checkcurr'. It is an object same as
1329 ifaceobj, but contains running attribute values and its config
1330 status. The modules can use it to return queried running state
1331 of interfaces. status is success if the running state is same
1332 as user required state in ifaceobj. error otherwise.
1334 op_handler
= self
._run
_ops
.get(operation
)
1337 self
._init
_command
_handlers
()
1338 self
._flush
_running
_vidinfo
()
1339 if operation
== 'query-checkcurr':
1340 op_handler(self
, ifaceobj
, query_ifaceobj
,
1341 ifaceobj_getfunc
=ifaceobj_getfunc
)
1343 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)