Ejemplo n.º 1
0
    def __start_async_logcat(self):
        """ Start logcat read in subprocess and make threads to read its stdout/stderr to queues """
        cmd = "adb -s {device_id} logcat".format(device_id=self.source)
        self.worker = Executioner(cmd)
        out_q, err_q = self.worker.execute()

        self.logcat_pipeline = Drain(
            LogParser(out_q, self.compiled_regexp,
                      self.config.get_option('phone', 'type')),
            self.my_metrics['events'])
        self.logcat_pipeline.start()
Ejemplo n.º 2
0
 def run_test(self):
     """ App stage: run app/phone tests """
     if self.test_package:
         cmd = "adb -s {device_id} shell am instrument -w -e class {test_class} {test_package}/{test_runner}".format(
             test_class=self.test_class,
             device_id=self.source,
             test_package=self.test_package,
             test_runner=self.test_runner
         )
     else:
         logger.info('Infinite loop for volta because there are no tests specified, waiting for SIGINT')
         cmd = '/bin/bash -c \'while [ 1 ]; do sleep 1; done\''
     logger.info('Command \'%s\' executing...', cmd)
     self.test_performer = Executioner(cmd)
     self.test_performer.execute()
Ejemplo n.º 3
0
    def __test_interaction_with_phone(self):
        def read_process_queues_and_report(outs_q, errs_q):
            outputs = get_nowait_from_queue(outs_q)
            for chunk in outputs:
                logger.debug('Command output: %s', chunk.strip('\n'))
                if chunk.strip('\n') == 'unknown':
                    worker.close()
                    raise RuntimeError(
                        'Phone "%s" has an unknown state. Please check device authorization and state'
                        % self.source)

            errors = get_nowait_from_queue(errs_q)
            if errors:
                worker.close()
                raise RuntimeError(
                    'There were errors trying to test connection to the phone %s. Errors :%s'
                    % (self.source, errors))

        cmd = "adb -s {device_id} get-state".format(device_id=self.source)
        # get-state
        worker = Executioner(cmd)
        outs_q, errs_q = worker.execute()
        while worker.is_finished() is None:
            read_process_queues_and_report(outs_q, errs_q)
            time.sleep(1)
        read_process_queues_and_report(outs_q, errs_q)
        while not outs_q.qsize() != 0 and errs_q.qsize() != 0:
            time.sleep(0.5)
        worker.close()
        logger.info('Command \'%s\' executed on device %s. Retcode: %s', cmd,
                    self.source, worker.is_finished())
Ejemplo n.º 4
0
    def __test_interaction_with_phone(self):
        def read_process_queues_and_report(outs_q, errs_q):
            outputs = get_nowait_from_queue(outs_q)
            for chunk in outputs:
                logger.debug('Command output: %s', chunk.strip('\n'))
                if chunk.strip('\n') == 'unknown':
                    worker.close()
                    raise RuntimeError(
                        'Phone "%s" has an unknown state. Please check device authorization and state'
                        % self.source)

            errors = get_nowait_from_queue(errs_q)
            if errors:
                worker.close()
                # FIXME
                # keep in mind this Sierra bug:
                # https://discussions.apple.com/thread/7690618
                # that causes:
                # RuntimeError:
                # There were errors trying to test connection to the phone 0x819CE30D080A6.
                # Errors :
                # ['cfgutil: error: Error Domain=NSCocoaErrorDomain Code=4097
                # "connection to service named com.apple.configurator.xpc.InternetService"
                # UserInfo={NSDebugDescription=connection to service named com.apple.configurator.xpc.InternetService}
                # FIXME
                raise RuntimeError(
                    'There were errors trying to test connection to the phone %s. Errors :%s'
                    % (self.source, errors))

        cmd = "{path}cfgutil -e {device_id} list".format(
            path=self.path_to_util, device_id=self.source)
        # get-state
        worker = Executioner(cmd)
        outs_q, errs_q = worker.execute()
        while worker.is_finished() is None:
            read_process_queues_and_report(outs_q, errs_q)
            time.sleep(1)
        read_process_queues_and_report(outs_q, errs_q)
        while not outs_q.qsize() != 0 and errs_q.qsize() != 0:
            time.sleep(0.5)
        worker.close()
        logger.info('Command \'%s\' executed on device %s. Retcode: %s', cmd,
                    self.source, worker.is_finished())
