| Index: tools/telemetry/telemetry/cros_interface.py | 
| diff --git a/tools/telemetry/telemetry/cros_interface.py b/tools/telemetry/telemetry/cros_interface.py | 
| deleted file mode 100644 | 
| index 7eb862d7d6a93c416213915210207299edbaae8f..0000000000000000000000000000000000000000 | 
| --- a/tools/telemetry/telemetry/cros_interface.py | 
| +++ /dev/null | 
| @@ -1,410 +0,0 @@ | 
| -# Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
| -# Use of this source code is governed by a BSD-style license that can be | 
| -# found in the LICENSE file. | 
| -"""A wrapper around ssh for common operations on a CrOS-based device""" | 
| -import logging | 
| -import os | 
| -import re | 
| -import subprocess | 
| -import sys | 
| -import time | 
| -import tempfile | 
| - | 
| -from telemetry import util | 
| - | 
| -# TODO(nduca): This whole file is built up around making individual ssh calls | 
| -# for each operation. It really could get away with a single ssh session built | 
| -# around pexpect, I suspect, if we wanted it to be faster. But, this was | 
| -# convenient. | 
| - | 
| -def RunCmd(args, cwd=None, quiet=False): | 
| -  """Opens a subprocess to execute a program and returns its return value. | 
| - | 
| -  Args: | 
| -    args: A string or a sequence of program arguments. The program to execute is | 
| -      the string or the first item in the args sequence. | 
| -    cwd: If not None, the subprocess's current directory will be changed to | 
| -      |cwd| before it's executed. | 
| - | 
| -  Returns: | 
| -    Return code from the command execution. | 
| -  """ | 
| -  if not quiet: | 
| -    logging.debug(' '.join(args) + ' ' + (cwd or '')) | 
| -  with open(os.devnull, 'w') as devnull: | 
| -    p = subprocess.Popen(args=args, cwd=cwd, stdout=devnull, | 
| -                         stderr=devnull, stdin=devnull, shell=False) | 
| -    return p.wait() | 
| - | 
| -def GetAllCmdOutput(args, cwd=None, quiet=False): | 
| -  """Open a subprocess to execute a program and returns its output. | 
| - | 
| -  Args: | 
| -    args: A string or a sequence of program arguments. The program to execute is | 
| -      the string or the first item in the args sequence. | 
| -    cwd: If not None, the subprocess's current directory will be changed to | 
| -      |cwd| before it's executed. | 
| - | 
| -  Returns: | 
| -    Captures and returns the command's stdout. | 
| -    Prints the command's stderr to logger (which defaults to stdout). | 
| -  """ | 
| -  if not quiet: | 
| -    logging.debug(' '.join(args) + ' ' + (cwd or '')) | 
| -  with open(os.devnull, 'w') as devnull: | 
| -    p = subprocess.Popen(args=args, cwd=cwd, stdout=subprocess.PIPE, | 
| -                         stderr=subprocess.PIPE, stdin=devnull, shell=False) | 
| -    stdout, stderr = p.communicate() | 
| -    if not quiet: | 
| -      logging.debug(' > stdout=[%s], stderr=[%s]', stdout, stderr) | 
| -    return stdout, stderr | 
| - | 
| -class DeviceSideProcess(object): | 
| -  def __init__(self, | 
| -               cri, | 
| -               device_side_args, | 
| -               prevent_output=True, | 
| -               extra_ssh_args=None, | 
| -               leave_ssh_alive=False, | 
| -               env=None, | 
| -               login_shell=False): | 
| - | 
| -    # Init members first so that Close will always succeed. | 
| -    self._cri = cri | 
| -    self._proc = None | 
| -    self._devnull = open(os.devnull, 'w') | 
| - | 
| -    if prevent_output: | 
| -      out = self._devnull | 
| -    else: | 
| -      out = sys.stderr | 
| - | 
| -    cri.RmRF('/tmp/cros_interface_remote_device_pid') | 
| -    cmd_str = ' '.join(device_side_args) | 
| -    if env: | 
| -      env_str = ' '.join(['%s=%s' % (k, v) for k, v in env.items()]) | 
| -      cmd = env_str + ' ' + cmd_str | 
| -    else: | 
| -      cmd = cmd_str | 
| -    contents = """%s&\n""" % cmd | 
| -    contents += 'echo $! > /tmp/cros_interface_remote_device_pid\n' | 
| -    cri.PushContents(contents, '/tmp/cros_interface_remote_device_bootstrap.sh') | 
| - | 
| -    cmdline = ['/bin/bash'] | 
| -    if login_shell: | 
| -      cmdline.append('-l') | 
| -    cmdline.append('/tmp/cros_interface_remote_device_bootstrap.sh') | 
| -    proc = subprocess.Popen( | 
| -      cri.FormSSHCommandLine(cmdline, | 
| -                              extra_ssh_args=extra_ssh_args), | 
| -      stdout=out, | 
| -      stderr=out, | 
| -      stdin=self._devnull, | 
| -      shell=False) | 
| - | 
| -    time.sleep(0.1) | 
| -    def TryGetResult(): | 
| -      try: | 
| -        self._pid = cri.GetFileContents( | 
| -            '/tmp/cros_interface_remote_device_pid').strip() | 
| -        return True | 
| -      except OSError: | 
| -        return False | 
| -    try: | 
| -      util.WaitFor(TryGetResult, 5) | 
| -    except util.TimeoutException: | 
| -      raise Exception('Something horrible has happened!') | 
| - | 
| -    # Killing the ssh session leaves the process running. We dont | 
| -    # need it anymore, unless we have port-forwards. | 
| -    if not leave_ssh_alive: | 
| -      proc.kill() | 
| -    else: | 
| -      self._proc = proc | 
| - | 
| -    self._pid = int(self._pid) | 
| -    if not self.IsAlive(): | 
| -      raise OSError('Process did not come up or did not stay alive very long!') | 
| -    self._cri = cri | 
| - | 
| -  def Close(self, try_sigint_first=False): | 
| -    if self.IsAlive(): | 
| -      # Try to politely shutdown, first. | 
| -      if try_sigint_first: | 
| -        logging.debug("kill -INT %i" % self._pid) | 
| -        self._cri.GetAllCmdOutput( | 
| -          ['kill', '-INT', str(self._pid)], quiet=True) | 
| -        try: | 
| -          self.Wait(timeout=0.5) | 
| -        except util.TimeoutException: | 
| -          pass | 
| - | 
| -      if self.IsAlive(): | 
| -        logging.debug("kill -KILL %i" % self._pid) | 
| -        self._cri.GetAllCmdOutput( | 
| -          ['kill', '-KILL', str(self._pid)], quiet=True) | 
| -        try: | 
| -          self.Wait(timeout=5) | 
| -        except util.TimeoutException: | 
| -          pass | 
| - | 
| -      if self.IsAlive(): | 
| -        raise Exception('Could not shutdown the process.') | 
| - | 
| -    self._cri = None | 
| -    if self._proc: | 
| -      self._proc.kill() | 
| -      self._proc = None | 
| - | 
| -    if self._devnull: | 
| -      self._devnull.close() | 
| -      self._devnull = None | 
| - | 
| -  def __enter__(self): | 
| -    return self | 
| - | 
| -  def __exit__(self, *args): | 
| -    self.Close() | 
| -    return | 
| - | 
| -  def Wait(self, timeout=1): | 
| -    if not self._pid: | 
| -      raise Exception('Closed') | 
| -    def IsDone(): | 
| -      return not self.IsAlive() | 
| -    util.WaitFor(IsDone, timeout) | 
| -    self._pid = None | 
| - | 
| -  def IsAlive(self, quiet=True): | 
| -    if not self._pid: | 
| -      return False | 
| -    exists = self._cri.FileExistsOnDevice('/proc/%i/cmdline' % self._pid, | 
| -                                          quiet=quiet) | 
| -    return exists | 
| - | 
| -def HasSSH(): | 
| -  try: | 
| -    RunCmd(['ssh'], quiet=True) | 
| -    RunCmd(['scp'], quiet=True) | 
| -    logging.debug("HasSSH()->True") | 
| -    return True | 
| -  except OSError: | 
| -    logging.debug("HasSSH()->False") | 
| -    return False | 
| - | 
| -class LoginException(Exception): | 
| -  pass | 
| - | 
| -class KeylessLoginRequiredException(LoginException): | 
| -  pass | 
| - | 
| -class CrOSInterface(object): | 
| -  # pylint: disable=R0923 | 
| -  def __init__(self, hostname, ssh_identity = None): | 
| -    self._hostname = hostname | 
| -    self._ssh_identity = None | 
| -    self._hostfile = tempfile.NamedTemporaryFile() | 
| -    self._hostfile.flush() | 
| -    self._ssh_args = ['-o ConnectTimeout=5', | 
| -                      '-o StrictHostKeyChecking=no', | 
| -                      '-o KbdInteractiveAuthentication=no', | 
| -                      '-o PreferredAuthentications=publickey', | 
| -                      '-o UserKnownHostsFile=%s' % self._hostfile.name] | 
| - | 
| -    # List of ports generated from GetRemotePort() that may not be in use yet. | 
| -    self._reserved_ports = [] | 
| - | 
| -    if ssh_identity: | 
| -      self._ssh_identity = os.path.abspath(os.path.expanduser(ssh_identity)) | 
| - | 
| -  @property | 
| -  def hostname(self): | 
| -    return self._hostname | 
| - | 
| -  def FormSSHCommandLine(self, args, extra_ssh_args=None): | 
| -    full_args = ['ssh', | 
| -                 '-o ForwardX11=no', | 
| -                 '-o ForwardX11Trusted=no', | 
| -                 '-n'] + self._ssh_args | 
| -    if self._ssh_identity is not None: | 
| -      full_args.extend(['-i', self._ssh_identity]) | 
| -    if extra_ssh_args: | 
| -      full_args.extend(extra_ssh_args) | 
| -    full_args.append('root@%s' % self._hostname) | 
| -    full_args.extend(args) | 
| -    return full_args | 
| - | 
| -  def GetAllCmdOutput(self, args, cwd=None, quiet=False): | 
| -    return GetAllCmdOutput(self.FormSSHCommandLine(args), cwd, quiet=quiet) | 
| - | 
| -  def _RemoveSSHWarnings(self, toClean): | 
| -    """Removes specific ssh warning lines from a string. | 
| - | 
| -    Args: | 
| -      toClean: A string that may be containing multiple lines. | 
| - | 
| -    Returns: | 
| -      A copy of toClean with all the Warning lines removed. | 
| -    """ | 
| -    # Remove the Warning about connecting to a new host for the first time. | 
| -    return re.sub('Warning: Permanently added [^\n]* to the list of known ' | 
| -                  'hosts.\s\n', '', toClean) | 
| - | 
| -  def TryLogin(self): | 
| -    logging.debug('TryLogin()') | 
| -    stdout, stderr = self.GetAllCmdOutput(['echo', '$USER'], quiet=True) | 
| - | 
| -    # The initial login will add the host to the hosts file but will also print | 
| -    # a warning to stderr that we need to remove. | 
| -    stderr = self._RemoveSSHWarnings(stderr) | 
| -    if stderr != '': | 
| -      if 'Host key verification failed' in stderr: | 
| -        raise LoginException(('%s host key verification failed. ' + | 
| -                             'SSH to it manually to fix connectivity.') % | 
| -            self._hostname) | 
| -      if 'Operation timed out' in stderr: | 
| -        raise LoginException('Timed out while logging into %s' % self._hostname) | 
| -      if 'UNPROTECTED PRIVATE KEY FILE!' in stderr: | 
| -        raise LoginException('Permissions for %s are too open. To fix this,\n' | 
| -                             'chmod 600 %s' % (self._ssh_identity, | 
| -                                               self._ssh_identity)) | 
| -      if 'Permission denied (publickey,keyboard-interactive)' in stderr: | 
| -        raise KeylessLoginRequiredException( | 
| -          'Need to set up ssh auth for %s' % self._hostname) | 
| -      raise LoginException('While logging into %s, got %s' % ( | 
| -          self._hostname, stderr)) | 
| -    if stdout != 'root\n': | 
| -      raise LoginException( | 
| -        'Logged into %s, expected $USER=root, but got %s.' % ( | 
| -          self._hostname, stdout)) | 
| - | 
| -  def FileExistsOnDevice(self, file_name, quiet=False): | 
| -    stdout, stderr = self.GetAllCmdOutput([ | 
| -        'if', 'test', '-a', file_name, ';', | 
| -        'then', 'echo', '1', ';', | 
| -        'fi' | 
| -        ], quiet=True) | 
| -    if stderr != '': | 
| -      if "Connection timed out" in stderr: | 
| -        raise OSError('Machine wasn\'t responding to ssh: %s' % | 
| -                      stderr) | 
| -      raise OSError('Unepected error: %s' % stderr) | 
| -    exists = stdout == '1\n' | 
| -    if not quiet: | 
| -      logging.debug("FileExistsOnDevice(<text>, %s)->%s" % ( | 
| -          file_name, exists)) | 
| -    return exists | 
| - | 
| -  def PushFile(self, filename, remote_filename): | 
| -    args = ['scp', '-r' ] + self._ssh_args | 
| -    if self._ssh_identity: | 
| -      args.extend(['-i', self._ssh_identity]) | 
| - | 
| -    args.extend([os.path.abspath(filename), | 
| -                 'root@%s:%s' % (self._hostname, remote_filename)]) | 
| - | 
| -    stdout, stderr = GetAllCmdOutput(args, quiet=True) | 
| -    if stderr != '': | 
| -      assert 'No such file or directory' in stderr | 
| -      raise OSError | 
| - | 
| -  def PushContents(self, text, remote_filename): | 
| -    logging.debug("PushContents(<text>, %s)" % remote_filename) | 
| -    with tempfile.NamedTemporaryFile() as f: | 
| -      f.write(text) | 
| -      f.flush() | 
| -      self.PushFile(f.name, remote_filename) | 
| - | 
| -  def GetFileContents(self, filename): | 
| -    with tempfile.NamedTemporaryFile() as f: | 
| -      args = ['scp'] + self._ssh_args | 
| -      if self._ssh_identity: | 
| -        args.extend(['-i', self._ssh_identity]) | 
| - | 
| -      args.extend(['root@%s:%s' % (self._hostname, filename), | 
| -                   os.path.abspath(f.name)]) | 
| - | 
| -      stdout, stderr = GetAllCmdOutput(args, quiet=True) | 
| - | 
| -      if stderr != '': | 
| -        assert 'No such file or directory' in stderr | 
| -        raise OSError | 
| - | 
| -      with open(f.name, 'r') as f2: | 
| -        res = f2.read() | 
| -        logging.debug("GetFileContents(%s)->%s" % (filename, res)) | 
| -        return res | 
| - | 
| -  def ListProcesses(self): | 
| -    stdout, stderr = self.GetAllCmdOutput([ | 
| -        '/bin/ps', '--no-headers', | 
| -        '-A', | 
| -        '-o', 'pid,args'], quiet=True) | 
| -    assert stderr == '' | 
| -    procs = [] | 
| -    for l in stdout.split('\n'): # pylint: disable=E1103 | 
| -      if l == '': | 
| -        continue | 
| -      m = re.match('^\s*(\d+)\s+(.+)', l, re.DOTALL) | 
| -      assert m | 
| -      procs.append(m.groups()) | 
| -    logging.debug("ListProcesses(<predicate>)->[%i processes]" % len(procs)) | 
| -    return procs | 
| - | 
| -  def RmRF(self, filename): | 
| -    logging.debug("rm -rf %s" % filename) | 
| -    self.GetCmdOutput(['rm', '-rf', filename], quiet=True) | 
| - | 
| -  def KillAllMatching(self, predicate): | 
| -    kills = ['kill', '-KILL'] | 
| -    for p in self.ListProcesses(): | 
| -      if predicate(p[1]): | 
| -        logging.info('Killing %s', repr(p)) | 
| -        kills.append(p[0]) | 
| -    logging.debug("KillAllMatching(<predicate>)->%i" % (len(kills) - 2)) | 
| -    if len(kills) > 2: | 
| -      self.GetCmdOutput(kills, quiet=True) | 
| -    return len(kills) - 2 | 
| - | 
| -  def IsServiceRunning(self, service_name): | 
| -    stdout, stderr = self.GetAllCmdOutput([ | 
| -        'status', service_name], quiet=True) | 
| -    assert stderr == '' | 
| -    running = 'running, process' in stdout | 
| -    logging.debug("IsServiceRunning(%s)->%s" % (service_name, running)) | 
| -    return running | 
| - | 
| -  def GetCmdOutput(self, args, quiet=False): | 
| -    stdout, stderr = self.GetAllCmdOutput(args, quiet=True) | 
| -    assert stderr == '' | 
| -    if not quiet: | 
| -      logging.debug("GetCmdOutput(%s)->%s" % (repr(args), stdout)) | 
| -    return stdout | 
| - | 
| -  def GetRemotePort(self): | 
| -    netstat = self.GetAllCmdOutput(['netstat', '-ant']) | 
| -    netstat = netstat[0].split('\n') | 
| -    ports_in_use = [] | 
| - | 
| -    for line in netstat[2:]: | 
| -      if not line: | 
| -        continue | 
| -      address_in_use = line.split()[3] | 
| -      port_in_use = address_in_use.split(':')[-1] | 
| -      ports_in_use.append(int(port_in_use)) | 
| - | 
| -    ports_in_use.extend(self._reserved_ports) | 
| - | 
| -    new_port = sorted(ports_in_use)[-1] + 1 | 
| -    self._reserved_ports.append(new_port) | 
| - | 
| -    return new_port | 
| - | 
| -  def IsHTTPServerRunningOnPort(self, port): | 
| -    wget_output = self.GetAllCmdOutput( | 
| -        ['wget', 'localhost:%i' % (port), '-T1', '-t1']) | 
| - | 
| -    if 'Connection refused' in wget_output[1]: | 
| -      return False | 
| - | 
| -    return True | 
|  |