]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/ethtool.py
addons: Allow to set `max` value in `--set-ring` option
[mirror_ifupdown2.git] / ifupdown2 / addons / ethtool.py
1 #!/usr/bin/env python3
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import os
8
9 try:
10 from ifupdown2.lib.addon import Addon
11
12 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
13 import ifupdown2.ifupdown.policymanager as policymanager
14 import ifupdown2.ifupdown.statemanager as statemanager
15
16 from ifupdown2.ifupdown.iface import *
17 from ifupdown2.ifupdown.utils import utils
18 from ifupdown2.ifupdown.exceptions import moduleNotSupported
19
20 from ifupdown2.ifupdownaddons.utilsbase import *
21 from ifupdown2.ifupdownaddons.modulebase import moduleBase
22 except (ImportError, ModuleNotFoundError):
23 from lib.addon import Addon
24
25 import ifupdown.ifupdownflags as ifupdownflags
26 import ifupdown.policymanager as policymanager
27 import ifupdown.statemanager as statemanager
28
29 from ifupdown.iface import *
30 from ifupdown.utils import utils
31 from ifupdown.exceptions import moduleNotSupported
32
33 from ifupdownaddons.utilsbase import *
34 from ifupdownaddons.modulebase import moduleBase
35
36
37 class ethtool(Addon, moduleBase):
38 """ ifupdown2 addon module to configure ethtool attributes """
39
40 _modinfo = {
41 "mhelp": "ethtool configuration module for interfaces",
42 "attrs": {
43 "link-speed": {
44 "help": "set link speed",
45 "validvals": [
46 "10",
47 "100",
48 "1000",
49 "10000",
50 "25000",
51 "40000",
52 "50000",
53 "100000",
54 "200000",
55 "400000"
56 ],
57 "example": ["link-speed 1000"],
58 "default": "varies by platform and port"
59 },
60 "link-duplex": {
61 "help": "set link duplex",
62 "example": ["link-duplex full"],
63 "validvals": ["half", "full"],
64 "default": "full"
65 },
66 "link-autoneg": {
67 "help": "set autonegotiation",
68 "example": ["link-autoneg on"],
69 "validvals": ["yes", "no", "on", "off"],
70 "default": "varies by platform and port"
71 },
72 "link-fec": {
73 "help": "set forward error correction mode",
74 "example": ["link-fec rs"],
75 "validvals": ["rs", "baser", "auto", "off"],
76 "default": "varies by platform and port"
77 },
78 'gro-offload': {
79 'help': 'Generic Receive Offload',
80 'example': ['gro-offload on'],
81 'validvals': ['on', 'off'],
82 'default': 'varies by interface'
83 },
84 'lro-offload': {
85 'help': 'Large Receive Offload',
86 'example': ['lro-offload on'],
87 'validvals': ['on', 'off'],
88 'default': 'varies by interface'
89 },
90 'gso-offload': {
91 'help': 'Generic Segmentation Offload',
92 'example': ['tso-offload on'],
93 'validvals': ['on', 'off'],
94 'default': 'varies by interface'
95 },
96 'tso-offload': {
97 'help': 'TCP Segmentation Offload',
98 'example': ['tso-offload on'],
99 'validvals': ['on', 'off'],
100 'default': 'varies by interface'
101 },
102 'ufo-offload': {
103 'help': 'UDP Fragmentation Offload',
104 'example': ['ufo-offload on'],
105 'validvals': ['on', 'off'],
106 'default': 'varies by interface'
107 },
108 'tx-offload': {
109 'help': 'TX Checksum Offload',
110 'example': ['tx-offload on'],
111 'validvals': ['on', 'off'],
112 'default': 'varies by interface'
113 },
114 'rx-offload': {
115 'help': 'RX Checksum Offload',
116 'example': ['rx-offload on'],
117 'validvals': ['on', 'off'],
118 'default': 'varies by interface'
119 },
120 'ring-rx': {
121 'help': 'Ring RX Parameter',
122 'example': ['ring-rx 512'],
123 'validvals': ['max', '<number>'],
124 'default': 'varies by interface'
125 },
126 'ring-tx': {
127 'help': 'Ring TX Parameter',
128 'example': ['ring-tx 512'],
129 'validvals': ['max', '<number>'],
130 'default': 'varies by interface'
131 },
132 }
133 }
134
135 def __init__(self, *args, **kargs):
136 Addon.__init__(self)
137 moduleBase.__init__(self, *args, **kargs)
138 if not os.path.exists(utils.ethtool_cmd):
139 raise moduleNotSupported('module init failed: %s: not found' % utils.ethtool_cmd)
140 # keep a list of iface objects who have modified link attributes
141 self.ifaceobjs_modified_configs = []
142 # Cache for features
143 self.feature_cache = None
144
145 self.ethtool_ignore_errors = policymanager.policymanager_api.get_module_globals(
146 module_name=self.__class__.__name__,
147 attr='ethtool_ignore_errors'
148 )
149
150 def do_ring_settings(self, ifaceobj, attr_name, option):
151 # Get the current configuration value and default value for the specified attribute
152 config_val = ifaceobj.get_attr_value_first(attr_name)
153 default_val = policymanager.policymanager_api.get_iface_default(
154 module_name='ethtool',
155 ifname=ifaceobj.name,
156 attr=attr_name)
157
158 # Check which variable to use, config_val > default_val. If none are set, return.
159 value = config_val or default_val
160 if not value:
161 return
162
163 if value == "max":
164 # Get the maximum value for the specified attribute
165 max_val = self.get_max_attr(attr_name, ifaceobj)
166 if not max_val:
167 return
168 value = max_val
169
170 # Get the current running value
171 running_val = self.get_running_attr(attr_name, ifaceobj)
172
173
174 # If the value is the same as the running value, do nothing
175 if value == running_val:
176 return
177
178 # Generate the ethtool command
179 cmd = ('%s --set-ring %s %s %s' %
180 (utils.ethtool_cmd, ifaceobj.name, option, value))
181
182 # Execute the ethtool command if command is generated
183 if cmd:
184 try:
185 utils.exec_command(cmd)
186 except Exception as e:
187 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
188
189 def do_offload_settings(self, ifaceobj, attr_name, eth_name):
190 default = 'default_' + eth_name
191 config_val = ifaceobj.get_attr_value_first(attr_name)
192 # Default
193 default_val = None
194 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
195 if saved_ifaceobjs:
196 default_val = saved_ifaceobjs[0].get_attr_value_first(default)
197 if config_val or default_val:
198
199 # get running value
200 running_val = str(self.get_running_attr(eth_name, ifaceobj)).lower()
201 # Save default value
202 # Load state data
203 if not default_val:
204 ifaceobj.config[default] = [running_val]
205 elif config_val:
206 # resave for state
207 ifaceobj.config[default] = [default_val]
208
209 if not config_val:
210 config_val = default_val
211
212 if config_val and config_val != running_val:
213 try:
214 cmd = ('%s -K %s %s %s' %
215 (utils.ethtool_cmd, ifaceobj.name, eth_name, config_val))
216 utils.exec_command(cmd)
217 except Exception as e:
218 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
219
220 self.ethtool_ignore_errors = policymanager.policymanager_api.get_module_globals(
221 module_name=self.__class__.__name__,
222 attr='ethtool_ignore_errors'
223 )
224
225 def do_fec_settings(self, ifaceobj):
226 feccmd = ''
227
228 # attribute existed before but we must reset to default
229 config_val = ifaceobj.get_attr_value_first('link-fec')
230 default_val = policymanager.policymanager_api.get_iface_default(
231 module_name='ethtool',
232 ifname=ifaceobj.name,
233 attr='link-fec')
234
235 if not default_val and not config_val:
236 # there is no point in checking the running config
237 # if we have no default and the user did not have settings
238 return
239
240 # use only lowercase values
241 running_val = str(self.get_running_attr('fec', ifaceobj)).lower()
242
243 if config_val:
244 config_val = config_val.lower()
245 if default_val:
246 default_val = default_val.lower()
247
248 # check running values
249 if config_val and config_val == running_val:
250 return
251
252 if not config_val and default_val and default_val == running_val:
253 # nothing configured but the default is running
254 return
255
256 # if we got this far, we need to change it
257 if config_val and (config_val != running_val):
258 # if the configured value is not set, set it
259 feccmd = ' %s %s' % ("encoding", config_val)
260 elif default_val and (default_val != running_val):
261 # or if it has a default not equal to running value, set it
262 feccmd = ' %s %s' % ("encoding", default_val)
263
264 if feccmd:
265 try:
266 feccmd = ('%s --set-fec %s %s' %
267 (utils.ethtool_cmd, ifaceobj.name, feccmd))
268 utils.exec_command(feccmd)
269 except Exception as e:
270 if not self.ethtool_ignore_errors:
271 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
272 else:
273 pass
274
275 def do_speed_settings(self, ifaceobj, operation='post_up'):
276 cmd = ''
277
278 autoneg_to_configure = None
279 speed_to_configure = None
280 duplex_to_configure = None
281
282 config_speed = ifaceobj.get_attr_value_first('link-speed')
283 config_duplex = ifaceobj.get_attr_value_first('link-duplex')
284 config_autoneg = ifaceobj.get_attr_value_first('link-autoneg')
285
286 default_speed = policymanager.policymanager_api.get_iface_default(
287 module_name='ethtool',
288 ifname=ifaceobj.name,
289 attr='link-speed'
290 )
291
292 default_duplex = policymanager.policymanager_api.get_iface_default(
293 module_name='ethtool',
294 ifname=ifaceobj.name,
295 attr='link-duplex'
296 )
297
298 default_autoneg = policymanager.policymanager_api.get_iface_default(
299 module_name='ethtool',
300 ifname=ifaceobj.name,
301 attr='link-autoneg'
302 )
303
304 # autoneg wins if provided by user and is on
305 if config_autoneg and utils.get_boolean_from_string(config_autoneg):
306 autoneg_to_configure = config_autoneg
307 speed_to_configure = None
308 duplex_to_configure = None
309 elif config_speed:
310 # Any speed settings configured by the user wins
311 autoneg_to_configure = None
312 speed_to_configure = config_speed
313 duplex_to_configure = config_duplex
314 if not config_duplex:
315 duplex_to_configure = default_duplex
316 else:
317 # if user given autoneg config is off, we must respect that and
318 # override any default autoneg config
319 if config_autoneg and not utils.get_boolean_from_string(config_autoneg):
320 default_autoneg = 'off'
321
322 if default_autoneg and utils.get_boolean_from_string(default_autoneg):
323 autoneg_to_configure = utils.get_onoff_bool(default_autoneg)
324 speed_to_configure = None
325 duplex_to_configure = None
326 else:
327 autoneg_to_configure = None
328 speed_to_configure = default_speed
329 duplex_to_configure = default_duplex
330
331 if autoneg_to_configure:
332 autoneg_to_configure = utils.get_onoff_bool(autoneg_to_configure)
333 # check running values
334 running_val = self.get_running_attr('autoneg', ifaceobj)
335 if autoneg_to_configure != running_val:
336 # if the configured value is not set, set it
337 cmd += ' autoneg %s' % autoneg_to_configure
338 else:
339 force_set = False
340 if speed_to_configure:
341 # check running values
342 if utils.get_boolean_from_string(self.get_running_attr('autoneg', ifaceobj) or 'off'):
343 cmd = 'autoneg off'
344 # if we are transitioning from autoneg 'on' to 'off'
345 # don't check running speed
346 force_set = True
347
348 running_val = self.get_running_attr('speed', ifaceobj)
349 if force_set or (speed_to_configure != running_val):
350 # if the configured value is not set, set it
351 cmd += ' speed %s' % speed_to_configure
352
353 if duplex_to_configure:
354 # check running values
355 running_val = self.get_running_attr('duplex', ifaceobj)
356 if force_set or (duplex_to_configure != running_val):
357 # if the configured value is not set, set it
358 cmd += ' duplex %s' % duplex_to_configure
359
360 if cmd:
361 try:
362 cmd = ('%s -s %s %s' % (utils.ethtool_cmd, ifaceobj.name, cmd))
363 utils.exec_command(cmd)
364 except Exception as e:
365 if not self.ethtool_ignore_errors:
366 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
367
368 def _pre_up(self, ifaceobj, operation='post_up'):
369 """
370 _pre_up and _pre_down will reset the layer 2 attributes to default policy
371 settings.
372 """
373 if not self.cache.link_exists(ifaceobj.name):
374 return
375
376 self.do_speed_settings(ifaceobj)
377 self.do_fec_settings(ifaceobj)
378 self.do_ring_settings(ifaceobj, 'ring-rx', 'rx')
379 self.do_ring_settings(ifaceobj, 'ring-tx', 'tx')
380 self.do_offload_settings(ifaceobj, 'gro-offload', 'gro')
381 self.do_offload_settings(ifaceobj, 'lro-offload', 'lro')
382 self.do_offload_settings(ifaceobj, 'gso-offload', 'gso')
383 self.do_offload_settings(ifaceobj, 'tso-offload', 'tso')
384 self.do_offload_settings(ifaceobj, 'ufo-offload', 'ufo')
385 self.do_offload_settings(ifaceobj, 'tx-offload', 'tx')
386 self.do_offload_settings(ifaceobj, 'rx-offload', 'rx')
387
388 def _pre_down(self, ifaceobj):
389 pass #self._post_up(ifaceobj,operation="_pre_down")
390
391 def _query_check(self, ifaceobj, ifaceobjcurr):
392 """
393 _query_check() needs to compare the configured (or running)
394 attribute with the running attribute.
395
396 If there is nothing configured, we compare the default attribute with
397 the running attribute and FAIL if they are different.
398 This is because a reboot will lose their running attribute
399 (the default will get set).
400 """
401 for attr in ['speed', 'duplex', 'autoneg', 'fec']:
402 configured = ifaceobj.get_attr_value_first('link-%s'%attr)
403 # if there is nothing configured, do not check
404 if not configured:
405 if not ifupdownflags.flags.WITHDEFAULTS:
406 continue
407 default = policymanager.policymanager_api.get_iface_default(
408 module_name='ethtool',
409 ifname=ifaceobj.name,
410 attr='link-%s'%attr)
411 # if we have no default, do not bother checking
412 # this avoids ethtool calls on virtual interfaces
413 if not default:
414 continue
415 # autoneg comes from ethtool whereas speed and duplex from /sys/class
416 running_attr = self.get_running_attr(attr, ifaceobj)
417 if not running_attr:
418 if not configured:
419 continue
420 ifaceobjcurr.update_config_with_status('link-%s' % attr,
421 'unknown', 1)
422 continue
423
424 if attr == 'autoneg':
425 if configured == 'yes' and running_attr == 'on':
426 running_attr = 'yes'
427 elif configured == 'no' and running_attr == 'off':
428 running_attr = 'no'
429
430 # we make sure we can get a running value first
431 if (running_attr and configured and running_attr == configured):
432 # PASS since running is what is configured
433 ifaceobjcurr.update_config_with_status('link-%s'%attr,
434 running_attr, 0)
435 elif (running_attr and configured and running_attr != configured):
436 # We show a FAIL since it is not the configured or default
437 ifaceobjcurr.update_config_with_status('link-%s'%attr,
438 running_attr, 1)
439 elif (running_attr and default and running_attr == default):
440 # PASS since running is default
441 ifaceobjcurr.update_config_with_status('link-%s'%attr,
442 running_attr, 0)
443 elif (default or configured):
444 # We show a FAIL since it is not the configured or default
445 ifaceobjcurr.update_config_with_status('link-%s'%attr,
446 running_attr, 1)
447 return
448
449 def get_autoneg(self,ethtool_output=None):
450 """
451 get_autoneg simply calls the ethtool command and parses out
452 the autoneg value.
453 """
454 ethtool_attrs = ethtool_output.split()
455 if ('Auto-negotiation:' in ethtool_attrs):
456 return(ethtool_attrs[ethtool_attrs.index('Auto-negotiation:')+1])
457 else:
458 return(None)
459
460 def get_fec_encoding(self,ethtool_output=None):
461 """
462 get_fec_encoding simply calls the ethtool show-fec command and parses out
463 the fec encoding value.
464 """
465 try:
466 for attr in ethtool_output.splitlines():
467 if attr.startswith('Configured FEC encodings:'):
468 fec_attrs = attr.split()
469 return(fec_attrs[fec_attrs.index('encodings:')+1])
470 except Exception as e:
471 self.logger.debug('ethtool: problems in ethtool set-fec output'
472 ' %s: %s' %(ethtool_output.splitlines(), str(e)))
473
474 return(None)
475
476 def get_offload_setting(self, ethtool_output, setting):
477
478 value = None
479
480 for line in ethtool_output.splitlines():
481 if setting in line:
482 if 'on' in line:
483 value = 'on'
484 elif 'off' in line:
485 value = 'off'
486
487 break
488
489 return value
490
491 def get_ring_setting(self, ethtool_output, setting, get_ring_max=False):
492 value = None
493
494 if get_ring_max:
495 settings = ethtool_output.split('Current hardware settings:', 1)[0]
496 else:
497 settings = ethtool_output.split('Current hardware settings:', 1)[1]
498
499 for line in settings.splitlines():
500 if line.startswith(setting):
501 value = line.split(':', 1)[1]
502 return value.strip()
503
504 return value
505
506 def get_attr_value(self,attr='',ifaceobj=None,get_max=False):
507 if not ifaceobj or not attr:
508 return
509 attr_value = None
510 try:
511 if attr == 'autoneg':
512 output = utils.exec_commandl([utils.ethtool_cmd, ifaceobj.name])
513 attr_value = self.get_autoneg(ethtool_output=output)
514 elif attr == 'ring-rx':
515 output = utils.exec_command('%s --show-ring %s'%
516 (utils.ethtool_cmd, ifaceobj.name))
517 attr_value = self.get_ring_setting(ethtool_output=output, setting='RX:', get_ring_max=get_max)
518 elif attr == 'ring-tx':
519 output = utils.exec_command('%s --show-ring %s'%
520 (utils.ethtool_cmd, ifaceobj.name))
521 attr_value = self.get_ring_setting(ethtool_output=output, setting='TX:', get_ring_max=get_max)
522 elif attr == 'fec':
523 output = utils.exec_command('%s --show-fec %s'%
524 (utils.ethtool_cmd, ifaceobj.name))
525 attr_value = self.get_fec_encoding(ethtool_output=output)
526 elif attr == 'gro':
527 if not self.feature_cache:
528 self.feature_cache = utils.exec_command('%s --show-features %s'%
529 (utils.ethtool_cmd, ifaceobj.name))
530 attr_value = self.get_offload_setting(ethtool_output=self.feature_cache, setting='generic-receive-offload')
531 elif attr == 'lro':
532 if not self.feature_cache:
533 self.feature_cache = utils.exec_command('%s --show-features %s'%
534 (utils.ethtool_cmd, ifaceobj.name))
535 attr_value = self.get_offload_setting(ethtool_output=self.feature_cache, setting='large-receive-offload')
536 elif attr == 'gso':
537 if not self.feature_cache:
538 self.feature_cache = utils.exec_command('%s --show-features %s'%
539 (utils.ethtool_cmd, ifaceobj.name))
540 attr_value = self.get_offload_setting(ethtool_output=self.feature_cache, setting='generic-segmentation-offload')
541 elif attr == 'tso':
542 if not self.feature_cache:
543 self.feature_cache = utils.exec_command('%s --show-features %s'%
544 (utils.ethtool_cmd, ifaceobj.name))
545 attr_value = self.get_offload_setting(ethtool_output=self.feature_cache, setting='tcp-segmentation-offload')
546 elif attr == 'ufo':
547 if not self.feature_cache:
548 self.feature_cache = utils.exec_command('%s --show-features %s'%
549 (utils.ethtool_cmd, ifaceobj.name))
550 attr_value = self.get_offload_setting(ethtool_output=self.feature_cache, setting='udp-fragmentation-offload')
551 elif attr == 'rx':
552 if not self.feature_cache:
553 self.feature_cache = utils.exec_command('%s --show-features %s'%
554 (utils.ethtool_cmd, ifaceobj.name))
555 attr_value = self.get_offload_setting(ethtool_output=self.feature_cache, setting='rx-checksumming')
556 elif attr == 'tx':
557 if not self.feature_cache:
558 self.feature_cache = utils.exec_command('%s --show-features %s'%
559 (utils.ethtool_cmd, ifaceobj.name))
560 attr_value = self.get_offload_setting(ethtool_output=self.feature_cache, setting='tx-checksumming')
561 else:
562 attr_value = self.io.read_file_oneline('/sys/class/net/%s/%s' % \
563 (ifaceobj.name, attr))
564 except Exception as e:
565 if not self.ethtool_ignore_errors:
566 # for nonexistent interfaces, we get an error (rc = 256 or 19200)
567 self.logger.debug('ethtool: problems calling ethtool or reading'
568 ' /sys/class on iface %s for attr %s: %s' %
569 (ifaceobj.name, attr, str(e)))
570 return attr_value
571
572 def get_running_attr(self,attr='',ifaceobj=None):
573 return self.get_attr_value(attr=attr, ifaceobj=ifaceobj, get_max=False)
574
575 def get_max_attr(self,attr='',ifaceobj=None):
576 return self.get_attr_value(attr=attr, ifaceobj=ifaceobj, get_max=True)
577
578 def _query_running(self, ifaceobj, ifaceobj_getfunc=None):
579 """
580 _query_running looks at the speed and duplex from /sys/class
581 and retreives autoneg from ethtool. We do not report autoneg
582 if speed is not available because this usually means the link is
583 down and the autoneg value is not reliable when the link is down.
584 """
585 # do not bother showing swp ifaces that are not up for the speed
586 # duplex and autoneg are not reliable.
587 if not self.cache.link_is_up(ifaceobj.name):
588 return
589 for attr in ['speed', 'duplex', 'autoneg']:
590 default_val = policymanager.policymanager_api.get_iface_default(
591 module_name='ethtool',
592 ifname=ifaceobj.name,
593 attr='link-%s'%attr)
594 # do not continue if we have no defaults
595 # this avoids ethtool calls on virtual interfaces
596 if not default_val:
597 continue
598 running_attr = self.get_running_attr(attr, ifaceobj)
599
600 # Only show the link attributes if they differ from defaults
601 # to see the defaults, we should implement another flag (--with-defaults)
602 if default_val == running_attr:
603 continue
604
605 # do not proceed if speed = 0
606 if attr == 'speed' and running_attr and running_attr == '0':
607 return
608 if running_attr:
609 ifaceobj.update_config('link-%s'%attr, running_attr)
610
611 return
612
613 def _query(self, ifaceobj, **kwargs):
614 """ add default policy attributes supported by the module """
615 for attr in ['speed', 'duplex', 'autoneg', 'fec']:
616 if ifaceobj.get_attr_value_first('link-%s'%attr):
617 continue
618 default = policymanager.policymanager_api.get_iface_default(
619 module_name='ethtool',
620 ifname=ifaceobj.name,
621 attr='link-%s' %attr)
622 if not default:
623 continue
624 ifaceobj.update_config('link-%s' %attr, default)
625
626 _run_ops = {'pre-down' : _pre_down,
627 'pre-up' : _pre_up,
628 'query-checkcurr' : _query_check,
629 'query-running' : _query_running,
630 'query' : _query}
631
632 def get_ops(self):
633 """ returns list of ops supported by this module """
634 return list(self._run_ops.keys())
635
636 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
637 """ run ethtool configuration on the interface object passed as
638 argument
639
640 Args:
641 **ifaceobj** (object): iface object
642
643 **operation** (str): any of 'post-up', 'query-checkcurr',
644 'query-running'
645 Kwargs:
646 **query_ifaceobj** (object): query check ifaceobject. This is only
647 valid when op is 'query-checkcurr'. It is an object same as
648 ifaceobj, but contains running attribute values and its config
649 status. The modules can use it to return queried running state
650 of interfaces. status is success if the running state is same
651 as user required state in ifaceobj. error otherwise.
652 """
653 if (ifaceobj.link_kind or
654 ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK):
655 return
656 op_handler = self._run_ops.get(operation)
657 if not op_handler:
658 return
659 if operation == 'query-checkcurr':
660 op_handler(self, ifaceobj, query_ifaceobj)
661 else:
662 op_handler(self, ifaceobj)