Ejemplo n.º 5
0
 def adb_execution(self, cmd):
     def read_process_queues_and_report(outs_q, errs_q):
         outputs = get_nowait_from_queue(outs_q)
         for chunk in outputs:
             logger.debug('Command \'%s\' output: %s', cmd, chunk.strip())
         errors = get_nowait_from_queue(errs_q)
         for err_chunk in errors:
             logger.warning('Errors in command \'%s\' output: %s', cmd, err_chunk.strip())
     worker = Executioner(cmd)
     outs_q, errs_q = worker.execute()
     while worker.is_finished() is None:
         read_process_queues_and_report(outs_q, errs_q)
         time.sleep(1)
     read_process_queues_and_report(outs_q, errs_q)
     while not outs_q.qsize() != 0 and errs_q.qsize() != 0:
         time.sleep(0.5)
     worker.close()
     logger.info('Command \'%s\' executed on device %s. Retcode: %s', cmd, self.source, worker.is_finished())
     if worker.is_finished() != 0:
         raise RuntimeError('Failed to execute adb command \'%s\'' % cmd)
Ejemplo n.º 6
0
class iPhone(Phone):
    """ iPhone worker class - work w/ phone, read phone logs, store data

    Attributes:
        source (string): path to data source, cfgutil id for iphones
        path_to_util (string, optional): path to Apple Configurators' cfgutil

    Todo:
        unlug_type manual
    """
    def __init__(self, config, core):
        """
        Args:
            config (VoltaConfig): module configuration data
        """
        Phone.__init__(self, config, core)
        self.log_stdout_reader = None
        self.log_stderr_reader = None
        self.drain_log_stdout = None
        self.path_to_util = config.get_option('phone', 'util')
        self.source = config.get_option('phone', 'source')
        self.phone_q = None
        try:
            self.compiled_regexp = re.compile(
                config.get_option('phone', 'event_regexp', iphone_logevent_re),
                re.VERBOSE | re.IGNORECASE)
        except SyntaxError:
            logger.debug('Unable to parse specified regexp', exc_info=True)
            raise RuntimeError(
                "Unable to parse specified regexp: %s" %
                config.get_option('phone', 'event_regexp', iphone_logevent_re))
        self.my_metrics = {}
        self.__create_my_metrics()

        self.__test_interaction_with_phone()

    def __create_my_metrics(self):
        self.my_metrics['events'] = self.core.data_session.new_metric({
            'type':
            'events',
            'name':
            'events',
            'source':
            'phone'
        })

    def __test_interaction_with_phone(self):
        def read_process_queues_and_report(outs_q, errs_q):
            outputs = get_nowait_from_queue(outs_q)
            for chunk in outputs:
                logger.debug('Command output: %s', chunk.strip('\n'))
                if chunk.strip('\n') == 'unknown':
                    worker.close()
                    raise RuntimeError(
                        'Phone "%s" has an unknown state. Please check device authorization and state'
                        % self.source)

            errors = get_nowait_from_queue(errs_q)
            if errors:
                worker.close()
                # FIXME
                # keep in mind this Sierra bug:
                # https://discussions.apple.com/thread/7690618
                # that causes:
                # RuntimeError:
                # There were errors trying to test connection to the phone 0x819CE30D080A6.
                # Errors :
                # ['cfgutil: error: Error Domain=NSCocoaErrorDomain Code=4097
                # "connection to service named com.apple.configurator.xpc.InternetService"
                # UserInfo={NSDebugDescription=connection to service named com.apple.configurator.xpc.InternetService}
                # FIXME
                raise RuntimeError(
                    'There were errors trying to test connection to the phone %s. Errors :%s'
                    % (self.source, errors))

        cmd = "{path}cfgutil -e {device_id} list".format(
            path=self.path_to_util, device_id=self.source)
        # get-state
        worker = Executioner(cmd)
        outs_q, errs_q = worker.execute()
        while worker.is_finished() is None:
            read_process_queues_and_report(outs_q, errs_q)
            time.sleep(1)
        read_process_queues_and_report(outs_q, errs_q)
        while not outs_q.qsize() != 0 and errs_q.qsize() != 0:
            time.sleep(0.5)
        worker.close()
        logger.info('Command \'%s\' executed on device %s. Retcode: %s', cmd,
                    self.source, worker.is_finished())

    def prepare(self):
        """ this method skipped by iphone - instruments do the thing """
        return

    def start(self, results):
        """ Grab stage: starts log reader

        pipeline:
            start async reader

        Args:
            results (queue-like object): Phone should put there dataframes, format: ['sys_uts', 'message']
        """

        self.phone_q = results
        self.__start_async_log()

    def run_test(self):
        """ App stage: run app/phone tests """
        return

    def end(self):
        """ pipeline: stop async log process, readers and queues """
        self.worker.close()
        if self.logcat_pipeline:
            self.logcat_pipeline.close()

    def __start_async_log(self):
        """ Start logcat read in subprocess and make threads to read its stdout/stderr to queues """
        cmd = "{path}cfgutil -e {device_id} syslog".format(
            path=self.path_to_util, device_id=self.source)
        self.worker = Executioner(cmd)
        out_q, err_q = self.worker.execute()

        self.logcat_pipeline = Drain(
            LogParser(out_q, self.compiled_regexp,
                      self.config.get_option('phone', 'type')),
            self.my_metrics['events'])
        self.logcat_pipeline.start()

    def close(self):
        pass

    def get_info(self):
        data = {}
        if self.drain_log_stdout:
            data['grabber_alive'] = self.drain_log_stdout.isAlive()
        if self.phone_q:
            data['grabber_queue_size'] = self.phone_q.qsize()
        return data
