Ejemplo n.º 1
0
    def locate_devices(self, images):
        """Locate device for each image in the given images list.

        @param images: A list of tuples of (build, serial). serial could be None
                if it's not specified. Following are some examples:
                [('branch1/shamu-userdebug/100', None),
                 ('branch1/shamu-userdebug/100', None)]
                [('branch1/hammerhead-userdebug/100', 'XZ123'),
                 ('branch1/hammerhead-userdebug/200', None)]
                where XZ123 is serial of one of the hammerheads connected to the
                testbed.

        @return: A dictionary of (serial, build). Note that build here should
                 not have a serial specified in it.
        @raise InstallError: If not enough duts are available to install the
                given images. Or there are more duts with the same board than
                the images list specified.
        """
        # The map between serial and build to install in that dut.
        serial_build_pairs = {}
        builds_without_serial = [
            build for build, serial in images if not serial
        ]
        for build, serial in images:
            if serial:
                serial_build_pairs[serial] = build
        # Return the mapping if all builds have serial specified.
        if not builds_without_serial:
            return serial_build_pairs

        # serials grouped by the board of duts.
        duts_by_name = {}
        for serial, host in self.get_adb_devices().iteritems():
            # Excluding duts already assigned to a build.
            if serial in serial_build_pairs:
                continue
            aliases = host.get_device_aliases()
            for alias in aliases:
                duts_by_name.setdefault(alias, []).append(serial)

        # Builds grouped by the board name.
        builds_by_name = {}
        for build in builds_without_serial:
            match = re.match(adb_host.BUILD_REGEX, build)
            if not match:
                raise error.InstallError(
                    'Build %s is invalid. Failed to parse '
                    'the board name.' % build)
            name = match.group('BUILD_TARGET')
            builds_by_name.setdefault(name, []).append(build)

        # Pair build with dut with matching board.
        for name, builds in builds_by_name.iteritems():
            duts = duts_by_name.get(name, [])
            if len(duts) != len(builds):
                raise error.InstallError(
                    'Expected number of DUTs for name %s is %d, got %d' %
                    (name, len(builds), len(duts) if duts else 0))
            serial_build_pairs.update(dict(zip(duts, builds)))
        return serial_build_pairs
Ejemplo n.º 2
0
    def _parse_image(self, image_string):
        """Parse the image string to a dictionary.

        Sample value of image_string:
        Provision dut with serial ZX1G2 to build `branch1/shamu-userdebug/111`,
        and provision another shamu with build `branch2/shamu-userdebug/222`
        branch1/shamu-userdebug/111:ZX1G2,branch2/shamu-userdebug/222

        Provision 10 shamu with build `branch1/shamu-userdebug/LATEST`
        branch1/shamu-userdebug/LATEST#10

        @param image_string: A comma separated string of images. The image name
                is in the format of branch/target/build_id[:serial]. Serial is
                optional once testbed machine_install supports allocating DUT
                based on board.

        @returns: A list of tuples of (build, serial). serial could be None if
                  it's not specified.
        """
        images = []
        for image in image_string.split(','):
            match = re.match(_IMAGE_NAME_PATTERN, image)
            # The image string cannot specify both serial and count.
            if not match or (match.group(2) and match.group(3)):
                raise error.InstallError(
                    'Image name of "%s" has invalid format. It should '
                    'follow naming convention of '
                    'branch/target/build_id[:serial][#count]', image)
            if match.group(3):
                images.extend([(match.group(1), None)] * int(match.group(3)))
            else:
                images.append((match.group(1), match.group(2)))
        return images
