class LinuxDevice(BaseLinuxDevice): platform = "linux" default_timeout = 30 delay = 2 long_delay = 3 * delay ready_timeout = 60 parameters = [ Parameter("host", mandatory=True, description="Host name or IP address for the device."), Parameter("username", mandatory=True, description="User name for the account on the device."), Parameter("password", description="Password for the account on the device (for password-based auth)."), Parameter("keyfile", description="Keyfile to be used for key-based authentication."), Parameter("port", kind=int, default=22, description="SSH port number on the device."), Parameter( "password_prompt", default="[sudo] password", description="Prompt presented by sudo when requesting the password.", ), Parameter( "use_telnet", kind=boolean, default=False, description="Optionally, telnet may be used instead of ssh, though this is discouraged.", ), Parameter( "boot_timeout", kind=int, default=120, description="How long to try to connect to the device after a reboot.", ), Parameter( "working_directory", default=None, description=""" Working directory to be used by WA. This must be in a location where the specified user has write permissions. This will default to /home/<username>/wa (or to /root/wa, if username is 'root'). """, ), Parameter( "binaries_directory", default="/usr/local/bin", description="Location of executable binaries on this device (must be in PATH).", ), ] @property def is_rooted(self): if self._is_rooted is None: # First check if the user is root try: self.execute("test $(id -u) = 0") self._is_root_user = True self._is_rooted = True return self._is_rooted except DeviceError: self._is_root_user = False # Otherwise, check if the user has sudo rights try: self.execute("ls /", as_root=True) self._is_rooted = True except DeviceError: self._is_rooted = False return self._is_rooted def __init__(self, *args, **kwargs): super(LinuxDevice, self).__init__(*args, **kwargs) self.shell = None self.local_binaries_directory = None self._is_rooted = None def validate(self): if self.working_directory is None: # pylint: disable=access-member-before-definition if self.username == "root": self.working_directory = "/root/wa" # pylint: disable=attribute-defined-outside-init else: self.working_directory = "/home/{}/wa".format( self.username ) # pylint: disable=attribute-defined-outside-init self.local_binaries_directory = self.path.join(self.working_directory, "bin") def initialize(self, context, *args, **kwargs): self.execute("mkdir -p {}".format(self.local_binaries_directory)) self.execute("mkdir -p {}".format(self.binaries_directory)) self.execute("export PATH={}:$PATH".format(self.local_binaries_directory)) self.execute("export PATH={}:$PATH".format(self.binaries_directory)) super(LinuxDevice, self).initialize(context, *args, **kwargs) # Power control def reset(self): self.execute("reboot", as_root=True) self._is_ready = False def hard_reset(self): self._is_ready = False def boot(self, hard=False, **kwargs): if hard: self.hard_reset() else: self.reset() self.logger.debug("Waiting for device...") start_time = time.time() while (time.time() - start_time) < self.boot_timeout: try: s = socket.create_connection((self.host, self.port), timeout=5) s.close() break except socket.timeout: pass except socket.error: time.sleep(5) else: raise DeviceError("Could not connect to {} after reboot".format(self.host)) def connect(self): # NOQA pylint: disable=R0912 self.shell = SshShell( password_prompt=self.password_prompt, timeout=self.default_timeout, telnet=self.use_telnet ) self.shell.login(self.host, self.username, self.password, self.keyfile, self.port) self._is_ready = True def disconnect(self): # NOQA pylint: disable=R0912 self.shell.logout() self._is_ready = False # Execution def has_root(self): try: self.execute("ls /", as_root=True) return True except DeviceError as e: if "not in the sudoers file" not in e.message: raise e return False def execute( self, command, timeout=default_timeout, check_exit_code=True, background=False, as_root=False, strip_colors=True, **kwargs ): """ Execute the specified command on the device using adb. Parameters: :param command: The command to be executed. It should appear exactly as if you were typing it into a shell. :param timeout: Time, in seconds, to wait for adb to return before aborting and raising an error. Defaults to ``AndroidDevice.default_timeout``. :param check_exit_code: If ``True``, the return code of the command on the Device will be check and exception will be raised if it is not 0. Defaults to ``True``. :param background: If ``True``, will execute create a new ssh shell rather than using the default session and will return it immediately. If this is ``True``, ``timeout``, ``strip_colors`` and (obvisously) ``check_exit_code`` will be ignored; also, with this, ``as_root=True`` is only valid if ``username`` for the device was set to ``root``. :param as_root: If ``True``, will attempt to execute command in privileged mode. The device must be rooted, otherwise an error will be raised. Defaults to ``False``. Added in version 2.1.3 :returns: If ``background`` parameter is set to ``True``, the subprocess object will be returned; otherwise, the contents of STDOUT from the device will be returned. """ self._check_ready() try: if background: if as_root and self.username != "root": raise DeviceError("Cannot execute in background with as_root=True unless user is root.") return self.shell.background(command) else: # If we're already the root user, don't bother with sudo if self._is_root_user: as_root = False return self.shell.execute(command, timeout, check_exit_code, as_root, strip_colors) except CalledProcessError as e: raise DeviceError(e) def kick_off(self, command): """ Like execute but closes adb session and returns immediately, leaving the command running on the device (this is different from execute(background=True) which keeps adb connection open and returns a subprocess object). """ self._check_ready() command = 'sh -c "{}" 1>/dev/null 2>/dev/null &'.format(escape_double_quotes(command)) return self.shell.execute(command) def get_pids_of(self, process_name): """Returns a list of PIDs of all processes with the specified name.""" # result should be a column of PIDs with the first row as "PID" header result = self.execute("ps -C {} -o pid".format(process_name), check_exit_code=False).strip().split() # NOQA if len(result) >= 2: # at least one row besides the header return map(int, result[1:]) else: return [] def ps(self, **kwargs): command = "ps -eo user,pid,ppid,vsize,rss,wchan,pcpu,state,fname" lines = iter(convert_new_lines(self.execute(command)).split("\n")) lines.next() # header result = [] for line in lines: parts = re.split(r"\s+", line, maxsplit=8) if parts: result.append(PsEntry(*(parts[0:1] + map(int, parts[1:5]) + parts[5:]))) if not kwargs: return result else: filtered_result = [] for entry in result: if all(getattr(entry, k) == v for k, v in kwargs.iteritems()): filtered_result.append(entry) return filtered_result # File management def push_file(self, source, dest, as_root=False, timeout=default_timeout): # pylint: disable=W0221 self._check_ready() try: if not as_root or self.username == "root": self.shell.push_file(source, dest, timeout=timeout) else: tempfile = self.path.join(self.working_directory, self.path.basename(dest)) self.shell.push_file(source, tempfile, timeout=timeout) self.shell.execute("cp -r {} {}".format(tempfile, dest), timeout=timeout, as_root=True) except CalledProcessError as e: raise DeviceError(e) def pull_file(self, source, dest, as_root=False, timeout=default_timeout): # pylint: disable=W0221 self._check_ready() try: if not as_root or self.username == "root": self.shell.pull_file(source, dest, timeout=timeout) else: tempfile = self.path.join(self.working_directory, self.path.basename(source)) self.shell.execute("cp -r {} {}".format(source, tempfile), timeout=timeout, as_root=True) self.shell.execute("chown -R {} {}".format(self.username, tempfile), timeout=timeout, as_root=True) self.shell.pull_file(tempfile, dest, timeout=timeout) except CalledProcessError as e: raise DeviceError(e) def delete_file(self, filepath, as_root=False): # pylint: disable=W0221 self.execute("rm -rf {}".format(filepath), as_root=as_root) def file_exists(self, filepath): output = self.execute("if [ -e '{}' ]; then echo 1; else echo 0; fi".format(filepath)) # output from ssh my contain part of the expression in the buffer, # split out everything except the last word. return boolean(output.split()[-1]) # pylint: disable=maybe-no-member def listdir(self, path, as_root=False, **kwargs): contents = self.execute("ls -1 {}".format(path), as_root=as_root).strip() if not contents: return [] return [x.strip() for x in contents.split("\n")] # pylint: disable=maybe-no-member def install(self, filepath, timeout=default_timeout, with_name=None): # pylint: disable=W0221 if self.is_rooted: destpath = self.path.join(self.binaries_directory, with_name and with_name or self.path.basename(filepath)) self.push_file(filepath, destpath, as_root=True) self.execute("chmod a+x {}".format(destpath), timeout=timeout, as_root=True) else: destpath = self.path.join( self.local_binaries_directory, with_name and with_name or self.path.basename(filepath) ) self.push_file(filepath, destpath) self.execute("chmod a+x {}".format(destpath), timeout=timeout) return destpath install_executable = install # compatibility def uninstall(self, name): if self.is_rooted: path = self.path.join(self.binaries_directory, name) self.delete_file(path, as_root=True) else: path = self.path.join(self.local_binaries_directory, name) self.delete_file(path) uninstall_executable = uninstall # compatibility def is_installed(self, name): try: self.execute("which {}".format(name)) return True except DeviceError: return False # misc def ping(self): try: # May be triggered inside initialize() self.shell.execute("ls /", timeout=5) except (TimeoutError, CalledProcessError): raise DeviceNotRespondingError(self.host) def capture_screen(self, filepath): if not self.is_installed("scrot"): self.logger.debug("Could not take screenshot as scrot is not installed.") return try: tempfile = self.path.join(self.working_directory, os.path.basename(filepath)) self.execute("DISPLAY=:0.0 scrot {}".format(tempfile)) self.pull_file(tempfile, filepath) self.delete_file(tempfile) except DeviceError as e: if "Can't open X dispay." not in e.message: raise e message = e.message.split("OUTPUT:", 1)[1].strip() self.logger.debug("Could not take screenshot: {}".format(message)) def is_screen_on(self): pass # TODO def ensure_screen_is_on(self): pass # TODO
class LinuxDevice(BaseLinuxDevice): platform = 'linux' default_timeout = 30 delay = 2 long_delay = 3 * delay ready_timeout = 60 parameters = [ Parameter('host', mandatory=True, description='Host name or IP address for the device.'), Parameter('username', mandatory=True, description='User name for the account on the device.'), Parameter('password', description='Password for the account on the device (for password-based auth).'), Parameter('keyfile', description='Keyfile to be used for key-based authentication.'), Parameter('port', kind=int, default=22, description='SSH port number on the device.'), Parameter('password_prompt', default='[sudo] password', description='Prompt presented by sudo when requesting the password.'), Parameter('use_telnet', kind=boolean, default=False, description='Optionally, telnet may be used instead of ssh, though this is discouraged.'), Parameter('boot_timeout', kind=int, default=120, description='How long to try to connect to the device after a reboot.'), ] @property def is_rooted(self): self._check_ready() if self._is_rooted is None: # First check if the user is root try: self.execute('test $(id -u) = 0') self._is_root_user = True self._is_rooted = True return self._is_rooted except DeviceError: self._is_root_user = False # Otherwise, check if the user has sudo rights try: self.execute('ls /', as_root=True) self._is_rooted = True except DeviceError: self._is_rooted = False return self._is_rooted def __init__(self, *args, **kwargs): super(LinuxDevice, self).__init__(*args, **kwargs) self.shell = None self._is_rooted = None def validate(self): if self.working_directory is None: # pylint: disable=access-member-before-definition if self.username == 'root': self.working_directory = '/root/wa' # pylint: disable=attribute-defined-outside-init else: self.working_directory = '/home/{}/wa'.format(self.username) # pylint: disable=attribute-defined-outside-init def initialize(self, context, *args, **kwargs): self.execute('mkdir -p {}'.format(self.binaries_directory)) self.execute('export PATH={}:$PATH'.format(self.binaries_directory)) super(LinuxDevice, self).initialize(context, *args, **kwargs) # Power control def reset(self): try: self.execute('reboot', as_root=True) except DeviceError as e: if 'Connection dropped' not in e.message: raise e self._is_ready = False def hard_reset(self): self._is_ready = False def boot(self, hard=False, **kwargs): if hard: self.hard_reset() else: self.reset() self.logger.debug('Waiting for device...') # Wait a fixed delay before starting polling to give the device time to # shut down, otherwise, might create the connection while it's still shutting # down resulting in subsequenct connection failing. initial_delay = 20 time.sleep(initial_delay) boot_timeout = max(self.boot_timeout - initial_delay, 10) start_time = time.time() while (time.time() - start_time) < boot_timeout: try: s = socket.create_connection((self.host, self.port), timeout=5) s.close() break except socket.timeout: pass except socket.error: time.sleep(5) else: raise DeviceError('Could not connect to {} after reboot'.format(self.host)) def connect(self): # NOQA pylint: disable=R0912 self.shell = SshShell(password_prompt=self.password_prompt, timeout=self.default_timeout, telnet=self.use_telnet) self.shell.login(self.host, self.username, self.password, self.keyfile, self.port) self._is_ready = True def disconnect(self): # NOQA pylint: disable=R0912 self.shell.logout() self._is_ready = False # Execution def execute(self, command, timeout=default_timeout, check_exit_code=True, background=False, as_root=False, strip_colors=True, **kwargs): """ Execute the specified command on the device using adb. Parameters: :param command: The command to be executed. It should appear exactly as if you were typing it into a shell. :param timeout: Time, in seconds, to wait for adb to return before aborting and raising an error. Defaults to ``AndroidDevice.default_timeout``. :param check_exit_code: If ``True``, the return code of the command on the Device will be check and exception will be raised if it is not 0. Defaults to ``True``. :param background: If ``True``, will execute create a new ssh shell rather than using the default session and will return it immediately. If this is ``True``, ``timeout``, ``strip_colors`` and (obvisously) ``check_exit_code`` will be ignored; also, with this, ``as_root=True`` is only valid if ``username`` for the device was set to ``root``. :param as_root: If ``True``, will attempt to execute command in privileged mode. The device must be rooted, otherwise an error will be raised. Defaults to ``False``. Added in version 2.1.3 :returns: If ``background`` parameter is set to ``True``, the subprocess object will be returned; otherwise, the contents of STDOUT from the device will be returned. """ self._check_ready() try: if background: if as_root and self.username != 'root': raise DeviceError('Cannot execute in background with as_root=True unless user is root.') return self.shell.background(command) else: # If we're already the root user, don't bother with sudo if self._is_root_user: as_root = False return self.shell.execute(command, timeout, check_exit_code, as_root, strip_colors) except CalledProcessError as e: raise DeviceError(e) def kick_off(self, command, as_root=None): """ Like execute but closes ssh session and returns immediately, leaving the command running on the device (this is different from execute(background=True) which keeps ssh connection open and returns a subprocess object). """ if as_root is None: as_root = self.is_rooted self._check_ready() command = 'sh -c "{}" 1>/dev/null 2>/dev/null &'.format(escape_double_quotes(command)) return self.shell.execute(command, as_root=as_root) def get_pids_of(self, process_name): """Returns a list of PIDs of all processes with the specified name.""" # result should be a column of PIDs with the first row as "PID" header result = self.execute('ps -C {} -o pid'.format(process_name), # NOQA check_exit_code=False).strip().split() if len(result) >= 2: # at least one row besides the header return map(int, result[1:]) else: return [] def ps(self, **kwargs): command = 'ps -eo user,pid,ppid,vsize,rss,wchan,pcpu,state,fname' lines = iter(convert_new_lines(self.execute(command)).split('\n')) lines.next() # header result = [] for line in lines: parts = re.split(r'\s+', line, maxsplit=8) if parts: result.append(PsEntry(*(parts[0:1] + map(int, parts[1:5]) + parts[5:]))) if not kwargs: return result else: filtered_result = [] for entry in result: if all(getattr(entry, k) == v for k, v in kwargs.iteritems()): filtered_result.append(entry) return filtered_result # File management def push_file(self, source, dest, as_root=False, timeout=default_timeout): # pylint: disable=W0221 self._check_ready() try: if not as_root or self.username == 'root': self.shell.push_file(source, dest, timeout=timeout) else: tempfile = self.path.join(self.working_directory, self.path.basename(dest)) self.shell.push_file(source, tempfile, timeout=timeout) self.shell.execute('cp -r {} {}'.format(tempfile, dest), timeout=timeout, as_root=True) except CalledProcessError as e: raise DeviceError(e) def pull_file(self, source, dest, as_root=False, timeout=default_timeout): # pylint: disable=W0221 self._check_ready() try: if not as_root or self.username == 'root': self.shell.pull_file(source, dest, timeout=timeout) else: tempfile = self.path.join(self.working_directory, self.path.basename(source)) self.shell.execute('cp -r {} {}'.format(source, tempfile), timeout=timeout, as_root=True) self.shell.execute('chown -R {} {}'.format(self.username, tempfile), timeout=timeout, as_root=True) self.shell.pull_file(tempfile, dest, timeout=timeout) except CalledProcessError as e: raise DeviceError(e) def delete_file(self, filepath, as_root=False): # pylint: disable=W0221 self.execute('rm -rf {}'.format(filepath), as_root=as_root) def file_exists(self, filepath): output = self.execute('if [ -e \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath)) # output from ssh my contain part of the expression in the buffer, # split out everything except the last word. return boolean(output.split()[-1]) # pylint: disable=maybe-no-member def listdir(self, path, as_root=False, **kwargs): contents = self.execute('ls -1 {}'.format(path), as_root=as_root).strip() if not contents: return [] return [x.strip() for x in contents.split('\n')] # pylint: disable=maybe-no-member def install(self, filepath, timeout=default_timeout, with_name=None): # pylint: disable=W0221 destpath = self.path.join(self.binaries_directory, with_name or self.path.basename(filepath)) self.push_file(filepath, destpath, as_root=True) self.execute('chmod a+x {}'.format(destpath), timeout=timeout, as_root=True) return destpath install_executable = install # compatibility def uninstall(self, executable_name): on_device_executable = self.get_binary_path(executable_name, search_system_binaries=False) if not on_device_executable: raise DeviceError("Could not uninstall {}, binary not found".format(on_device_executable)) self.delete_file(on_device_executable, as_root=self.is_rooted) uninstall_executable = uninstall # compatibility # misc def lsmod(self): """List loaded kernel modules.""" lines = self.execute('lsmod').splitlines() entries = [] for line in lines[1:]: # first line is the header if not line.strip(): continue parts = line.split() name = parts[0] size = int(parts[1]) use_count = int(parts[2]) if len(parts) > 3: used_by = ''.join(parts[3:]).split(',') else: used_by = [] entries.append(LsmodEntry(name, size, use_count, used_by)) return entries def insmod(self, path): """Install a kernel module located on the host on the target device.""" target_path = self.path.join(self.working_directory, os.path.basename(path)) self.push_file(path, target_path) self.execute('insmod {}'.format(target_path), as_root=True) def ping(self): try: # May be triggered inside initialize() self.shell.execute('ls /', timeout=5) except (TimeoutError, CalledProcessError): raise DeviceNotRespondingError(self.host) def capture_screen(self, filepath): if not self.get_binary_path('scrot'): self.logger.debug('Could not take screenshot as scrot is not installed.') return try: tempfile = self.path.join(self.working_directory, os.path.basename(filepath)) self.execute('DISPLAY=:0.0 scrot {}'.format(tempfile)) self.pull_file(tempfile, filepath) self.delete_file(tempfile) except DeviceError as e: if "Can't open X dispay." not in e.message: raise e message = e.message.split('OUTPUT:', 1)[1].strip() self.logger.debug('Could not take screenshot: {}'.format(message)) def is_screen_on(self): pass # TODO def ensure_screen_is_on(self): pass # TODO
class LinuxDevice(BaseLinuxDevice): platform = 'linux' default_timeout = 30 delay = 2 long_delay = 3 * delay ready_timeout = 60 parameters = [ Parameter('host', mandatory=True, description='Host name or IP address for the device.'), Parameter('username', mandatory=True, description='User name for the account on the device.'), Parameter('password', description='Password for the account on the device (for password-based auth).'), Parameter('keyfile', description='Keyfile to be used for key-based authentication.'), Parameter('port', kind=int, default=22, description='SSH port number on the device.'), Parameter('password_prompt', default='[sudo] password', description='Prompt presented by sudo when requesting the password.'), Parameter('use_telnet', kind=boolean, default=False, description='Optionally, telnet may be used instead of ssh, though this is discouraged.'), Parameter('boot_timeout', kind=int, default=120, description='How long to try to connect to the device after a reboot.'), Parameter('working_directory', default=None, description=''' Working directory to be used by WA. This must be in a location where the specified user has write permissions. This will default to /home/<username>/wa (or to /root/wa, if username is 'root'). '''), Parameter('binaries_directory', default='/usr/local/bin', description='Location of executable binaries on this device (must be in PATH).'), ] @property def is_rooted(self): if self._is_rooted is None: # First check if the user is root try: self.execute('test $(id -u) = 0') self._is_root_user = True self._is_rooted = True return self._is_rooted except DeviceError: self._is_root_user = False # Otherwise, check if the user has sudo rights try: self.execute('ls /', as_root=True) self._is_rooted = True except DeviceError: self._is_rooted = False return self._is_rooted def __init__(self, *args, **kwargs): super(LinuxDevice, self).__init__(*args, **kwargs) self.shell = None self.local_binaries_directory = None self._is_rooted = None def validate(self): if self.working_directory is None: # pylint: disable=access-member-before-definition if self.username == 'root': self.working_directory = '/root/wa' # pylint: disable=attribute-defined-outside-init else: self.working_directory = '/home/{}/wa'.format(self.username) # pylint: disable=attribute-defined-outside-init self.local_binaries_directory = self.path.join(self.working_directory, 'bin') def initialize(self, context, *args, **kwargs): self.execute('mkdir -p {}'.format(self.local_binaries_directory)) self.execute('mkdir -p {}'.format(self.binaries_directory)) self.execute('export PATH={}:$PATH'.format(self.local_binaries_directory)) self.execute('export PATH={}:$PATH'.format(self.binaries_directory)) super(LinuxDevice, self).initialize(context, *args, **kwargs) # Power control def reset(self): self.execute('reboot', as_root=True) self._is_ready = False def hard_reset(self): super(LinuxDevice, self).hard_reset() self._is_ready = False def boot(self, hard=False, **kwargs): if hard: self.hard_reset() else: self.reset() self.logger.debug('Waiting for device...') start_time = time.time() while (time.time() - start_time) < self.boot_timeout: try: s = socket.create_connection((self.host, self.port), timeout=5) s.close() break except socket.timeout: pass except socket.error: time.sleep(5) else: raise DeviceError('Could not connect to {} after reboot'.format(self.host)) def connect(self): # NOQA pylint: disable=R0912 self.shell = SshShell(password_prompt=self.password_prompt, timeout=self.default_timeout, telnet=self.use_telnet) self.shell.login(self.host, self.username, self.password, self.keyfile, self.port) self._is_ready = True def disconnect(self): # NOQA pylint: disable=R0912 self.shell.logout() self._is_ready = False # Execution def has_root(self): try: self.execute('ls /', as_root=True) return True except DeviceError as e: if 'not in the sudoers file' not in e.message: raise e return False def execute(self, command, timeout=default_timeout, check_exit_code=True, background=False, as_root=False, strip_colors=True, **kwargs): """ Execute the specified command on the device using adb. Parameters: :param command: The command to be executed. It should appear exactly as if you were typing it into a shell. :param timeout: Time, in seconds, to wait for adb to return before aborting and raising an error. Defaults to ``AndroidDevice.default_timeout``. :param check_exit_code: If ``True``, the return code of the command on the Device will be check and exception will be raised if it is not 0. Defaults to ``True``. :param background: If ``True``, will execute create a new ssh shell rather than using the default session and will return it immediately. If this is ``True``, ``timeout``, ``strip_colors`` and (obvisously) ``check_exit_code`` will be ignored; also, with this, ``as_root=True`` is only valid if ``username`` for the device was set to ``root``. :param as_root: If ``True``, will attempt to execute command in privileged mode. The device must be rooted, otherwise an error will be raised. Defaults to ``False``. Added in version 2.1.3 :returns: If ``background`` parameter is set to ``True``, the subprocess object will be returned; otherwise, the contents of STDOUT from the device will be returned. """ self._check_ready() try: if background: if as_root and self.username != 'root': raise DeviceError('Cannot execute in background with as_root=True unless user is root.') return self.shell.background(command) else: # If we're already the root user, don't bother with sudo if self._is_root_user: as_root = False return self.shell.execute(command, timeout, check_exit_code, as_root, strip_colors) except CalledProcessError as e: raise DeviceError(e) def kick_off(self, command): """ Like execute but closes adb session and returns immediately, leaving the command running on the device (this is different from execute(background=True) which keeps adb connection open and returns a subprocess object). """ self._check_ready() command = 'sh -c "{}" 1>/dev/null 2>/dev/null &'.format(escape_double_quotes(command)) return self.shell.execute(command) def get_pids_of(self, process_name): """Returns a list of PIDs of all processes with the specified name.""" # result should be a column of PIDs with the first row as "PID" header result = self.execute('ps -C {} -o pid'.format(process_name), # NOQA check_exit_code=False).strip().split() if len(result) >= 2: # at least one row besides the header return map(int, result[1:]) else: return [] def ps(self, **kwargs): command = 'ps -eo user,pid,ppid,vsize,rss,wchan,pcpu,state,fname' lines = iter(convert_new_lines(self.execute(command)).split('\n')) lines.next() # header result = [] for line in lines: parts = re.split(r'\s+', line, maxsplit=8) if parts: result.append(PsEntry(*(parts[0:1] + map(int, parts[1:5]) + parts[5:]))) if not kwargs: return result else: filtered_result = [] for entry in result: if all(getattr(entry, k) == v for k, v in kwargs.iteritems()): filtered_result.append(entry) return filtered_result # File management def push_file(self, source, dest, as_root=False, timeout=default_timeout): # pylint: disable=W0221 self._check_ready() try: if not as_root or self.username == 'root': self.shell.push_file(source, dest, timeout=timeout) else: tempfile = self.path.join(self.working_directory, self.path.basename(dest)) self.shell.push_file(source, tempfile, timeout=timeout) self.shell.execute('cp -r {} {}'.format(tempfile, dest), timeout=timeout, as_root=True) except CalledProcessError as e: raise DeviceError(e) def pull_file(self, source, dest, as_root=False, timeout=default_timeout): # pylint: disable=W0221 self._check_ready() try: if not as_root or self.username == 'root': self.shell.pull_file(source, dest, timeout=timeout) else: tempfile = self.path.join(self.working_directory, self.path.basename(source)) self.shell.execute('cp -r {} {}'.format(source, tempfile), timeout=timeout, as_root=True) self.shell.execute('chown -R {} {}'.format(self.username, tempfile), timeout=timeout, as_root=True) self.shell.pull_file(tempfile, dest, timeout=timeout) except CalledProcessError as e: raise DeviceError(e) def delete_file(self, filepath, as_root=False): # pylint: disable=W0221 self.execute('rm -rf {}'.format(filepath), as_root=as_root) def file_exists(self, filepath): output = self.execute('if [ -e \'{}\' ]; then echo 1; else echo 0; fi'.format(filepath)) # output from ssh my contain part of the expression in the buffer, # split out everything except the last word. return boolean(output.split()[-1]) # pylint: disable=maybe-no-member def listdir(self, path, as_root=False, **kwargs): contents = self.execute('ls -1 {}'.format(path), as_root=as_root).strip() if not contents: return [] return [x.strip() for x in contents.split('\n')] # pylint: disable=maybe-no-member def install(self, filepath, timeout=default_timeout, with_name=None): # pylint: disable=W0221 if self.is_rooted: destpath = self.path.join(self.binaries_directory, with_name and with_name or self.path.basename(filepath)) self.push_file(filepath, destpath, as_root=True) self.execute('chmod a+x {}'.format(destpath), timeout=timeout, as_root=True) else: destpath = self.path.join(self.local_binaries_directory, with_name and with_name or self.path.basename(filepath)) self.push_file(filepath, destpath) self.execute('chmod a+x {}'.format(destpath), timeout=timeout) return destpath install_executable = install # compatibility def uninstall(self, name): if self.is_rooted: path = self.path.join(self.binaries_directory, name) self.delete_file(path, as_root=True) else: path = self.path.join(self.local_binaries_directory, name) self.delete_file(path) uninstall_executable = uninstall # compatibility def is_installed(self, name): try: self.execute('which {}'.format(name)) return True except DeviceError: return False # misc def ping(self): try: # May be triggered inside initialize() self.shell.execute('ls /', timeout=5) except (TimeoutError, CalledProcessError): raise DeviceNotRespondingError(self.host) def capture_screen(self, filepath): if not self.is_installed('scrot'): self.logger.debug('Could not take screenshot as scrot is not installed.') return try: tempfile = self.path.join(self.working_directory, os.path.basename(filepath)) self.execute('DISPLAY=:0.0 scrot {}'.format(tempfile)) self.pull_file(tempfile, filepath) self.delete_file(tempfile) except DeviceError as e: if "Can't open X dispay." not in e.message: raise e message = e.message.split('OUTPUT:', 1)[1].strip() self.logger.debug('Could not take screenshot: {}'.format(message)) def is_screen_on(self): pass # TODO def ensure_screen_is_on(self): pass # TODO