Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Unified Diff: tools/telemetry/telemetry/cros_interface.py

Issue 12278015: [Telemetry] Reorganize everything. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Re-add shebangs. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « tools/telemetry/telemetry/cros_browser_finder_unittest.py ('k') | tools/telemetry/telemetry/cros_interface_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698