Ejemplo n.º 7
0
class AndroidPhone(Phone):
    """ Android phone worker class - work w/ phone, read phone logs, run test apps and store data

    Attributes:
        source (string): path to data source, phone id (adb devices)
        lightning_apk_path (string, optional): path to lightning app
            may be url, e.g. 'http://myhost.tld/path/to/file'
            may be path to file, e.g. '/home/users/netort/path/to/file.apk'
        lightning_apk_class (string, optional): lightning class
        test_apps (list, optional): list of apps to be installed to device for test
        test_class (string, optional): app class to be started during test execution
        test_package (string, optional): app package to be started during test execution
        test_runner (string, optional): app runner to be started during test execution

    """

    def __init__(self, config, core):
        """
        Args:
            config (VoltaConfig): module configuration data
        """
        Phone.__init__(self, config, core)
        self.logcat_stdout_reader = None
        self.logcat_stderr_reader = None

        # mandatory options
        self.source = config.get_option('phone', 'source')

        # lightning app configuration
        self.lightning_apk_path = config.get_option(
            'phone', 'lightning', pkg_resources.resource_filename(
                'volta.providers.phones', 'binary/lightning-new3.apk'
            )
        )
        self.lightning_apk_class = config.get_option('phone', 'lightning_class')
        self.lightning_apk_fname = None

        # test app configuration
        self.test_apps = config.get_option('phone', 'test_apps')
        self.test_class = config.get_option('phone', 'test_class')
        self.test_package = config.get_option('phone', 'test_package')
        self.test_runner = config.get_option('phone', 'test_runner')
        self.cleanup_apps = config.get_option('phone', 'cleanup_apps')
        try:
            self.compiled_regexp = re.compile(
                config.get_option('phone', 'event_regexp', event_regexp), re.VERBOSE | re.IGNORECASE
            )
        except SyntaxError:
            logger.debug('Unable to parse specified regexp', exc_info=True)
            raise RuntimeError(
                "Unable to parse specified regexp: %s" % config.get_option('phone', 'event_regexp', event_regexp)
            )
        self.logcat_pipeline = None
        self.test_performer = None
        self.phone_q = None

        subprocess.call('adb start-server', shell=True)  # start adb server
        self.__test_interaction_with_phone()

        self.worker = None
        self.closed = False

        self.shellexec_metrics = config.get_option('phone', 'shellexec_metrics')
        self.shellexec_executor = threading.Thread(target=self.__shell_executor)
        self.shellexec_executor.setDaemon(True)
        self.shellexec_executor.start()

        self.my_metrics = {}
        self.__create_my_metrics()

    def __create_my_metrics(self):
        self.my_metrics['events'] = self.core.data_session.new_metric(
            {
                'type': 'events',
                'name': 'events',
                'source': 'phone'
            }
        )
        for key, value in self.shellexec_metrics.items():
            self.my_metrics[key] = self.core.data_session.new_metric(
                {
                    'type': 'metrics',
                    'name': key,
                    'source': 'phone',
                    '_apply': value.get('apply') if value.get('apply') else '',
                }
            )

    def __test_interaction_with_phone(self):
        def read_process_queues_and_report(outs_q, errs_q):
            outputs = get_nowait_from_queue(outs_q)
            for chunk in outputs:
                logger.debug('Command output: %s', chunk.strip())
                if chunk.strip() == 'unknown':
                    worker.close()
                    raise RuntimeError(
                        'Phone "%s" has an unknown state. Please check device authorization and state' % self.source
                    )

            errors = get_nowait_from_queue(errs_q)
            if errors:
                worker.close()
                raise RuntimeError(
                    'There were errors trying to test connection to the phone %s. Errors :%s' % (
                        self.source, errors
                    )
                )
        cmd = "adb -s {device_id} get-state".format(device_id=self.source)
        # get-state
        worker = Executioner(cmd)
        outs_q, errs_q = worker.execute()
        while worker.is_finished() is None:
            read_process_queues_and_report(outs_q, errs_q)
            time.sleep(1)
        read_process_queues_and_report(outs_q, errs_q)
        while not outs_q.qsize() != 0 and errs_q.qsize() != 0:
            time.sleep(0.5)
        worker.close()
        logger.info('Command \'%s\' executed on device %s. Retcode: %s', cmd, self.source, worker.is_finished())

    def adb_execution(self, cmd):
        def read_process_queues_and_report(outs_q, errs_q):
            outputs = get_nowait_from_queue(outs_q)
            for chunk in outputs:
                logger.debug('Command \'%s\' output: %s', cmd, chunk.strip())
            errors = get_nowait_from_queue(errs_q)
            for err_chunk in errors:
                logger.warning('Errors in command \'%s\' output: %s', cmd, err_chunk.strip())
        worker = Executioner(cmd)
        outs_q, errs_q = worker.execute()
        while worker.is_finished() is None:
            read_process_queues_and_report(outs_q, errs_q)
            time.sleep(1)
        read_process_queues_and_report(outs_q, errs_q)
        while not outs_q.qsize() != 0 and errs_q.qsize() != 0:
            time.sleep(0.5)
        worker.close()
        logger.info('Command \'%s\' executed on device %s. Retcode: %s', cmd, self.source, worker.is_finished())
        if worker.is_finished() != 0:
            raise RuntimeError('Failed to execute adb command \'%s\'' % cmd)

    def prepare(self):
        """ Phone preparation: install apps etc

        pipeline:
            install lightning
            install apks
            clean log
        """
        # apps cleanup
        for apk in self.cleanup_apps:
            self.adb_execution("adb -s {device_id} uninstall {app}".format(device_id=self.source, app=apk))

        # install lightning
        self.lightning_apk_fname = resource.get_opener(self.lightning_apk_path).get_filename
        logger.info('Installing lightning apk...')
        self.adb_execution(
            "adb -s {device_id} install -r -d -t {apk}".format(device_id=self.source, apk=self.lightning_apk_fname)
        )

        # install apks
        for apk in self.test_apps:
            apk_fname = resource.get_opener(apk).get_filename
            self.adb_execution("adb -s {device_id} install -r -d -t {apk}".format(device_id=self.source, apk=apk_fname))

        # clean logcat
        self.adb_execution("adb -s {device_id} logcat -c".format(device_id=self.source))

    def start(self, results):
        """ Grab stage: starts log reader, make sync w/ flashlight
        Args:
            results (queue-like object): Phone should put there dataframes, format: ['sys_uts', 'message']
        """
        self.phone_q = results
        self.__start_async_logcat()
        # start flashes app
        self.adb_execution(
            "adb -s {device_id} shell am start -n {package}/{runner}.MainActivity".format(
                device_id=self.source,
                package=self.lightning_apk_class,
                runner=self.lightning_apk_class
            )
        )
        logger.info('Waiting additional 15 seconds till flashlight app end its work...')
        time.sleep(15)

    def __start_async_logcat(self):
        """ Start logcat read in subprocess and make threads to read its stdout/stderr to queues """
        cmd = "adb -s {device_id} logcat".format(device_id=self.source)
        self.worker = Executioner(cmd)
        out_q, err_q = self.worker.execute()

        self.logcat_pipeline = Drain(
            LogParser(
                out_q, self.compiled_regexp, self.config.get_option('phone', 'type')
            ),
            self.my_metrics['events']
        )
        self.logcat_pipeline.start()

    def run_test(self):
        """ App stage: run app/phone tests """
        if self.test_package:
            cmd = "adb -s {device_id} shell am instrument -w -e class {test_class} {test_package}/{test_runner}".format(
                test_class=self.test_class,
                device_id=self.source,
                test_package=self.test_package,
                test_runner=self.test_runner
            )
        else:
            logger.info('Infinite loop for volta because there are no tests specified, waiting for SIGINT')
            cmd = '/bin/bash -c \'while [ 1 ]; do sleep 1; done\''
        logger.info('Command \'%s\' executing...', cmd)
        self.test_performer = Executioner(cmd)
        self.test_performer.execute()

    def end(self):
        """ Stop test and grabbers """
        self.closed = True
        if self.worker:
            self.worker.close()
        if self.test_performer:
            self.test_performer.close()
        if self.logcat_pipeline:
            self.logcat_pipeline.close()

        # apps cleanup
        for apk in self.cleanup_apps:
            self.adb_execution("adb -s {device_id} uninstall {app}".format(device_id=self.source, app=apk))

    def close(self):
        pass

    def get_info(self):
        data = {}
        if self.phone_q:
            data['grabber_queue_size'] = self.phone_q.qsize()
        if self.test_performer:
            data['test_performer_is_finished'] = self.test_performer.is_finished()
        return data

    def __shell_executor(self):
        while not self.closed:
            for key, value in self.shellexec_metrics.items():
                try:
                    if not self.shellexec_metrics[key].get('last_ts') \
                            or self.shellexec_metrics[key]['last_ts'] < int(time.time()) * 10**6:
                        metric_value = self.__execute_shellexec_metric(value.get('cmd'))
                        ts = int(time.time()) * 10 ** 6
                        if not value.get('start_time'):
                            self.shellexec_metrics[key]['start_time'] = ts
                            ts = 0
                        else:
                            ts = ts - self.shellexec_metrics[key]['start_time']
                            self.shellexec_metrics[key]['last_ts'] = ts
                        self.my_metrics[key].put(
                            pd.DataFrame(
                                data={
                                    ts:
                                        {'ts': ts, 'value': metric_value}
                                },
                            ).T
                        )
                    else:
                        continue
                except Exception:
                    logger.warning('Failed to collect shellexec metric: %s', key)
                    logger.debug('Failed to collect shellexec metric: %s', key, exc_info=True)

            time.sleep(0.1)

    @staticmethod
    def __execute_shellexec_metric(cmd):
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        (stdout, stderr) = proc.communicate()
        return stdout.strip()