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