Ejemplo n.º 3
0
    def machine_install(self):
        """Install the DUT.

        @returns The name of the image installed.
        """
        if not self._parser.options.image:
            raise error.InstallError('No image string is provided to test bed.')
        images = self._parse_image(self._parser.options.image)

        arguments = []
        for serial, build in self.locate_devices(images).iteritems():
            logging.info('Installing build %s on DUT with serial %s.', build,
                         serial)
            host = self.get_adb_devices()[serial]
            build_url, _ = host.stage_build_for_install(build)
            arguments.append({'host': host,
                              'build_url': build_url})

        thread_pool = pool.ThreadPool(_POOL_SIZE)
        thread_pool.map(self._install_device, arguments)
        thread_pool.close()
        return self._parser.options.image
    def machine_install(self, build_url=None, build_local_path=None, wipe=True,
                        flash_all=False, os_type=None):
        """Install the DUT.

        @param build_url: The url to use for downloading Android artifacts.
                          pattern: http://$devserver:###/static/$build.
                          If build_url is set to None, the code may try
                          _parser.options.image to do the installation. If none
                          of them is set, machine_install will fail.
        @param build_local_path: The path to a local directory that contains the
                                 image files needed to provision the device.
        @param wipe: No-op
        @param flash_all: No-op
        @param os_type: OS to install (overrides label).

        @returns A tuple of (image_name, host_attributes). image_name is the
                 name of image installed, e.g.,
                 git_mnc-release/shamu-userdebug/1234
                 host_attributes is a dictionary of (attribute, value), which
                 can be saved to afe_host_attributes table in database. This
                 method returns a dictionary with a single entry of
                 `job_repo_url_[adb_serial]`: devserver_url, where devserver_url
                 is a url to the build staged on devserver.
        """
        os_type = os_type or self.get_os_type()
        if not build_url and self._parser.options.image:
            build_url, _ = self.stage_build_for_install(
                    self._parser.options.image, os_type=os_type)
        if os_type == OS_TYPE_EMULATED_BRILLO:
            self.setup_brillo_emulator(
                    build_url=build_url, build_local_path=build_local_path)
            self.ensure_adb_mode()
        else:
            raise error.InstallError(
                    'Installation of os type %s is not supported.' %
                    os_type)
        return (build_url.split('static/')[-1],
                {self.job_repo_url_attribute: build_url})
Ejemplo n.º 5
0
    def _parse_image(self, image_string):
        """Parse the image string to a dictionary.

        Sample value of image_string:
        branch1/shamu-userdebug/LATEST:ZX1G2,branch2/shamu-userdebug/LATEST

        @param image_string: A comma separated string of images. The image name
                is in the format of branch/target/build_id[:serial]. Serial is
                optional once testbed machine_install supports allocating DUT
                based on board.

        @returns: A list of tuples of (build, serial). serial could be None if
                  it's not specified.
        """
        images = []
        for image in image_string.split(','):
            match = re.match(_IMAGE_NAME_PATTERN, image)
            if not match:
                raise error.InstallError(
                        'Image name of "%s" has invalid format. It should '
                        'follow naming convention of '
                        'branch/target/build_id[:serial]', image)
            images.append((match.group(1), match.group(2)))
        return images
