1# Copyright 2015 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import time 7import sys 8 9from multiprocessing import Process 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros.faft.utils import shell_wrapper 13 14class ConnectionError(Exception): 15 """Raised on an error of connecting DUT.""" 16 pass 17 18 19class _BaseFwBypasser(object): 20 """Base class that controls bypass logic for firmware screens.""" 21 22 def __init__(self, faft_framework): 23 self.servo = faft_framework.servo 24 self.faft_config = faft_framework.faft_config 25 self.client_host = faft_framework._client 26 27 28 def bypass_dev_mode(self): 29 """Bypass the dev mode firmware logic to boot internal image.""" 30 raise NotImplementedError 31 32 33 def bypass_dev_boot_usb(self): 34 """Bypass the dev mode firmware logic to boot USB.""" 35 raise NotImplementedError 36 37 38 def bypass_rec_mode(self): 39 """Bypass the rec mode firmware logic to boot USB.""" 40 raise NotImplementedError 41 42 43 def trigger_dev_to_rec(self): 44 """Trigger to the rec mode from the dev screen.""" 45 raise NotImplementedError 46 47 48 def trigger_rec_to_dev(self): 49 """Trigger to the dev mode from the rec screen.""" 50 raise NotImplementedError 51 52 53 def trigger_dev_to_normal(self): 54 """Trigger to the normal mode from the dev screen.""" 55 raise NotImplementedError 56 57 58class _CtrlDBypasser(_BaseFwBypasser): 59 """Controls bypass logic via Ctrl-D combo.""" 60 61 def bypass_dev_mode(self): 62 """Bypass the dev mode firmware logic to boot internal image.""" 63 time.sleep(self.faft_config.firmware_screen) 64 self.servo.ctrl_d() 65 66 67 def bypass_dev_boot_usb(self): 68 """Bypass the dev mode firmware logic to boot USB.""" 69 time.sleep(self.faft_config.firmware_screen) 70 self.servo.ctrl_u() 71 72 73 def bypass_rec_mode(self): 74 """Bypass the rec mode firmware logic to boot USB.""" 75 self.servo.switch_usbkey('host') 76 time.sleep(self.faft_config.usb_plug) 77 self.servo.switch_usbkey('dut') 78 if not self.client_host.ping_wait_up( 79 timeout=self.faft_config.delay_reboot_to_ping): 80 psc = self.servo.get_power_state_controller() 81 psc.power_on(psc.REC_ON) 82 83 84 def trigger_dev_to_rec(self): 85 """Trigger to the rec mode from the dev screen.""" 86 time.sleep(self.faft_config.firmware_screen) 87 88 # Pressing Enter for too long triggers a second key press. 89 # Let's press it without delay 90 self.servo.enter_key(press_secs=0) 91 92 # For Alex/ZGB, there is a dev warning screen in text mode. 93 # Skip it by pressing Ctrl-D. 94 if self.faft_config.need_dev_transition: 95 time.sleep(self.faft_config.legacy_text_screen) 96 self.servo.ctrl_d() 97 98 99 def trigger_rec_to_dev(self): 100 """Trigger to the dev mode from the rec screen.""" 101 time.sleep(self.faft_config.firmware_screen) 102 self.servo.ctrl_d() 103 time.sleep(self.faft_config.confirm_screen) 104 if self.faft_config.rec_button_dev_switch: 105 logging.info('RECOVERY button pressed to switch to dev mode') 106 self.servo.toggle_recovery_switch() 107 else: 108 logging.info('ENTER pressed to switch to dev mode') 109 self.servo.enter_key() 110 111 112 def trigger_dev_to_normal(self): 113 """Trigger to the normal mode from the dev screen.""" 114 time.sleep(self.faft_config.firmware_screen) 115 self.servo.enter_key() 116 time.sleep(self.faft_config.confirm_screen) 117 self.servo.enter_key() 118 119 120class _JetstreamBypasser(_BaseFwBypasser): 121 """Controls bypass logic of Jetstream devices.""" 122 123 def bypass_dev_mode(self): 124 """Bypass the dev mode firmware logic to boot internal image.""" 125 # Jetstream does nothing to bypass. 126 pass 127 128 129 def bypass_dev_boot_usb(self): 130 """Bypass the dev mode firmware logic to boot USB.""" 131 self.servo.switch_usbkey('dut') 132 time.sleep(self.faft_config.firmware_screen) 133 self.servo.toggle_development_switch() 134 135 136 def bypass_rec_mode(self): 137 """Bypass the rec mode firmware logic to boot USB.""" 138 self.servo.switch_usbkey('host') 139 time.sleep(self.faft_config.usb_plug) 140 self.servo.switch_usbkey('dut') 141 if not self.client_host.ping_wait_up( 142 timeout=self.faft_config.delay_reboot_to_ping): 143 psc = self.servo.get_power_state_controller() 144 psc.power_on(psc.REC_ON) 145 146 147 def trigger_dev_to_rec(self): 148 """Trigger to the rec mode from the dev screen.""" 149 # Jetstream does not have this triggering logic. 150 raise NotImplementedError 151 152 153 def trigger_rec_to_dev(self): 154 """Trigger to the dev mode from the rec screen.""" 155 self.servo.disable_development_mode() 156 time.sleep(self.faft_config.firmware_screen) 157 self.servo.toggle_development_switch() 158 159 160 def trigger_dev_to_normal(self): 161 """Trigger to the normal mode from the dev screen.""" 162 # Jetstream does not have this triggering logic. 163 raise NotImplementedError 164 165 166def _create_fw_bypasser(faft_framework): 167 """Creates a proper firmware bypasser. 168 169 @param faft_framework: The main FAFT framework object. 170 """ 171 bypasser_type = faft_framework.faft_config.fw_bypasser_type 172 if bypasser_type == 'ctrl_d_bypasser': 173 logging.info('Create a CtrlDBypasser') 174 return _CtrlDBypasser(faft_framework) 175 elif bypasser_type == 'jetstream_bypasser': 176 logging.info('Create a JetstreamBypasser') 177 return _JetstreamBypasser(faft_framework) 178 elif bypasser_type == 'ryu_bypasser': 179 # FIXME Create an RyuBypasser 180 logging.info('Create a CtrlDBypasser') 181 return _CtrlDBypasser(faft_framework) 182 else: 183 raise NotImplementedError('Not supported fw_bypasser_type: %s', 184 bypasser_type) 185 186 187class _BaseModeSwitcher(object): 188 """Base class that controls firmware mode switching.""" 189 190 def __init__(self, faft_framework): 191 self.faft_framework = faft_framework 192 self.client_host = faft_framework._client 193 self.faft_client = faft_framework.faft_client 194 self.servo = faft_framework.servo 195 self.faft_config = faft_framework.faft_config 196 self.checkers = faft_framework.checkers 197 self.bypasser = _create_fw_bypasser(faft_framework) 198 self._backup_mode = None 199 200 201 def setup_mode(self, mode): 202 """Setup for the requested mode. 203 204 It makes sure the system in the requested mode. If not, it tries to 205 do so. 206 207 @param mode: A string of mode, one of 'normal', 'dev', or 'rec'. 208 """ 209 if not self.checkers.mode_checker(mode): 210 logging.info('System not in expected %s mode. Reboot into it.', 211 mode) 212 if self._backup_mode is None: 213 # Only resume to normal/dev mode after test, not recovery. 214 self._backup_mode = 'dev' if mode == 'normal' else 'normal' 215 self.reboot_to_mode(mode) 216 217 218 def restore_mode(self): 219 """Restores original dev mode status if it has changed.""" 220 if self._backup_mode is not None: 221 self.reboot_to_mode(self._backup_mode) 222 223 224 def reboot_to_mode(self, to_mode, from_mode=None, sync_before_boot=True, 225 wait_for_dut_up=True): 226 """Reboot and execute the mode switching sequence. 227 228 @param to_mode: The target mode, one of 'normal', 'dev', or 'rec'. 229 @param from_mode: The original mode, optional, one of 'normal, 'dev', 230 or 'rec'. 231 @param sync_before_boot: True to sync to disk before booting. 232 @param wait_for_dut_up: True to wait DUT online again. False to do the 233 reboot and mode switching sequence only and may 234 need more operations to pass the firmware 235 screen. 236 """ 237 logging.info('-[ModeSwitcher]-[ start reboot_to_mode(%r, %r, %r) ]-', 238 to_mode, from_mode, wait_for_dut_up) 239 if sync_before_boot: 240 self.faft_framework.blocking_sync() 241 if to_mode == 'rec': 242 self._enable_rec_mode_and_reboot(usb_state='dut') 243 if wait_for_dut_up: 244 self.wait_for_client() 245 246 elif to_mode == 'dev': 247 self._enable_dev_mode_and_reboot() 248 if wait_for_dut_up: 249 self.bypass_dev_mode() 250 self.wait_for_client() 251 252 elif to_mode == 'normal': 253 self._enable_normal_mode_and_reboot() 254 if wait_for_dut_up: 255 self.wait_for_client() 256 257 else: 258 raise NotImplementedError( 259 'Not supported mode switching from %s to %s' % 260 (str(from_mode), to_mode)) 261 logging.info('-[ModeSwitcher]-[ end reboot_to_mode(%r, %r, %r) ]-', 262 to_mode, from_mode, wait_for_dut_up) 263 264 def simple_reboot(self, reboot_type='warm', sync_before_boot=True): 265 """Simple reboot method 266 267 Just reboot the DUT using either cold or warm reset. Does not wait for 268 DUT to come back online. Will wait for test to handle this. 269 270 @param reboot_type: A string of reboot type, 'warm' or 'cold'. 271 If reboot_type != warm/cold, raise exception. 272 @param sync_before_boot: True to sync to disk before booting. 273 If sync_before_boot=False, DUT offline before 274 calling mode_aware_reboot. 275 """ 276 if reboot_type == 'warm': 277 reboot_method = self.servo.get_power_state_controller().warm_reset 278 elif reboot_type == 'cold': 279 reboot_method = self.servo.get_power_state_controller().reset 280 else: 281 raise NotImplementedError('Not supported reboot_type: %s', 282 reboot_type) 283 if sync_before_boot: 284 boot_id = self.faft_framework.get_bootid() 285 self.faft_framework.blocking_sync() 286 logging.info("-[ModeSwitcher]-[ start simple_reboot(%r) ]-", 287 reboot_type) 288 reboot_method() 289 if sync_before_boot: 290 self.wait_for_client_offline(orig_boot_id=boot_id) 291 logging.info("-[ModeSwitcher]-[ end simple_reboot(%r) ]-", 292 reboot_type) 293 294 def mode_aware_reboot(self, reboot_type=None, reboot_method=None, 295 sync_before_boot=True, wait_for_dut_up=True): 296 """Uses a mode-aware way to reboot DUT. 297 298 For example, if DUT is in dev mode, it requires pressing Ctrl-D to 299 bypass the developer screen. 300 301 @param reboot_type: A string of reboot type, one of 'warm', 'cold', or 302 'custom'. Default is a warm reboot. 303 @param reboot_method: A custom method to do the reboot. Only use it if 304 reboot_type='custom'. 305 @param sync_before_boot: True to sync to disk before booting. 306 If sync_before_boot=False, DUT offline before 307 calling mode_aware_reboot. 308 @param wait_for_dut_up: True to wait DUT online again. False to do the 309 reboot only. 310 """ 311 if reboot_type is None or reboot_type == 'warm': 312 reboot_method = self.servo.get_power_state_controller().warm_reset 313 elif reboot_type == 'cold': 314 reboot_method = self.servo.get_power_state_controller().reset 315 elif reboot_type != 'custom': 316 raise NotImplementedError('Not supported reboot_type: %s', 317 reboot_type) 318 319 logging.info("-[ModeSwitcher]-[ start mode_aware_reboot(%r, %s, ..) ]-", 320 reboot_type, reboot_method.__name__) 321 is_dev = False 322 if sync_before_boot: 323 is_dev = self.checkers.mode_checker('dev') 324 boot_id = self.faft_framework.get_bootid() 325 self.faft_framework.blocking_sync() 326 logging.info("-[mode_aware_reboot]-[ is_dev=%s ]-", is_dev); 327 reboot_method() 328 if sync_before_boot: 329 self.wait_for_client_offline(orig_boot_id=boot_id) 330 # Encapsulating the behavior of skipping dev firmware screen, 331 # hitting ctrl-D 332 # Note that if booting from recovery mode, will not 333 # call bypass_dev_mode because can't determine prior to 334 # reboot if we're going to boot up in dev or normal mode. 335 if is_dev: 336 self.bypass_dev_mode() 337 if wait_for_dut_up: 338 self.wait_for_client() 339 logging.info("-[ModeSwitcher]-[ end mode_aware_reboot(%r, %s, ..) ]-", 340 reboot_type, reboot_method.__name__) 341 342 343 def _enable_rec_mode_and_reboot(self, usb_state=None): 344 """Switch to rec mode and reboot. 345 346 This method emulates the behavior of the old physical recovery switch, 347 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled 348 recovery mode, i.e. just press Power + Esc + Refresh. 349 350 @param usb_state: A string, one of 'dut', 'host', or 'off'. 351 """ 352 psc = self.servo.get_power_state_controller() 353 psc.power_off() 354 if usb_state: 355 self.servo.switch_usbkey(usb_state) 356 psc.power_on(psc.REC_ON) 357 358 359 def _disable_rec_mode_and_reboot(self, usb_state=None): 360 """Disable the rec mode and reboot. 361 362 It is achieved by calling power state controller to do a normal 363 power on. 364 """ 365 psc = self.servo.get_power_state_controller() 366 psc.power_off() 367 time.sleep(self.faft_config.ec_boot_to_pwr_button) 368 psc.power_on(psc.REC_OFF) 369 370 371 def _enable_dev_mode_and_reboot(self): 372 """Switch to developer mode and reboot.""" 373 raise NotImplementedError 374 375 376 def _enable_normal_mode_and_reboot(self): 377 """Switch to normal mode and reboot.""" 378 raise NotImplementedError 379 380 381 # Redirects the following methods to FwBypasser 382 def bypass_dev_mode(self): 383 """Bypass the dev mode firmware logic to boot internal image.""" 384 logging.info("-[bypass_dev_mode]-") 385 self.bypasser.bypass_dev_mode() 386 387 388 def bypass_dev_boot_usb(self): 389 """Bypass the dev mode firmware logic to boot USB.""" 390 logging.info("-[bypass_dev_boot_usb]-") 391 self.bypasser.bypass_dev_boot_usb() 392 393 394 def bypass_rec_mode(self): 395 """Bypass the rec mode firmware logic to boot USB.""" 396 logging.info("-[bypass_rec_mode]-") 397 self.bypasser.bypass_rec_mode() 398 399 400 def trigger_dev_to_rec(self): 401 """Trigger to the rec mode from the dev screen.""" 402 self.bypasser.trigger_dev_to_rec() 403 404 405 def trigger_rec_to_dev(self): 406 """Trigger to the dev mode from the rec screen.""" 407 self.bypasser.trigger_rec_to_dev() 408 409 410 def trigger_dev_to_normal(self): 411 """Trigger to the normal mode from the dev screen.""" 412 self.bypasser.trigger_dev_to_normal() 413 414 415 def wait_for_client(self, timeout=180): 416 """Wait for the client to come back online. 417 418 New remote processes will be launched if their used flags are enabled. 419 420 @param timeout: Time in seconds to wait for the client SSH daemon to 421 come up. 422 @raise ConnectionError: Failed to connect DUT. 423 """ 424 logging.info("-[FAFT]-[ start wait_for_client ]---") 425 # Wait for the system to respond to ping before attempting ssh 426 if not self.client_host.ping_wait_up(timeout): 427 logging.warning("-[FAFT]-[ system did not respond to ping ]") 428 if self.client_host.wait_up(timeout): 429 # Check the FAFT client is avaiable. 430 self.faft_client.system.is_available() 431 # Stop update-engine as it may change firmware/kernel. 432 self.faft_framework._stop_service('update-engine') 433 else: 434 logging.error('wait_for_client() timed out.') 435 raise ConnectionError() 436 logging.info("-[FAFT]-[ end wait_for_client ]-----") 437 438 439 def wait_for_client_offline(self, timeout=60, orig_boot_id=None): 440 """Wait for the client to come offline. 441 442 @param timeout: Time in seconds to wait the client to come offline. 443 @param orig_boot_id: A string containing the original boot id. 444 @raise ConnectionError: Failed to wait DUT offline. 445 """ 446 # When running against panther, we see that sometimes 447 # ping_wait_down() does not work correctly. There needs to 448 # be some investigation to the root cause. 449 # If we sleep for 120s before running get_boot_id(), it 450 # does succeed. But if we change this to ping_wait_down() 451 # there are implications on the wait time when running 452 # commands at the fw screens. 453 if not self.client_host.ping_wait_down(timeout): 454 if orig_boot_id and self.client_host.get_boot_id() != orig_boot_id: 455 logging.warn('Reboot done very quickly.') 456 return 457 raise ConnectionError() 458 459 460class _PhysicalButtonSwitcher(_BaseModeSwitcher): 461 """Class that switches firmware mode via physical button.""" 462 463 def _enable_dev_mode_and_reboot(self): 464 """Switch to developer mode and reboot.""" 465 self.servo.enable_development_mode() 466 self.faft_client.system.run_shell_command( 467 'chromeos-firmwareupdate --mode todev && reboot') 468 469 470 def _enable_normal_mode_and_reboot(self): 471 """Switch to normal mode and reboot.""" 472 self.servo.disable_development_mode() 473 self.faft_client.system.run_shell_command( 474 'chromeos-firmwareupdate --mode tonormal && reboot') 475 476 477class _KeyboardDevSwitcher(_BaseModeSwitcher): 478 """Class that switches firmware mode via keyboard combo.""" 479 480 def _enable_dev_mode_and_reboot(self): 481 """Switch to developer mode and reboot.""" 482 logging.info("Enabling keyboard controlled developer mode") 483 # Rebooting EC with rec mode on. Should power on AP. 484 # Plug out USB disk for preventing recovery boot without warning 485 self._enable_rec_mode_and_reboot(usb_state='host') 486 self.wait_for_client_offline() 487 self.bypasser.trigger_rec_to_dev() 488 489 490 def _enable_normal_mode_and_reboot(self): 491 """Switch to normal mode and reboot.""" 492 logging.info("Disabling keyboard controlled developer mode") 493 self._disable_rec_mode_and_reboot() 494 self.wait_for_client_offline() 495 self.bypasser.trigger_dev_to_normal() 496 497 498class _JetstreamSwitcher(_BaseModeSwitcher): 499 """Class that switches firmware mode in Jetstream devices.""" 500 501 def _enable_dev_mode_and_reboot(self): 502 """Switch to developer mode and reboot.""" 503 logging.info("Enabling Jetstream developer mode") 504 self._enable_rec_mode_and_reboot(usb_state='host') 505 self.wait_for_client_offline() 506 self.bypasser.trigger_rec_to_dev() 507 508 509 def _enable_normal_mode_and_reboot(self): 510 """Switch to normal mode and reboot.""" 511 logging.info("Disabling Jetstream developer mode") 512 self.servo.disable_development_mode() 513 self._enable_rec_mode_and_reboot(usb_state='host') 514 time.sleep(self.faft_config.firmware_screen) 515 self._disable_rec_mode_and_reboot(usb_state='host') 516 517 518class _RyuSwitcher(_BaseModeSwitcher): 519 """Class that switches firmware mode via physical button.""" 520 521 FASTBOOT_OEM_DELAY = 10 522 RECOVERY_TIMEOUT = 2400 523 RECOVERY_SETUP = 60 524 ANDROID_BOOTUP = 600 525 FWTOOL_STARTUP_DELAY = 30 526 527 def wait_for_client(self, timeout=180): 528 """Wait for the client to come back online. 529 530 New remote processes will be launched if their used flags are enabled. 531 532 @param timeout: Time in seconds to wait for the client SSH daemon to 533 come up. 534 @raise ConnectionError: Failed to connect DUT. 535 """ 536 if not self.faft_client.system.wait_for_client(timeout): 537 raise ConnectionError() 538 539 # there's a conflict between fwtool and crossystem trying to access 540 # the nvram after the OS boots up. Temporarily put a hard wait of 541 # 30 seconds to try to wait for fwtool to finish up. 542 time.sleep(self.FWTOOL_STARTUP_DELAY) 543 544 545 def wait_for_client_offline(self, timeout=60, orig_boot_id=None): 546 """Wait for the client to come offline. 547 548 @param timeout: Time in seconds to wait the client to come offline. 549 @param orig_boot_id: A string containing the original boot id. 550 @raise ConnectionError: Failed to wait DUT offline. 551 """ 552 # TODO: Add a way to check orig_boot_id 553 if not self.faft_client.system.wait_for_client_offline(timeout): 554 raise ConnectionError() 555 556 def print_recovery_warning(self): 557 """Print recovery warning""" 558 logging.info("***") 559 logging.info("*** Entering recovery mode. This may take awhile ***") 560 logging.info("***") 561 # wait a minute for DUT to get settled into wipe stage 562 time.sleep(self.RECOVERY_SETUP) 563 564 def is_fastboot_mode(self): 565 """Return True if DUT in fastboot mode, False otherwise""" 566 result = self.faft_client.host.run_shell_command_get_output( 567 'fastboot devices') 568 if not result: 569 return False 570 else: 571 return True 572 573 def wait_for_client_fastboot(self, timeout=30): 574 """Wait for the client to come online in fastboot mode 575 576 @param timeout: Time in seconds to wait the client 577 @raise ConnectionError: Failed to wait DUT offline. 578 """ 579 utils.wait_for_value(self.is_fastboot_mode, True, timeout_sec=timeout) 580 581 def _run_cmd(self, args): 582 """Wrapper for run_shell_command 583 584 For Process creation 585 """ 586 return self.faft_client.host.run_shell_command(args) 587 588 def _enable_dev_mode_and_reboot(self): 589 """Switch to developer mode and reboot.""" 590 logging.info("Entering RyuSwitcher: _enable_dev_mode_and_reboot") 591 try: 592 self.faft_client.system.run_shell_command('reboot bootloader') 593 self.wait_for_client_fastboot() 594 595 process = Process( 596 target=self._run_cmd, 597 args=('fastboot oem unlock',)) 598 process.start() 599 600 # need a slight delay to give the ap time to get into valid state 601 time.sleep(self.FASTBOOT_OEM_DELAY) 602 self.servo.power_key(self.faft_config.hold_pwr_button_poweron) 603 process.join() 604 605 self.print_recovery_warning() 606 self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT) 607 self.faft_client.host.run_shell_command('fastboot continue') 608 self.wait_for_client(self.ANDROID_BOOTUP) 609 610 # need to reset DUT into clean state 611 except shell_wrapper.ShellError: 612 raise error.TestError('Error executing shell command') 613 except ConnectionError: 614 raise error.TestError('Timed out waiting for DUT to exit recovery') 615 except: 616 raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0]) 617 logging.info("Exiting RyuSwitcher: _enable_dev_mode_and_reboot") 618 619 def _enable_normal_mode_and_reboot(self): 620 """Switch to normal mode and reboot.""" 621 try: 622 self.faft_client.system.run_shell_command('reboot bootloader') 623 self.wait_for_client_fastboot() 624 625 process = Process( 626 target=self._run_cmd, 627 args=('fastboot oem lock',)) 628 process.start() 629 630 # need a slight delay to give the ap time to get into valid state 631 time.sleep(self.FASTBOOT_OEM_DELAY) 632 self.servo.power_key(self.faft_config.hold_pwr_button_poweron) 633 process.join() 634 635 self.print_recovery_warning() 636 self.wait_for_client_fastboot(self.RECOVERY_TIMEOUT) 637 self.faft_client.host.run_shell_command('fastboot continue') 638 self.wait_for_client(self.ANDROID_BOOTUP) 639 640 # need to reset DUT into clean state 641 except shell_wrapper.ShellError: 642 raise error.TestError('Error executing shell command') 643 except ConnectionError: 644 raise error.TestError('Timed out waiting for DUT to exit recovery') 645 except: 646 raise error.TestError('Unexpected Exception: %s' % sys.exc_info()[0]) 647 logging.info("Exiting RyuSwitcher: _enable_normal_mode_and_reboot") 648 649def create_mode_switcher(faft_framework): 650 """Creates a proper mode switcher. 651 652 @param faft_framework: The main FAFT framework object. 653 """ 654 switcher_type = faft_framework.faft_config.mode_switcher_type 655 if switcher_type == 'physical_button_switcher': 656 logging.info('Create a PhysicalButtonSwitcher') 657 return _PhysicalButtonSwitcher(faft_framework) 658 elif switcher_type == 'keyboard_dev_switcher': 659 logging.info('Create a KeyboardDevSwitcher') 660 return _KeyboardDevSwitcher(faft_framework) 661 elif switcher_type == 'jetstream_switcher': 662 logging.info('Create a JetstreamSwitcher') 663 return _JetstreamSwitcher(faft_framework) 664 elif switcher_type == 'ryu_switcher': 665 logging.info('Create a RyuSwitcher') 666 return _RyuSwitcher(faft_framework) 667 else: 668 raise NotImplementedError('Not supported mode_switcher_type: %s', 669 switcher_type) 670