aboutsummaryrefslogtreecommitdiff
path: root/build/fuchsia/test/base_ermine_ctl.py
diff options
context:
space:
mode:
Diffstat (limited to 'build/fuchsia/test/base_ermine_ctl.py')
-rw-r--r--build/fuchsia/test/base_ermine_ctl.py201
1 files changed, 0 insertions, 201 deletions
diff --git a/build/fuchsia/test/base_ermine_ctl.py b/build/fuchsia/test/base_ermine_ctl.py
deleted file mode 100644
index c751986786..0000000000
--- a/build/fuchsia/test/base_ermine_ctl.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Copyright 2022 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Adds python interface to erminectl tools on workstation products."""
-
-import logging
-import subprocess
-import time
-from typing import List, Tuple
-
-
-class BaseErmineCtl:
- """Compatible class for automating control of Ermine and its OOBE.
-
- Must be used after checking if the tool exists.
-
- Usage:
- ctl = base_ermine_ctl.BaseErmineCtl(some_target)
- if ctl.exists:
- ctl.take_to_shell()
-
- logging.info('In the shell')
- else:
- logging.info('Tool does not exist!')
-
- This is only necessary after a target reboot or provision (IE pave).
- """
-
- _OOBE_PASSWORD = 'workstation_test_password'
- _TOOL = 'erminectl'
- _OOBE_SUBTOOL = 'oobe'
- _MAX_STATE_TRANSITIONS = 5
-
- # Mapping between the current state and the next command to run
- # to move it to the next state.
- _STATE_TO_NEXT = {
- 'SetPassword': ['set_password', _OOBE_PASSWORD],
- 'Unknown': ['skip'],
- 'Shell': [],
- 'Login': ['login', _OOBE_PASSWORD],
- }
- _COMPLETE_STATE = 'Shell'
-
- _READY_TIMEOUT = 10
- _WAIT_ATTEMPTS = 10
- _WAIT_FOR_READY_SLEEP_SEC = 3
-
- def __init__(self):
- self._ermine_exists = False
- self._ermine_exists_check = False
-
- # pylint: disable=no-self-use
- # Overridable method to determine how command gets executed.
- def execute_command_async(self, args: List[str]) -> subprocess.Popen:
- """Executes command asynchronously, returning immediately."""
- raise NotImplementedError
-
- # pylint: enable=no-self-use
-
- @property
- def exists(self) -> bool:
- """Returns the existence of the tool.
-
- Checks whether the tool exists on and caches the result.
-
- Returns:
- True if the tool exists, False if not.
- """
- if not self._ermine_exists_check:
- self._ermine_exists = self._execute_tool(['--help'],
- can_fail=True) == 0
- self._ermine_exists_check = True
- logging.debug('erminectl exists: %s',
- ('true' if self._ermine_exists else 'false'))
- return self._ermine_exists
-
- @property
- def status(self) -> Tuple[int, str]:
- """Returns the status of ermine.
-
- Note that if the tool times out or does not exist, a non-zero code
- is returned.
-
- Returns:
- Tuple of (return code, status as string). -1 for timeout.
- Raises:
- AssertionError: if the tool does not exist.
- """
- assert self.exists, (f'Tool {self._TOOL} cannot have a status if'
- ' it does not exist')
- # Executes base command, which returns status.
- proc = self._execute_tool_async([])
- try:
- proc.wait(timeout=self._READY_TIMEOUT)
- except subprocess.TimeoutExpired:
- logging.warning('Timed out waiting for status')
- return -1, 'Timeout'
- stdout, _ = proc.communicate()
- return proc.returncode, stdout.strip()
-
- @property
- def ready(self) -> bool:
- """Indicates if the tool is ready for regular use.
-
- Returns:
- False if not ready, and True if ready.
- Raises:
- AssertionError: if the tool does not exist.
- """
- assert self.exists, (f'Tool {self._TOOL} cannot be ready if'
- ' it does not exist')
- return_code, _ = self.status
- return return_code == 0
-
- def _execute_tool_async(self, command: List[str]) -> subprocess.Popen:
- """Executes a sub-command asynchronously.
-
- Args:
- command: list of strings to compose the command. Forwards to the
- command runner.
- Returns:
- Popen of the subprocess.
- """
- full_command = [self._TOOL, self._OOBE_SUBTOOL]
- full_command.extend(command)
-
- # Returns immediately with Popen.
- return self.execute_command_async(full_command)
-
- def _execute_tool(self, command: List[str], can_fail: bool = False) -> int:
- """Executes a sub-command of the tool synchronously.
- Raises exception if non-zero returncode is given and |can_fail| = False.
-
- Args:
- command: list of strings to compose the command. Forwards to the
- command runner.
- can_fail: Whether or not the command can fail.
- Raises:
- RuntimeError: if non-zero returncode is returned and can_fail =
- False.
- Returns:
- Return code of command execution if |can_fail| is True.
- """
- proc = self._execute_tool_async(command)
- stdout, stderr = proc.communicate()
- if not can_fail and proc.returncode != 0:
- raise RuntimeError(f'Command {" ".join(command)} failed.'
- f'\nSTDOUT: {stdout}\nSTDERR: {stderr}')
- return proc.returncode
-
- def wait_until_ready(self) -> None:
- """Waits until the tool is ready through sleep-poll.
-
- The tool may not be ready after a pave or restart.
- This checks the status and exits after its ready or Timeout.
-
- Raises:
- TimeoutError: if tool is not ready after certain amount of attempts.
- AssertionError: if tool does not exist.
- """
- assert self.exists, f'Tool {self._TOOL} must exist to use it.'
- for _ in range(self._WAIT_ATTEMPTS):
- if self.ready:
- return
- time.sleep(self._WAIT_FOR_READY_SLEEP_SEC)
- raise TimeoutError('Timed out waiting for a valid status to return')
-
- def take_to_shell(self) -> None:
- """Takes device to shell after waiting for tool to be ready.
-
- Examines the current state of the device after waiting for it to be
- ready. Once ready, goes through the states of logging in. This is:
- - CreatePassword -> Skip screen -> Shell
- - Login -> Shell
- - Shell
-
- Regardless of starting state, this will exit once the shell state is
- reached.
-
- Raises:
- NotImplementedError: if an unknown state is reached.
- RuntimeError: If number of state transitions exceeds the max number
- that is expected.
- """
- self.wait_until_ready()
- _, state = self.status
- max_states = self._MAX_STATE_TRANSITIONS
- while state != self._COMPLETE_STATE and max_states:
- max_states -= 1
- command = self._STATE_TO_NEXT.get(state)
- logging.debug('Ermine state is: %s', state)
- if command is None:
- raise NotImplementedError('Encountered invalid state: %s' %
- state)
- self._execute_tool(command)
- _, state = self.status
-
- if not max_states:
- raise RuntimeError('Did not transition to shell in %d attempts.'
- ' Please file a bug.' %
- self._MAX_STATE_TRANSITIONS)