Ejemplo n.º 6
0
    def testTriggerUpdate(self):
        """Tests that we correctly handle updater errors."""
        update_url = 'http://server/test/url'
        self.host = self.mox.CreateMockAnything()
        self.mox.StubOutWithMock(self.host, 'run')
        self.mox.StubOutWithMock(autoupdater.ChromiumOSUpdater,
                                 'get_last_update_error')
        self.host.hostname = 'test_host'
        updater_control_bin = '/usr/bin/update_engine_client'
        test_url = 'http://server/test/url'
        expected_wait_cmd = ('%s -status | grep CURRENT_OP' %
                             updater_control_bin)
        expected_cmd = ('%s --check_for_update --omaha_url=%s' %
                        (updater_control_bin, test_url))
        self.mox.StubOutWithMock(time, "sleep")
        UPDATE_ENGINE_RETRY_WAIT_TIME = 5

        # Generic SSH Error.
        cmd_result_255 = self.mox.CreateMockAnything()
        cmd_result_255.exit_status = 255

        # Command Failed Error
        cmd_result_1 = self.mox.CreateMockAnything()
        cmd_result_1.exit_status = 1

        # Error 37
        cmd_result_37 = self.mox.CreateMockAnything()
        cmd_result_37.exit_status = 37

        updater = autoupdater.ChromiumOSUpdater(update_url, host=self.host)

        # (SUCCESS) Expect one wait command and one status command.
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(expected_cmd)

        # (SUCCESS) Test with one retry to wait for update-engine.
        self._host_run_for_update(expected_wait_cmd,
                                  exception=error.AutoservRunError(
                                      'non-zero status', cmd_result_1))
        time.sleep(UPDATE_ENGINE_RETRY_WAIT_TIME)
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(expected_cmd)

        # (SUCCESS) One-time SSH timeout, then success on retry.
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(expected_cmd,
                                  exception=error.AutoservSSHTimeout(
                                      'ssh timed out', cmd_result_255))
        self._host_run_for_update(expected_cmd)

        # (SUCCESS) One-time ERROR 37, then success.
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(expected_cmd,
                                  exception=error.AutoservRunError(
                                      'ERROR_CODE=37', cmd_result_37))
        self._host_run_for_update(expected_cmd)

        # (FAILURE) Bad status of update engine.
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(
            expected_cmd,
            bad_update_status=True,
            exception=error.InstallError('host is not in installable state'))

        # (FAILURE) Two-time SSH timeout.
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(expected_cmd,
                                  exception=error.AutoservSSHTimeout(
                                      'ssh timed out', cmd_result_255))
        self._host_run_for_update(expected_cmd,
                                  exception=error.AutoservSSHTimeout(
                                      'ssh timed out', cmd_result_255))

        # (FAILURE) SSH Permission Error
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(
            expected_cmd,
            exception=error.AutoservSshPermissionDeniedError(
                'no permission', cmd_result_255))

        # (FAILURE) Other ssh failure
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(
            expected_cmd,
            exception=error.AutoservSshPermissionDeniedError(
                'no permission', cmd_result_255))
        # (FAILURE) Other error
        self._host_run_for_update(expected_wait_cmd)
        self._host_run_for_update(expected_cmd,
                                  exception=error.AutoservRunError(
                                      "unknown error", cmd_result_1))

        self.mox.ReplayAll()

        # Expect success
        updater.trigger_update()
        updater.trigger_update()
        updater.trigger_update()
        updater.trigger_update()

        # Expect errors as listed above
        self.assertRaises(autoupdater.RootFSUpdateError,
                          updater.trigger_update)
        self.assertRaises(autoupdater.RootFSUpdateError,
                          updater.trigger_update)
        self.assertRaises(autoupdater.RootFSUpdateError,
                          updater.trigger_update)
        self.assertRaises(autoupdater.RootFSUpdateError,
                          updater.trigger_update)
        self.assertRaises(autoupdater.RootFSUpdateError,
                          updater.trigger_update)

        self.mox.VerifyAll()
Ejemplo n.º 7
0
    def machine_install(self, image=None):
        """Install the DUT.

        @param image: Image we want to install on this testbed, e.g.,
                      `branch1/shamu-eng/1001,branch2/shamu-eng/1002`

        @returns A tuple of (the name of the image installed, None), where None
                is a placeholder for update_url. Testbed does not have a single
                update_url, thus it's set to None.
        @returns A tuple of (image_name, host_attributes).
                image_name is the name of images installed, e.g.,
                `branch1/shamu-eng/1001,branch2/shamu-eng/1002`
                host_attributes is a dictionary of (attribute, value), which
                can be saved to afe_host_attributes table in database. This
                method returns a dictionary with entries of job_repo_urls for
                each provisioned devices:
                `job_repo_url_[adb_serial]`: devserver_url, where devserver_url
                is a url to the build staged on devserver.
                For example:
                {'job_repo_url_XZ001': 'http://10.1.1.3/branch1/shamu-eng/1001',
                 'job_repo_url_XZ002': 'http://10.1.1.3/branch2/shamu-eng/1002'}
        """
        image = image or self._parser.options.image
        if not image:
            raise error.InstallError(
                'No image string is provided to test bed.')
        images = self._parse_image(image)
        host_attributes = {}

        # Change logging formatter to include thread name. This is to help logs
        # from each provision runs have the dut's serial, which is set as the
        # thread name.
        logging_config.add_threadname_in_log()

        serial_build_map = self.locate_devices(images)

        build_url, build_local_path, teststation = self._stage_shared_build(
            serial_build_map)

        thread_pool = None
        try:
            arguments = []
            for serial, build in serial_build_map.iteritems():
                logging.info('Installing build %s on DUT with serial %s.',
                             build, serial)
                host = self.get_adb_devices()[serial]
                if build_url:
                    device_build_url = build_url
                else:
                    device_build_url, _ = host.stage_build_for_install(build)
                arguments.append({
                    'host': host,
                    'build_url': device_build_url,
                    'build_local_path': build_local_path
                })
                attribute_name = '%s_%s' % (constants.JOB_REPO_URL,
                                            host.adb_serial)
                host_attributes[attribute_name] = device_build_url

            thread_pool = pool.ThreadPool(_POOL_SIZE)
            thread_pool.map(self._install_device, arguments)
            thread_pool.close()
        except Exception as err:
            logging.error(err.message)
        finally:
            if thread_pool:
                thread_pool.join()

            if build_local_path:
                logging.debug('Clean up build artifacts %s:%s',
                              teststation.hostname, build_local_path)
                teststation.run('rm -rf %s' % build_local_path)

        return image, host_attributes
