3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 from ifupdown2
.lib
.addon
import Addon
12 from ifupdown2
.ifupdown
.iface
import *
13 from ifupdown2
.ifupdown
.utils
import utils
15 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
16 import ifupdown2
.ifupdown
.policymanager
as policymanager
18 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
19 from ifupdown2
.ifupdownaddons
.mstpctlutil
import mstpctlutil
20 from ifupdown2
.ifupdownaddons
.systemutils
import systemUtils
21 from ifupdown2
.ifupdown
.exceptions
import moduleNotSupported
23 from lib
.addon
import Addon
25 from ifupdown
.iface
import *
26 from ifupdown
.utils
import utils
28 import ifupdown
.ifupdownflags
as ifupdownflags
29 import ifupdown
.policymanager
as policymanager
31 from ifupdownaddons
.modulebase
import moduleBase
32 from ifupdownaddons
.mstpctlutil
import mstpctlutil
33 from ifupdownaddons
.systemutils
import systemUtils
34 from ifupdown
.exceptions
import moduleNotSupported
40 class mstpctl(Addon
, moduleBase
):
41 """ ifupdown2 addon module to configure mstp attributes """
44 "mhelp": "mstp configuration module for bridges",
50 "new-attribute": "bridge-ports"
53 "help": "bridge stp yes/no",
54 "validvals": ["yes", "no", "on", "off"],
58 "new-attribute": "bridge-stp"
61 "help": "tree priority",
64 "0", "4096", "8192", "12288", "16384",
65 "20480", "24576", "28672", "32768",
66 "36864", "40960", "45056", "49152",
67 "53248", "57344", "61440"
70 "example": ["mstpctl-treeprio 32768"]
73 "help": "ageing time",
74 "validrange": ["0", "4096"],
77 "jsonAttr": "ageingTime",
78 "example": ["mstpctl-ageing 300"]
81 "help": "max message age",
82 "validrange": ["0", "255"],
84 "jsonAttr": "bridgeMaxAge",
86 "example": ["mstpctl-maxage 20"]
89 "help": "set forwarding delay",
90 "validrange": ["0", "255"],
92 "jsonAttr": "bridgeFwdDelay",
94 "example": ["mstpctl-fdelay 15"]
97 "help": "bridge max hops",
98 "validrange": ["0", "255"],
100 "jsonAttr": "maxHops",
102 "example": ["mstpctl-maxhops 15"]
104 "mstpctl-txholdcount": {
105 "help": "bridge transmit holdcount",
106 "validrange": ["0", "255"],
108 "jsonAttr": "txHoldCounter",
110 "example": ["mstpctl-txholdcount 6"]
112 "mstpctl-forcevers": {
113 "help": "bridge force stp version",
114 "validvals": ["rstp", ],
117 "jsonAttr": "forceProtocolVersion",
118 "example": ["mstpctl-forcevers rstp"]
120 "mstpctl-portpathcost": {
121 "help": "bridge port path cost",
122 "validvals": ["<interface-range-list>"],
123 "validrange": ["0", "65535"],
125 "jsonAttr": "adminExtPortCost",
128 "under the bridge: mstpctl-portpathcost swp1=0 swp2=1",
129 "under the port (recommended): mstpctl-portpathcost 0"
133 "help": "bridge port p2p detection mode",
135 "jsonAttr": "adminPointToPoint",
136 "validvals": ["<interface-yes-no-auto-list>"],
139 "under the bridge: mstpctl-portp2p swp1=yes swp2=no",
140 "under the port (recommended): mstpctl-portp2p yes"
143 "mstpctl-portrestrrole": {
145 "enable/disable port ability to take root role of the port",
147 "jsonAttr": "restrictedRole",
148 "validvals": ["<interface-yes-no-list>"],
151 "under the bridge: mstpctl-portrestrrole swp1=yes swp2=no",
152 "under the port (recommended): mstpctl-portrestrrole yes"
155 "mstpctl-portrestrtcn": {
157 "enable/disable port ability to propagate received "
158 "topology change notification of the port",
160 "jsonAttr": "restrictedTcn",
161 "validvals": ["<interface-yes-no-list>"],
164 "under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no",
165 "under the port (recommended): mstpctl-portrestrtcn yes"
168 "mstpctl-bpduguard": {
170 "enable/disable bpduguard",
172 "jsonAttr": "bpduGuardPort",
173 "validvals": ["<interface-yes-no-list>"],
176 "under the bridge: mstpctl-bpduguard swp1=yes swp2=no",
177 "under the port (recommended): mstpctl-bpduguard yes"
180 "mstpctl-treeportprio": {
181 "help": "Sets the <port>'s priority MSTI instance. "
182 "The priority value must be a number between 0 and 240 "
183 "and a multiple of 16.",
185 "validvals": ["<interface-range-list-multiple-of-16>"],
186 "validrange": ["0", "240"],
187 "jsonAttr": "treeportprio",
190 "under the bridge: mstpctl-treeportprio swp1=128 swp2=128",
191 "under the port (recommended): mstpctl-treeportprio 128"
195 "help": "set hello time",
196 "validrange": ["0", "255"],
199 "jsonAttr": "helloTime",
200 "example": ["mstpctl-hello 2"]
202 "mstpctl-portnetwork": {
203 "help": "enable/disable bridge assurance capability for a port",
204 "validvals": ["<interface-yes-no-list>"],
206 "jsonAttr": "networkPort",
209 "under the bridge: mstpctl-portnetwork swp1=yes swp2=no",
210 "under the port (recommended): mstpctl-portnetwork yes"
213 "mstpctl-portadminedge": {
214 "help": "enable/disable initial edge state of the port",
215 "validvals": ["<interface-yes-no-list>"],
217 "jsonAttr": "adminEdgePort",
220 "under the bridge: mstpctl-portadminedge swp1=yes swp2=no",
221 "under the port (recommended): mstpctl-portadminedge yes"
224 "mstpctl-portautoedge": {
225 "help": "enable/disable auto transition to/from edge state of the port",
226 "validvals": ["<interface-yes-no-list>"],
228 "jsonAttr": "autoEdgePort",
231 "under the bridge: mstpctl-portautoedge swp1=yes swp2=no",
232 "under the port (recommended): mstpctl-portautoedge yes"
235 "mstpctl-treeportcost": {
236 "help": "port tree cost",
237 # "validrange": ["0", "255"],
239 "jsonAttr": "extPortCost",
241 "mstpctl-portbpdufilter": {
242 "help": "enable/disable bpdu filter on a port. syntax varies "
243 "when defined under a bridge vs under a port",
244 "validvals": ["<interface-yes-no-list>"],
245 "jsonAttr": "bpduFilterPort",
249 "under a bridge: mstpctl-portbpdufilter swp1=no swp2=no",
250 "under a port: mstpctl-portbpdufilter yes"
256 # Maps mstp bridge attribute names to corresponding mstpctl commands
257 # XXX: This can be encoded in the modules dict above
258 _attrs_map
= OrderedDict([('mstpctl-treeprio' , 'treeprio'),
259 ('mstpctl-ageing' , 'ageing'),
260 ('mstpctl-fdelay' , 'fdelay'),
261 ('mstpctl-maxage' , 'maxage'),
262 ('mstpctl-maxhops' , 'maxhops'),
263 ('mstpctl-txholdcount' , 'txholdcount'),
264 ('mstpctl-forcevers', 'forcevers'),
265 ('mstpctl-hello' , 'hello')])
267 # Maps mstp port attribute names to corresponding mstpctl commands
268 # XXX: This can be encoded in the modules dict above
269 _port_attrs_map
= {'mstpctl-portpathcost' : 'portpathcost',
270 'mstpctl-portadminedge' : 'portadminedge',
271 'mstpctl-portautoedge' : 'portautoedge' ,
272 'mstpctl-portp2p' : 'portp2p',
273 'mstpctl-portrestrrole' : 'portrestrrole',
274 'mstpctl-portrestrtcn' : 'portrestrtcn',
275 'mstpctl-bpduguard' : 'bpduguard',
276 'mstpctl-treeportprio' : 'treeportprio',
277 'mstpctl-treeportcost' : 'treeportcost',
278 'mstpctl-portnetwork' : 'portnetwork',
279 'mstpctl-portbpdufilter' : 'portbpdufilter'}
281 def __init__(self
, *args
, **kargs
):
283 moduleBase
.__init
__(self
, *args
, **kargs
)
284 if not os
.path
.exists('/sbin/mstpctl'):
285 raise moduleNotSupported('module init failed: no /sbin/mstpctl found')
286 self
.name
= self
.__class
__.__name
__
287 self
.mstpctlcmd
= None
288 self
.mstpd_running
= (True if systemUtils
.is_process_running('mstpd')
292 # The ask is to make "mstpctl-portadminedge yes" part of the default ifupdown2
293 # policy for all vxlan interfaces. In the absence of this, the mstp work flow
294 # is flawed in the event of vxlan flap.
296 # As of today, for vxlan interfaces "oper edge port" is set to 'yes' and also
297 # "bpdufilter port" is also set to 'yes'. So, in a case where bridge has multiple
298 # vxlan interfaces, if one vxlan interface is flapped, this would trigger mstp
299 # re-evaluation of states on other vxlan interfaces, creating momentary traffic
300 # glitch on those vxlans. Setting "admin edge port" to yes (in addition to the
301 # defaults we already have) prevents this.
303 # We use to only support 'mstpctl-vxlan-always-set-bpdu-params' but introducing a
304 # separate policy attribute doesn't make sense, we should have one single
305 # attribute to handle the whole thing (and deprecate mstpctl-vxlan-always-set-bpdu-params)
306 # mstpctl-set-default-vxlan-bridge-attrs=yes will set
307 # mstpctl-portbpdufilter
309 # mstpctl-portadminedge
311 self
.set_default_mstp_vxlan_bridge_config
= utils
.get_boolean_from_string(
312 policymanager
.policymanager_api
.get_module_globals(
313 module_name
=self
.__class
__.__name
__,
314 attr
='mstpctl-vxlan-always-set-bpdu-params'
316 ) or utils
.get_boolean_from_string(
317 policymanager
.policymanager_api
.get_module_globals(
318 module_name
=self
.__class
__.__name
__,
319 attr
='mstpctl-set-default-vxlan-bridge-attrs'
323 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
324 if self
._is
_bridge
(ifaceobj
):
325 if (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_VLAN_AWARE
326 and ifaceobj
.get_attr_value_first('mstpctl-portadminedge')):
327 self
.logger
.error('%s: unsupported use of keyword '
328 '\'mstpctl-portadminedge\' when '
329 'bridge-vlan-aware is on'
334 def _is_bridge(self
, ifaceobj
):
335 return ifaceobj
.link_kind
& ifaceLinkKind
.BRIDGE \
336 or ifaceobj
.get_attr_value_first('mstpctl-ports') \
337 or ifaceobj
.get_attr_value_first('bridge-ports')
339 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
340 if not self
._is
_bridge
(ifaceobj
):
342 return self
.parse_port_list(ifaceobj
.name
,
343 ifaceobj
.get_attr_value_first(
344 'mstpctl-ports'), ifacenames_all
)
346 def get_dependent_ifacenames_running(self
, ifaceobj
):
347 self
._init
_command
_handlers
()
348 if (self
.cache
.bridge_exists(ifaceobj
.name
) and
349 not self
.mstpctlcmd
.mstpbridge_exists(ifaceobj
.name
)):
351 return self
.cache
.get_slaves(ifaceobj
.name
)
353 def _get_bridge_port_attr_value(self
, bridgename
, portname
, attr
):
354 json_attr
= self
.get_mod_subattr(attr
, 'jsonAttr')
355 return self
.mstpctlcmd
.get_bridge_port_attr(bridgename
,
359 def _get_bridge_port_list(self
, ifaceobj
):
361 # port list is also available in the previously
362 # parsed dependent list. Use that if available, instead
363 # of parsing port expr again
364 port_list
= ifaceobj
.lowerifaces
367 ports
= ifaceobj
.get_attr_value_first('mstpctl-ports')
369 return self
.parse_port_list(ifaceobj
.name
, ports
)
373 def _ports_enable_disable_ipv6(self
, ports
, enable
='1'):
376 self
.write_file('/proc/sys/net/ipv6/conf/%s' %p
+
377 '/disable_ipv6', enable
)
378 except Exception as e
:
379 self
.logger
.info(str(e
))
382 def _add_ports(self
, ifaceobj
):
383 bridgeports
= self
._get
_bridge
_port
_list
(ifaceobj
)
385 runningbridgeports
= []
386 # Delete active ports not in the new port list
387 if not ifupdownflags
.flags
.PERFMODE
:
388 runningbridgeports
= self
.cache
.get_slaves(ifaceobj
.name
)
389 if runningbridgeports
:
390 for bport
in runningbridgeports
:
391 if not bridgeports
or bport
not in bridgeports
:
392 self
.netlink
.link_set_nomaster(bport
)
394 runningbridgeports
= []
398 for bridgeport
in set(bridgeports
).difference(set(runningbridgeports
)):
400 if (not ifupdownflags
.flags
.DRYRUN
and
401 not self
.cache
.link_exists(bridgeport
)):
402 self
.log_warn('%s: bridge port %s does not exist'
403 %(ifaceobj
.name
, bridgeport
))
406 self
.netlink
.link_set_master(bridgeport
, ifaceobj
.name
)
407 self
.netlink
.addr_flush(bridgeport
)
408 except Exception as e
:
409 self
.log_error(str(e
), ifaceobj
)
412 self
.log_error('error configuring bridge (missing ports)')
414 def _apply_bridge_settings(self
, ifaceobj
, ifaceobj_getfunc
):
415 check
= False if ifupdownflags
.flags
.PERFMODE
else True
417 # set bridge attributes
418 for attrname
, dstattrname
in list(self
._attrs
_map
.items()):
419 config_val
= ifaceobj
.get_attr_value_first(attrname
)
420 default_val
= policymanager
.policymanager_api
.get_iface_default(module_name
=self
.__class
__.__name
__, ifname
=ifaceobj
.name
, attr
=attrname
)
422 default_val
= self
.get_mod_subattr(attrname
, 'default')
423 jsonAttr
= self
.get_mod_subattr(attrname
, 'jsonAttr')
425 running_val
= self
.mstpctlcmd
.get_bridge_attr(
426 ifaceobj
.name
, jsonAttr
)
428 self
.logger
.info('%s: could not get running %s value'
429 %(ifaceobj
.name
, attrname
))
431 if (not config_val
and default_val
and (running_val
!= default_val
)):
432 # this happens when users remove an attribute from a port
433 # and expect the default to be restored with ifreload.
434 config_val
= default_val
436 # there is nothing configured and no default to reset
439 if attrname
== 'mstpctl-treeprio':
440 self
.mstpctlcmd
.set_bridge_treeprio(ifaceobj
.name
,
443 self
.mstpctlcmd
.set_bridge_attr(ifaceobj
.name
,
444 dstattrname
, config_val
, check
)
445 except Exception as e
:
446 self
.logger
.warning('%s' %str
(e
))
449 if self
.cache
.bridge_is_vlan_aware(ifaceobj
.name
):
451 bridgeports
= self
._get
_bridge
_port
_list
(ifaceobj
)
454 # set bridge port attributes
455 for attrname
, dstattrname
in list(self
._port
_attrs
_map
.items()):
456 config_val
= ifaceobj
.get_attr_value_first(attrname
)
457 default_val
= self
.get_mod_subattr(attrname
,'default')
459 # nothing configured, we may need to reset all ports to defaults
460 # if the default exists and jsonAttribute conversion exists
462 jsonAttr
= self
.get_mod_subattr(attrname
, 'jsonAttr')
463 if default_val
and jsonAttr
:
464 for port
in bridgeports
:
465 if not self
.cache
.link_is_bridge_port(port
):
468 bport_ifaceobjs
= ifaceobj_getfunc(port
)
470 default_val
= self
._get
_default
_val
(attrname
, bport_ifaceobjs
[0], ifaceobj
)
471 for brport_ifaceobj
in bport_ifaceobjs
or []:
472 attr_value
= brport_ifaceobj
.get_attr_value_first(attrname
)
474 default_val
= attr_value
477 self
.mstpctlcmd
.set_bridge_port_attr(ifaceobj
.name
,
482 except Exception as e
:
483 self
.logger
.debug('%s' % str(e
))
484 self
.logger
.info('%s: not resetting %s config'
485 %(ifaceobj
.name
, attrname
))
486 # leave the loop for this attribute
489 portlist
= self
.parse_port_list(ifaceobj
.name
, config_val
)
491 self
.log_error('%s: error parsing \'%s %s\''
492 %(ifaceobj
.name
, attrname
, config_val
), ifaceobj
)
494 # there was a configured value so we need to parse it
495 # and set the attribute for each port configured
498 (port
, val
) = p
.split('=')
499 # if it is not bridge port, continue
500 if not os
.path
.exists('/sys/class/net/%s/brport' %port
):
502 json_attr
= self
.get_mod_subattr(attrname
, 'jsonAttr')
503 self
.mstpctlcmd
.set_bridge_port_attr(ifaceobj
.name
,
508 except Exception as e
:
509 self
.log_error('%s: error setting %s (%s)'
510 %(ifaceobj
.name
, attrname
, str(e
)),
511 ifaceobj
, raise_error
=False)
512 except Exception as e
:
513 self
.log_warn(str(e
))
516 def _get_default_val(self
, attr
, ifaceobj
, bridgeifaceobj
):
517 if (self
.set_default_mstp_vxlan_bridge_config
518 and ifaceobj
.link_kind
& ifaceLinkKind
.VXLAN
520 'mstpctl-portbpdufilter',
522 'mstpctl-portadminedge',
526 config_val
= bridgeifaceobj
.get_attr_value_first(attr
)
527 except Exception as e
:
530 if ifaceobj
.name
not in [v
.split('=')[0] for v
in config_val
.split()]:
533 index
= [v
.split('=')[0] for v
in config_val
.split()].index(ifaceobj
.name
)
534 return [v
.split('=')[1] for v
in config_val
.split()][index
]
538 default_val
= policymanager
.policymanager_api
.get_iface_default(module_name
=self
.__class
__.__name
__, ifname
=ifaceobj
.name
, attr
=attr
)
540 return self
.get_mod_subattr(attr
,'default')
543 def _apply_bridge_port_settings(self
, ifaceobj
, bvlan_aware
, bridgename
=None,
547 check
= False if ifupdownflags
.flags
.PERFMODE
else True
549 if not bridgename
and bridgeifaceobj
:
550 bridgename
= bridgeifaceobj
.name
552 if not stp_running_on
:
553 # stp may get turned on at a later point
554 self
.logger
.info('%s: ignoring config'
556 ' (stp on bridge %s is not on yet)' %bridgename
)
558 if (not mstpd_running
or
559 not os
.path
.exists('/sys/class/net/%s/brport' %ifaceobj
.name
) or
561 if (not bvlan_aware
and
562 self
.set_default_mstp_vxlan_bridge_config
and
563 (ifaceobj
.link_kind
& ifaceLinkKind
.VXLAN
)):
565 'mstpctl-portbpdufilter',
567 'mstpctl-portadminedge'
569 json_attr
= self
.get_mod_subattr(attr
, 'jsonAttr')
570 config_val
= self
._get
_default
_val
(attr
, ifaceobj
,
573 self
.mstpctlcmd
.set_bridge_port_attr(bridgename
,
575 self
._port
_attrs
_map
[attr
],
578 except Exception as e
:
579 self
.log_warn('%s: error setting %s (%s)'
580 % (ifaceobj
.name
, attr
, str(e
)))
583 # for "traditional" bridges we also want to let the user configure
584 # some attributes (possibly all of them in the future)
585 applied
= self
._apply
_bridge
_port
_settings
_attributes
_list
(
587 ('mstpctl-portrestrrole', 'portrestrrole'),
588 ('mstpctl-portautoedge', 'portautoedge')
596 # set bridge port attributes
597 return self
._apply
_bridge
_port
_settings
_attributes
_list
(
598 list(self
._port
_attrs
_map
.items()),
605 def _apply_bridge_port_settings_attributes_list(self
, attributes_list
, ifaceobj
, bridgeifaceobj
, bridgename
, applied
):
606 for attrname
, dstattrname
in attributes_list
:
607 config_val
= ifaceobj
.get_attr_value_first(attrname
)
608 default_val
= self
._get
_default
_val
(attrname
, ifaceobj
, bridgeifaceobj
)
609 jsonAttr
= self
.get_mod_subattr(attrname
, 'jsonAttr')
610 # to see the running value, stp would have to be on
611 # so we would have parsed mstpctl showportdetail json output
613 running_val
= self
.mstpctlcmd
.get_bridge_port_attr(bridgename
,
614 ifaceobj
.name
, jsonAttr
)
616 self
.logger
.info('%s %s: could not get running %s value'
617 %(bridgename
, ifaceobj
.name
, attrname
))
619 if (not config_val
and default_val
and (running_val
!= default_val
)):
620 # this happens when users remove an attribute from a port
621 # and expect the default to be restored with ifreload.
622 config_val
= default_val
624 # there is nothing configured and no default to reset
628 self
.mstpctlcmd
.set_bridge_port_attr(bridgename
,
629 ifaceobj
.name
, dstattrname
, config_val
, json_attr
=jsonAttr
)
631 except Exception as e
:
632 self
.log_error('%s: error setting %s (%s)'
633 %(ifaceobj
.name
, attrname
, str(e
)), ifaceobj
,
637 def _apply_bridge_port_settings_all(self
, ifaceobj
,
638 ifaceobj_getfunc
=None):
639 self
.logger
.info('%s: applying mstp configuration '
640 %ifaceobj
.name
+ 'specific to ports')
641 # Query running bridge ports. and only apply attributes on them
642 bridgeports
= self
.cache
.get_slaves(ifaceobj
.name
)
644 self
.logger
.debug('%s: cannot find bridgeports' %ifaceobj
.name
)
647 bvlan_aware
= self
.cache
.bridge_is_vlan_aware(ifaceobj
.name
)
649 for bport
in bridgeports
:
650 self
.logger
.info('%s: processing mstp config for port %s'
651 %(ifaceobj
.name
, bport
))
652 if not self
.cache
.link_exists(bport
):
654 if not os
.path
.exists('/sys/class/net/%s/brport' %bport
):
656 bportifaceobjlist
= ifaceobj_getfunc(bport
)
657 if not bportifaceobjlist
:
659 for bportifaceobj
in bportifaceobjlist
:
660 # Dont process bridge port if it already has been processed
661 if (bportifaceobj
.module_flags
.get(self
.name
,0x0) & \
662 mstpctlFlags
.PORT_PROCESSED
):
665 self
._apply
_bridge
_port
_settings
(bportifaceobj
, bvlan_aware
,
666 ifaceobj
.name
, ifaceobj
)
667 except Exception as e
:
669 self
.log_warn(str(e
))
671 def _is_running_userspace_stp_state_on(self
, bridgename
):
672 stp_state_file
= '/sys/class/net/%s/bridge/stp_state' %bridgename
673 if not stp_state_file
:
675 running_stp_state
= self
.read_file_oneline(stp_state_file
)
676 if running_stp_state
and running_stp_state
== '2':
680 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
682 # bridge port specific:
683 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
:
684 bridgename
= self
.cache
.get_master(ifaceobj
.name
)
688 bvlan_aware
= self
.cache
.bridge_is_vlan_aware(bridgename
)
689 mstpd_running
= self
.mstpd_running
690 stp_running_on
= bool(self
.cache
.get_bridge_stp(bridgename
))
691 applied
= self
._apply
_bridge
_port
_settings
(ifaceobj
, bvlan_aware
, bridgename
,
692 None, stp_running_on
,
695 ifaceobj
.module_flags
[self
.name
] = \
696 ifaceobj
.module_flags
.setdefault(self
.name
,0) | \
697 mstpctlFlags
.PORT_PROCESSED
700 elif not self
._is
_bridge
(ifaceobj
):
702 # we are now here because the ifaceobj is a bridge
707 if ifaceobj
.get_attr_value_first('mstpctl-ports'):
708 # If bridge ports specified with mstpctl attr, create the
709 # bridge and also add its ports
710 if not self
.cache
.link_exists(ifaceobj
.name
):
711 self
.netlink
.link_add_bridge(ifaceobj
.name
)
714 self
._add
_ports
(ifaceobj
)
715 except Exception as e
:
720 running_ports
= self
.cache
.get_slaves(ifaceobj
.name
)
722 # disable ipv6 for ports that were added to bridge
723 self
._ports
_enable
_disable
_ipv
6(running_ports
, '1')
725 stp
= ifaceobj
.get_attr_value_first('mstpctl-stp')
727 self
.set_iface_attr(ifaceobj
, 'mstpctl-stp',
728 self
.iproute2
.bridge_set_stp
)
729 stp
= utils
.get_boolean_from_string(stp
)
731 stp
= self
.cache
.get_bridge_stp(ifaceobj
.name
)
732 if self
.mstpd_running
and stp
:
733 self
.mstpctlcmd
.batch_start()
734 self
._apply
_bridge
_settings
(ifaceobj
, ifaceobj_getfunc
)
735 self
._apply
_bridge
_port
_settings
_all
(ifaceobj
,
736 ifaceobj_getfunc
=ifaceobj_getfunc
)
737 self
.mstpctlcmd
.batch_commit()
738 except Exception as e
:
739 self
.log_error(str(e
), ifaceobj
)
741 raise Exception(porterrstr
)
743 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
744 if not self
._is
_bridge
(ifaceobj
):
747 if ifaceobj
.get_attr_value_first('mstpctl-ports'):
748 # If bridge ports specified with mstpctl attr, delete the
750 ports
= self
.cache
.get_slaves(ifaceobj
.name
)
752 self
._ports
_enable
_disable
_ipv
6(ports
, '0')
753 self
.netlink
.link_del(ifaceobj
.name
)
754 except Exception as e
:
755 self
.log_error(str(e
), ifaceobj
)
757 def _query_running_attrs(self
, ifaceobjrunning
, bridge_vlan_aware
=False):
760 tmpbridgeattrdict
= self
.mstpctlcmd
.get_bridge_attrs(ifaceobjrunning
.name
)
761 #self.logger.info('A' + str(tmpbridgeattrdict))
762 if not tmpbridgeattrdict
:
763 return bridgeattrdict
765 for k
,v
in list(tmpbridgeattrdict
.items()):
766 if k
== 'stp' or not v
:
769 ports
= list(v
.keys())
771 attrname
= 'mstpctl-' + k
772 if (v
and v
!= self
.get_mod_subattr(attrname
, 'default')
773 and attrname
!= 'mstpctl-maxhops'):
774 bridgeattrdict
[attrname
] = [v
]
776 ports
= self
.cache
.get_slaves(ifaceobjrunning
.name
)
777 # Do this only for vlan-UNAWARE-bridge
778 if ports
and not bridge_vlan_aware
:
779 portconfig
= {'mstpctl-portautoedge' : '',
780 'mstpctl-portbpdufilter' : '',
781 'mstpctl-portnetwork' : '',
782 'mstpctl-portpathcost' : '',
783 'mstpctl-portadminedge' : '',
784 'mstpctl-portp2p' : '',
785 'mstpctl-portrestrrole' : '',
786 'mstpctl-portrestrtcn' : '',
787 'mstpctl-bpduguard' : '',
788 'mstpctl-treeportprio' : '',
789 'mstpctl-treeportcost' : ''}
793 for attr
in ['mstpctl-portautoedge',
794 'mstpctl-portbpdufilter',
795 'mstpctl-portnetwork',
796 'mstpctl-portadminedge',
798 'mstpctl-portrestrrole',
799 'mstpctl-portrestrtcn',
802 v
= self
._get
_bridge
_port
_attr
_value
(ifaceobjrunning
.name
,
805 portconfig
[attr
] += ' %s=%s' % (p
, v
)
807 for attr
in ['mstpctl-portpathcost', 'mstpctl-treeportcost']:
808 v
= self
._get
_bridge
_port
_attr
_value
(ifaceobjrunning
.name
,
810 if v
and v
!= self
.get_mod_subattr(attr
, 'default'):
811 portconfig
[attr
] += ' %s=%s' % (p
, v
)
813 bridgeattrdict
.update({k
: [v
] for k
, v
in list(portconfig
.items())
815 return bridgeattrdict
817 def _get_config_stp(self
, ifaceobj
):
818 stp
= (ifaceobj
.get_attr_value_first('mstpctl-stp') or
819 ifaceobj
.get_attr_value_first('bridge-stp') or
820 policymanager
.policymanager_api
.get_iface_default(module_name
=self
.__class
__.__name
__, ifname
=ifaceobj
.name
, attr
='mstpctl-stp') or
821 # this is a temporary method to access policy default value of bridge-stp
822 policymanager
.policymanager_api
.get_iface_default(module_name
='bridge', ifname
=ifaceobj
.name
, attr
='bridge-stp'))
823 return utils
.get_boolean_from_string(stp
)
825 def _query_check_bridge(self
, ifaceobj
, ifaceobjcurr
,
826 ifaceobj_getfunc
=None):
827 # list of attributes that are not supported currently
828 blacklistedattrs
= ['mstpctl-portpathcost',
829 'mstpctl-treeportprio', 'mstpctl-treeportcost']
830 if not self
.cache
.bridge_exists(ifaceobj
.name
):
831 self
.logger
.debug('bridge %s does not exist' %ifaceobj
.name
)
833 ifaceattrs
= self
.dict_key_subset(ifaceobj
.config
,
834 self
.get_mod_attrs())
835 if self
.set_default_mstp_vxlan_bridge_config
:
836 for attr
in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
837 if attr
not in ifaceattrs
:
838 ifaceattrs
.append(attr
)
841 runningattrs
= self
.mstpctlcmd
.get_bridge_attrs(ifaceobj
.name
)
842 #self.logger.info('B' + str(runningattrs))
845 config_stp
= self
._get
_config
_stp
(ifaceobj
)
846 running_stp
= self
.cache
.get_bridge_stp(ifaceobj
.name
)
847 running_port_list
= self
.cache
.get_slaves(ifaceobj
.name
)
849 # for all mstpctl options
850 if k
in blacklistedattrs
:
852 if k
in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
853 #special case, 'ifquery --check --with-defaults' on a VLAN
855 if not running_port_list
:
857 if (not config_stp
or not running_stp
):
859 v
= ifaceobj
.get_attr_value_first(k
)
866 for bportval
in v
.split():
867 config_val
[bportval
.split('=')[0]] = bportval
.split('=')[1]
868 #for bport in bridgeports:
869 for bport
in running_port_list
:
870 bportifaceobjlist
= ifaceobj_getfunc(bport
)
871 if not bportifaceobjlist
:
873 for bportifaceobj
in bportifaceobjlist
:
874 if (bport
not in config_val
):
875 if (bportifaceobj
.link_kind
& ifaceLinkKind
.VXLAN
):
876 if (not ifupdownflags
.flags
.WITHDEFAULTS
or
877 (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_VLAN_AWARE
)):
883 if ((bportifaceobj
.link_kind
& ifaceLinkKind
.VXLAN
) and
884 (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_VLAN_AWARE
)):
886 conf
= config_val
[bport
]
887 jsonAttr
= self
.get_mod_subattr(k
, 'jsonAttr')
889 running_val
= self
.mstpctlcmd
.get_bridge_port_attr(ifaceobj
.name
, bport
, jsonAttr
)
891 self
.logger
.info('%s %s: could not get running %s value'
892 %(ifaceobj
.name
, bport
, attr
))
894 if conf
!= running_val
:
896 bridge_ports
.update({bport
: running_val
})
897 for port
, val
in list(bridge_ports
.items()):
898 #running state format
899 #mstpctl-portbpdufilter swp2=yes swp1=yes vx-14567101=yes [pass]
900 #mstpctl-bpduguard swp2=yes swp1=yes vx-14567101=yes [pass]
901 state
+= port
+ '=' + val
+ ' '
903 ifaceobjcurr
.update_config_with_status(k
, state
, result
)
906 # get the corresponding ifaceobj attr
907 v
= ifaceobj
.get_attr_value_first(k
)
911 # Get the running attribute
912 rv
= runningattrs
.get(k
[8:])
913 if k
== 'mstpctl-stp':
914 # special case stp compare because it may
915 # contain more than one valid values
916 stp_on_vals
= ['on', 'yes']
917 stp_off_vals
= ['off']
918 rv
= self
.sysfs
.bridge_get_stp(ifaceobj
.name
)
919 if ((v
in stp_on_vals
and rv
in stp_on_vals
) or
920 (v
in stp_off_vals
and rv
in stp_off_vals
)):
921 ifaceobjcurr
.update_config_with_status('mstpctl-stp', v
, 0)
923 ifaceobjcurr
.update_config_with_status('mstpctl-stp', v
, 1)
926 if k
== 'mstpctl-ports':
927 # special case ports because it can contain regex or glob
928 # XXX: We get all info from mstputils, which means if
929 # mstpd is down, we will not be returning any bridge bridgeports
930 bridge_port_list
= self
._get
_bridge
_port
_list
(ifaceobj
)
931 if not running_port_list
and not bridge_port_list
:
934 if running_port_list
and bridge_port_list
:
935 difference
= set(running_port_list
).symmetric_difference(
936 set(bridge_port_list
))
939 ifaceobjcurr
.update_config_with_status('mstpctl-ports',
940 ' '.join(running_port_list
)
941 if running_port_list
else '', portliststatus
)
942 elif k
[:12] == 'mstpctl-port' or k
== 'mstpctl-bpduguard':
943 # Now, look at port attributes
944 # derive the mstpctlcmd attr name
945 #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:]
946 mstpctlcmdattrname
= k
[8:]
948 # for port attributes, the attributes are in a list
949 # <portname>=<portattrvalue>
952 vlist
= self
.parse_port_list(ifaceobj
.name
, v
)
955 for vlistitem
in vlist
:
957 (p
, v
) = vlistitem
.split('=')
958 currv
= self
._get
_bridge
_port
_attr
_value
(ifaceobj
.name
, p
, k
)
960 currstr
+= ' %s=%s' %(p
, currv
)
962 currstr
+= ' %s=%s' %(p
, 'None')
965 except Exception as e
:
966 self
.log_warn(str(e
))
968 ifaceobjcurr
.update_config_with_status(k
, currstr
, status
)
970 ifaceobjcurr
.update_config_with_status(k
, '', 1)
972 ifaceobjcurr
.update_config_with_status(k
, rv
, 1)
974 ifaceobjcurr
.update_config_with_status(k
, rv
, 0)
976 def _query_check_bridge_vxlan_port(self
, ifaceobj
, ifaceobjcurr
,
977 ifaceobj_getfunc
=None):
978 masters
= ifaceobj
.upperifaces
981 for bridge
in masters
:
982 bifaceobjlist
= ifaceobj_getfunc(bridge
)
983 for bifaceobj
in bifaceobjlist
:
984 if (self
._is
_bridge
(bifaceobj
) and
985 self
.set_default_mstp_vxlan_bridge_config
and
986 (bifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_VLAN_AWARE
)):
987 config_stp
= self
._get
_config
_stp
(bifaceobj
)
988 running_stp
= self
.cache
.get_bridge_stp(bifaceobj
.name
)
989 if (not config_stp
or not running_stp
):
992 'mstpctl-portbpdufilter',
994 'mstpctl-portadminedge'
996 jsonAttr
= self
.get_mod_subattr(attr
, 'jsonAttr')
997 config_val
= bifaceobj
.get_attr_value_first(attr
)
999 if ifaceobj
.name
not in [v
.split('=')[0] for v
in config_val
.split()]:
1000 if not ifupdownflags
.flags
.WITHDEFAULTS
:
1004 index
= [v
.split('=')[0] for v
in config_val
.split()].index(ifaceobj
.name
)
1005 config_val
= [v
.split('=')[1] for v
in config_val
.split()][index
]
1007 if not ifupdownflags
.flags
.WITHDEFAULTS
:
1011 running_val
= self
.mstpctlcmd
.get_bridge_port_attr(bifaceobj
.name
,
1012 ifaceobj
.name
, jsonAttr
)
1014 self
.logger
.info('%s %s: could not get running %s value'
1015 %(bifaceobj
.name
, ifaceobj
.name
, attr
))
1017 ifaceobjcurr
.update_config_with_status(attr
,
1019 0 if running_val
== config_val
else 1)
1023 def _query_check_bridge_port(self
, ifaceobj
, ifaceobjcurr
):
1024 if not self
.cache
.link_exists(ifaceobj
.name
):
1025 #self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
1026 ifaceobjcurr
.status
= ifaceStatus
.NOTFOUND
1028 # Check if this is a bridge port
1029 if not self
.cache
.link_is_bridge_port(ifaceobj
.name
):
1030 # mark all the bridge attributes as error
1031 ifaceobjcurr
.check_n_update_config_with_status_many(ifaceobj
,
1032 list(self
._port
_attrs
_map
.keys()), 0)
1034 bridgename
= self
.cache
.get_master(ifaceobj
.name
)
1035 # list of attributes that are not supported currently
1036 blacklistedattrs
= ['mstpctl-portpathcost',
1037 'mstpctl-treeportprio', 'mstpctl-treeportcost']
1038 ifaceattrs
= self
.dict_key_subset(ifaceobj
.config
,
1039 list(self
._port
_attrs
_map
.keys()))
1042 runningattrs
= self
.mstpctlcmd
.get_bridge_attrs(ifaceobj
.name
)
1043 #self.logger.info('C' + str(runningattrs))
1044 if not runningattrs
:
1046 for k
in ifaceattrs
:
1047 # for all mstpctl options
1048 # get the corresponding ifaceobj attr
1049 v
= ifaceobj
.get_attr_value_first(k
)
1050 if not v
or k
in blacklistedattrs
:
1051 ifaceobjcurr
.update_config_with_status(k
, v
, -1)
1053 currv
= self
._get
_bridge
_port
_attr
_value
(bridgename
, ifaceobj
.name
, k
)
1056 ifaceobjcurr
.update_config_with_status(k
, currv
, 1)
1058 ifaceobjcurr
.update_config_with_status(k
, currv
, 0)
1060 ifaceobjcurr
.update_config_with_status(k
, None, 1)
1062 def _query_check(self
, ifaceobj
, ifaceobjcurr
, ifaceobj_getfunc
=None):
1063 if self
._is
_bridge
(ifaceobj
):
1064 self
._query
_check
_bridge
(ifaceobj
, ifaceobjcurr
, ifaceobj_getfunc
)
1065 elif ifaceobj
.link_kind
& ifaceLinkKind
.VXLAN
:
1066 self
._query
_check
_bridge
_vxlan
_port
(ifaceobj
, ifaceobjcurr
,
1069 self
._query
_check
_bridge
_port
(ifaceobj
, ifaceobjcurr
)
1071 def _query_bridge_port_attr(self
, ifaceobjrunning
, bridgename
, attr
, value_cmp
):
1072 v
= self
._get
_bridge
_port
_attr
_value
(bridgename
,
1073 ifaceobjrunning
.name
,
1075 if v
and value_cmp
and v
!= value_cmp
:
1076 ifaceobjrunning
.update_config(attr
, v
)
1077 elif v
and not value_cmp
:
1078 ifaceobjrunning
.update_config(attr
, v
)
1080 def _query_running_bridge_port(self
, ifaceobjrunning
):
1081 bridgename
= self
.cache
.get_master(ifaceobjrunning
.name
)
1083 self
.logger
.warning('%s: unable to determine bridgename'
1084 %ifaceobjrunning
.name
)
1086 if self
.sysfs
.bridge_get_stp(bridgename
) == 'no':
1087 # This bridge does not run stp, return
1089 # if userspace stp not set, return
1090 if self
.systcl_get_net_bridge_stp_user_space() != '1':
1093 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1094 'mstpctl-portautoedge',
1095 self
.get_mod_subattr('mstpctl-portautoedge', 'default'))
1097 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1098 'mstpctl-portbpdufilter',
1101 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1102 'mstpctl-portnetwork',
1105 # XXX: Can we really get path cost of a port ???
1106 #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
1107 #if v and v != self.get_mod_subattr('mstpctl-pathcost',
1109 # ifaceobjrunning.update_config('mstpctl-network', v)
1111 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1112 'mstpctl-portadminedge',
1115 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1119 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1120 'mstpctl-portrestrrole',
1123 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1124 'mstpctl-portrestrtcn',
1127 self
._query
_bridge
_port
_attr
(ifaceobjrunning
, bridgename
,
1128 'mstpctl-bpduguard',
1131 # XXX: Can we really get path cost of a port ???
1132 #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
1134 #if v and v != self.get_mod_subattr('mstpctl-treeportprio',
1136 # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
1138 #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
1140 #if v and v != self.get_mod_subattr('mstpctl-treeportcost',
1142 # portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
1144 def _query_running_bridge(self
, ifaceobjrunning
):
1145 if self
.sysfs
.bridge_get_stp(ifaceobjrunning
.name
) == 'no':
1146 # This bridge does not run stp, return
1148 # if userspace stp not set, return
1149 if self
.systcl_get_net_bridge_stp_user_space() != '1':
1151 # Check if mstp really knows about this bridge
1152 if not self
.mstpctlcmd
.mstpbridge_exists(ifaceobjrunning
.name
):
1154 bridge_vlan_aware
= False
1155 if ifaceobjrunning
.get_attr_value_first('bridge-vlan-aware') == 'yes':
1156 bridge_vlan_aware
= True
1157 ifaceobjrunning
.update_config_dict(self
._query
_running
_attrs
(
1161 def _query_running(self
, ifaceobjrunning
, **extra_args
):
1162 if self
.cache
.bridge_exists(ifaceobjrunning
.name
):
1163 self
._query
_running
_bridge
(ifaceobjrunning
)
1164 elif self
.cache
.link_is_bridge_port(ifaceobjrunning
.name
):
1165 self
._query
_running
_bridge
_port
(ifaceobjrunning
)
1167 def _query_bridge_port(self
, ifaceobj
, ifaceobj_getfunc
=None):
1181 bridge_ports vxlan1wd vxlan2wd
1182 bridge-vlan-aware yes
1190 $ ifquery --with-defaults vxlan1wd
1194 mstpctl-portbpdufilter yes
1195 mstpctl-bpduguard yes
1197 masters
= ifaceobj
.upperifaces
1201 for bridge
in masters
:
1202 bifaceobj
= ifaceobj_getfunc(bridge
)[0]
1203 if (self
._is
_bridge
(bifaceobj
) and
1204 self
.set_default_mstp_vxlan_bridge_config
and
1205 (bifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_VLAN_AWARE
)):
1206 for attr
in ('mstpctl-portbpdufilter',
1207 'mstpctl-bpduguard',
1208 'mstpctl-portadminedge'):
1209 jsonAttr
= self
.get_mod_subattr(attr
, 'jsonAttr')
1210 config_val
= ifaceobj
.get_attr_value_first(attr
)
1211 if config_val
or not ifupdownflags
.flags
.WITHDEFAULTS
:
1214 ifaceobj
.replace_config(attr
, config_val
)
1216 except Exception as e
:
1217 self
.logger
.info("%s: %s" %(ifaceobj
.name
, str(e
)))
1220 def _query(self
, ifaceobj
, ifaceobj_getfunc
=None, **kwargs
):
1221 """ add default policy attributes supported by the module """
1222 if not self
._is
_bridge
(ifaceobj
):
1223 if (ifaceobj
.module_flags
.get(self
.name
,0x0) &
1224 mstpctlFlags
.PORT_PROCESSED
):
1226 self
._query
_bridge
_port
(ifaceobj
, ifaceobj_getfunc
)
1227 ifaceobj
.module_flags
[self
.name
] = (
1228 ifaceobj
.module_flags
.setdefault(self
.name
,0) |
1229 mstpctlFlags
.PORT_PROCESSED
)
1231 lowerinfs
= ifaceobj
.lowerifaces
1234 if ifaceobj
.get_attr_value_first('bridge-vlan-aware') != 'yes':
1235 for attr
in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
1237 config
= ifaceobj
.get_attr_value_first(attr
)
1238 for port
in lowerinfs
:
1239 bportobjlist
= ifaceobj_getfunc(port
)
1240 for bportobj
in bportobjlist
:
1241 if bportobj
.get_attr_value_first('vxlan-id'):
1243 if port
not in [v
.split('=')[0] for v
in config
.split()]:
1244 config
+= ' %s=yes' %port
1246 state
+= '%s=yes ' %port
1247 ifaceobj
.replace_config(attr
, config
if config
else state
)
1249 for attr
in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
1251 config
= ifaceobj
.get_attr_value_first(attr
)
1252 for port
in lowerinfs
:
1253 bportobjlist
= ifaceobj_getfunc(port
)
1254 for bportobj
in bportobjlist
:
1255 if (bportobj
.module_flags
.get(self
.name
,0x0) &
1256 mstpctlFlags
.PORT_PROCESSED
):
1258 if bportobj
.get_attr_value_first('vxlan-id'):
1260 if port
not in [v
.split('=')[0] for v
in config
.split()]:
1261 bportobj
.update_config(attr
, 'yes')
1263 index
= [v
.split('=')[0] for v
in config
.split()].index(port
)
1264 state
= [v
.split('=')[1] for v
in config
.split()][index
]
1265 bportobj
.update_config(attr
, '%s' %state
)
1268 config
= ' '.join(v
)
1270 bportobj
.replace_config(attr
, 'yes')
1271 bportobj
.module_flags
[self
.name
] = (
1272 bportobj
.module_flags
.setdefault(self
.name
,0) |
1273 mstpctlFlags
.PORT_PROCESSED
)
1275 ifaceobj
.replace_config(attr
, config
)
1280 "query-checkcurr": _query_check
,
1281 "query-running": _query_running
,
1286 """ returns list of ops supported by this module """
1287 return list(self
._run
_ops
.keys())
1289 def _init_command_handlers(self
):
1290 if not self
.mstpctlcmd
:
1291 self
.mstpctlcmd
= mstpctlutil()
1293 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1294 ifaceobj_getfunc
=None, **extra_args
):
1295 """ run mstp configuration on the interface object passed as argument
1298 **ifaceobj** (object): iface object
1300 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1303 **query_ifaceobj** (object): query check ifaceobject. This is only
1304 valid when op is 'query-checkcurr'. It is an object same as
1305 ifaceobj, but contains running attribute values and its config
1306 status. The modules can use it to return queried running state
1307 of interfaces. status is success if the running state is same
1308 as user required state in ifaceobj. error otherwise.
1310 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
1312 op_handler
= self
._run
_ops
.get(operation
)
1315 self
._init
_command
_handlers
()
1316 if operation
== 'query-checkcurr':
1317 op_handler(self
, ifaceobj
, query_ifaceobj
,
1318 ifaceobj_getfunc
=ifaceobj_getfunc
)
1320 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)