Ejemplo n.º 8
0
    def locate_devices(self, images):
        """Locate device for each image in the given images list.

        If the given images all have no serial associated and have the same
        image for the same board, testbed will assign all devices with the
        desired board to the image. This allows tests to randomly pick devices
        to run.
        As an example, a testbed with 4 devices, 2 for board_1 and 2 for
        board_2. If the given images value is:
        [('board_1_build', None), ('board_2_build', None)]
        The testbed will return following device allocation:
        {'serial_1_board_1': 'board_1_build',
         'serial_2_board_1': 'board_1_build',
         'serial_1_board_2': 'board_2_build',
         'serial_2_board_2': 'board_2_build',
        }
        That way, all board_1 duts will be installed with board_1_build, and
        all board_2 duts will be installed with board_2_build. Test can pick
        any dut from board_1 duts and same applies to board_2 duts.

        @param images: A list of tuples of (build, serial). serial could be None
                if it's not specified. Following are some examples:
                [('branch1/shamu-userdebug/100', None),
                 ('branch1/shamu-userdebug/100', None)]
                [('branch1/hammerhead-userdebug/100', 'XZ123'),
                 ('branch1/hammerhead-userdebug/200', None)]
                where XZ123 is serial of one of the hammerheads connected to the
                testbed.

        @return: A dictionary of (serial, build). Note that build here should
                 not have a serial specified in it.
        @raise InstallError: If not enough duts are available to install the
                given images. Or there are more duts with the same board than
                the images list specified.
        """
        # The map between serial and build to install in that dut.
        serial_build_pairs = {}
        builds_without_serial = [
            build for build, serial in images if not serial
        ]
        for build, serial in images:
            if serial:
                serial_build_pairs[serial] = build
        # Return the mapping if all builds have serial specified.
        if not builds_without_serial:
            return serial_build_pairs

        # serials grouped by the board of duts.
        duts_by_name = {}
        for serial, host in self.get_adb_devices().iteritems():
            # Excluding duts already assigned to a build.
            if serial in serial_build_pairs:
                continue
            aliases = host.get_device_aliases()
            for alias in aliases:
                duts_by_name.setdefault(alias, []).append(serial)

        # Builds grouped by the board name.
        builds_by_name = {}
        for build in builds_without_serial:
            match = re.match(adb_host.BUILD_REGEX, build)
            if not match:
                raise error.InstallError(
                    'Build %s is invalid. Failed to parse '
                    'the board name.' % build)
            name = match.group('BUILD_TARGET')
            builds_by_name.setdefault(name, []).append(build)

        # Pair build with dut with matching board.
        for name, builds in builds_by_name.iteritems():
            duts = duts_by_name.get(name, [])
            if len(duts) < len(builds):
                raise error.InstallError(
                    'Expected number of DUTs for name %s is %d, got %d' %
                    (name, len(builds), len(duts) if duts else 0))
            elif len(duts) == len(builds):
                serial_build_pairs.update(dict(zip(duts, builds)))
            else:
                # In this cases, available dut number is greater than the number
                # of builds.
                if len(set(builds)) > 1:
                    raise error.InstallError(
                        'Number of available DUTs are greater than builds '
                        'needed, testbed cannot allocate DUTs for testing '
                        'deterministically.')
                # Set all DUTs to the same build.
                for serial in duts:
                    serial_build_pairs[serial] = builds[0]

        return serial_build_pairs