Ejemplo n.º 1
0
def get_test_configs(pav_cfg,
                     host: str,
                     test_files: List[Union[str, Path]] = None,
                     tests: List[str] = None,
                     modes: List[str] = None,
                     overrides: List[str] = None,
                     outfile: TextIO = StringIO(),
                     conditions: Dict[str, Dict[str, List[str]]] = None):
    """Translate a general set of pavilion test configs into the final,
    resolved configurations.

    :param pav_cfg: The pavilion config
    :param host: The host config to target these tests with
    :param modes: The mode configs to use.
    :param test_files: Files containing a newline separated list of tests.
    :param tests: The tests to run.
    :param overrides: Overrides to apply to the configurations.
        name) of lists of tuples test configs and their variable managers.
    :param conditions: A dict containing the only_if and not_if conditions.
    :param outfile: Where to print user error messages.
    """

    overrides = overrides if overrides else []
    tests = tests if tests else []
    modes = modes if modes else []
    test_files = test_files if test_files else []
    conditions = conditions if conditions else {}

    resolver = test_config.TestConfigResolver(pav_cfg)

    tests = list(tests)
    for file in test_files:
        try:
            with pathlib.PosixPath(file).open() as test_file:
                for line in test_file.readlines():
                    line = line.strip()
                    if line and not line.startswith('#'):
                        tests.append(line)
        except (OSError, IOError) as err:
            raise commands.CommandError(
                "Could not read test file {}: {}".format(file, err.args[0]))

    try:
        resolved_cfgs = resolver.load(
            tests,
            host,
            modes,
            overrides,
            conditions=conditions,
            output_file=outfile,
        )
    except TestConfigError as err:
        raise commands.CommandError(err.args[0])

    return resolved_cfgs
Ejemplo n.º 2
0
    def _get_test_configs(self, pav_cfg, host, test_files, tests, modes,
                          overrides):
        """Translate a general set of pavilion test configs into the final,
        resolved configurations. These objects will be organized in a
        dictionary by scheduler, and have a scheduler object instantiated and
        attached.
        :param pav_cfg: The pavilion config
        :param str host: The host config to target these tests with
        :param list(str) modes: The mode configs to use.
        :param list(Path) test_files: Files containing a newline separated
            list of tests.
        :param list(str) tests: The tests to run.
        :param list(str) overrides: Overrides to apply to the configurations.
name) of lists of tuples
            test configs and their variable managers.
        """
        self.logger.debug("Finding Configs")

        resolver = test_config.TestConfigResolver(pav_cfg)

        tests = list(tests)
        for file in test_files:
            try:
                with pathlib.PosixPath(file).open() as test_file:
                    for line in test_file.readlines():
                        line = line.strip()
                        if line and not line.startswith('#'):
                            tests.append(line)
            except (OSError, IOError) as err:
                msg = "Could not read test file {}: {}".format(file, err)
                self.logger.error(msg)
                raise commands.CommandError(msg)

        try:
            resolved_cfgs = resolver.load(
                tests,
                host,
                modes,
                overrides,
                output_file=self.outfile,
            )
        except TestConfigError as err:
            raise commands.CommandError(err.args[0])

        tests_by_scheduler = defaultdict(lambda: [])
        for cfg, var_man in resolved_cfgs:
            tests_by_scheduler[cfg['scheduler']].append((cfg, var_man))

        return tests_by_scheduler
Ejemplo n.º 3
0
    def _configs_to_tests(pav_cfg, configs_by_sched, mb_tracker=None,
                          build_only=False, rebuild=False):
        """Convert the dictionary of test configs by scheduler into actual
        tests.

        :param pav_cfg: The Pavilion config
        :param dict[str,list] configs_by_sched: A dictionary of lists of test
        configs.
        :param Union[MultiBuildTracker,None] mb_tracker: The build tracker.
        :param bool build_only: Whether to only build these tests.
        :param bool rebuild: After figuring out what build to use, rebuild it.
        :return:
        """

        tests_by_sched = {}

        for sched_name in configs_by_sched.keys():
            tests_by_sched[sched_name] = []
            try:
                for i in range(len(configs_by_sched[sched_name])):
                    cfg, var_man = configs_by_sched[sched_name][i]
                    tests_by_sched[sched_name].append(TestRun(
                        pav_cfg=pav_cfg,
                        config=cfg,
                        var_man=var_man,
                        build_tracker=mb_tracker,
                        build_only=build_only,
                        rebuild=rebuild,
                    ))
            except (TestRunError, TestConfigError) as err:
                raise commands.CommandError(err)

        return tests_by_sched
Ejemplo n.º 4
0
def get_statuses(pav_cfg, args, errfile):
    """Get the statuses of the listed tests or series.
    :param pav_cfg: The pavilion config.
    :param argparse namespace args: The tests via the command line args.
    :param errfile: stream to output errors as needed.
    :returns: List of dictionary objects with the test id, name, state,
              time that the most recent status was set, and the associated
              note.
    """

    if not args.tests:
        # Get the last series ran by this user.
        series_id = series.TestSeries.load_user_series_id(pav_cfg)
        if series_id is not None:
            args.tests.append(series_id)
        else:
            raise commands.CommandError(
                "No tests specified and no last series was found.")

    test_list = []
    for test_id in args.tests:
        if test_id.startswith('s'):
            try:
                test_list.extend(
                    series.TestSeries.from_id(pav_cfg, int(test_id[1:])).tests)
            except series.TestSeriesError as err:
                utils.fprint("Suite {} could not be found.\n{}".format(
                    test_id[1:], err),
                             file=errfile,
                             color=utils.RED)
                continue
        else:
            test_list.append(test_id)

    test_list = list(map(int, test_list))

    test_statuses = []
    test_obj_list = []
    for test_id in test_list:
        try:
            test = PavTest.load(pav_cfg, test_id)
            test_obj_list.append(test)
        except (PavTestError, PavTestNotFoundError) as err:
            test_statuses.append({
                'test_id': test_id,
                'name': "",
                'state': STATES.UNKNOWN,
                'time': "",
                'note': "Test not found.",
            })

    statuses = status_from_test_obj(pav_cfg, test_obj_list)

    if statuses is not None:
        test_statuses = test_statuses + statuses
    return test_statuses
Ejemplo n.º 5
0
    def _configs_to_tests(pav_cfg,
                          configs_by_sched,
                          mb_tracker=None,
                          build_only=False,
                          rebuild=False,
                          outfile=None):
        """Convert the dictionary of test configs by scheduler into actual
        tests.

        :param pav_cfg: The Pavilion config
        :param dict[str,list] configs_by_sched: A dictionary of lists of test
        configs.
        :param Union[MultiBuildTracker,None] mb_tracker: The build tracker.
        :param bool build_only: Whether to only build these tests.
        :param bool rebuild: After figuring out what build to use, rebuild it.
        :return:
        """

        tests_by_sched = {}
        progress = 0
        tot_tests = sum([len(tests) for tests in configs_by_sched.values()])

        for sched_name in configs_by_sched.keys():
            tests_by_sched[sched_name] = []
            try:
                for i in range(len(configs_by_sched[sched_name])):
                    cfg, var_man = configs_by_sched[sched_name][i]
                    tests_by_sched[sched_name].append(
                        TestRun(
                            pav_cfg=pav_cfg,
                            config=cfg,
                            var_man=var_man,
                            build_tracker=mb_tracker,
                            build_only=build_only,
                            rebuild=rebuild,
                        ))
                    progress += 1.0 / tot_tests
                    if outfile is not None:
                        fprint("Creating Test Runs: {:.0%}".format(progress),
                               file=outfile,
                               end='\r')
            except (TestRunError, TestConfigError) as err:
                raise commands.CommandError(err)

        if outfile is not None:
            fprint('', file=outfile)

        return tests_by_sched
Ejemplo n.º 6
0
    def run(self, pav_cfg, args):
        """Wait for the requested tests to complete."""

        # get start time
        start_time = time.time()

        tests = []
        # if args.tests is empty, then retrieve the last series
        if not args.tests:
            series_id = series_util.load_user_series_id(pav_cfg)
            if series_id is not None:
                series_obj = series.TestSeries.from_id(pav_cfg, series_id)
                # if this is a series made from a series file, add the
                # whole series id to the list of tests
                if Path(series_obj.path / 'series.pgid').exists():
                    tests.append(series_id)
                else:
                    tests.extend(
                        status_utils.get_tests(pav_cfg, args.tests,
                                               self.errfile))
            else:
                raise commands.CommandError(
                    "No tests specified and no last series found")

        else:
            tests_cli = copy.deepcopy(args.tests)
            for test_id in tests_cli:
                if test_id.startswith('s'):
                    series_obj = series.TestSeries.from_id(pav_cfg, test_id)
                    if Path(series_obj.path / 'series.pgid').exists():
                        tests.append(test_id)
                        args.tests.remove(test_id)

            tests.extend(
                status_utils.get_tests(pav_cfg, args.tests, self.errfile))

        # determine timeout time, if there is one
        end_time = None
        if args.timeout is not None:
            end_time = start_time + float(args.timeout)

        self.wait(pav_cfg, tests, end_time, args.out_mode)
        return 0
Ejemplo n.º 7
0
def get_tests(pav_cfg, args, errfile):
    """
    Gets the tests depending on arguments.

:param pav_cfg: The pavilion config
:param argparse namespace args: The tests via command line args.
:param errfile: stream to output errors as needed
:return: List of test objects
    """

    if not args.tests:
        # Get the last series ran by this user
        series_id = series.TestSeries.load_user_series_id(pav_cfg)
        if series_id is not None:
            args.tests.append(series_id)
        else:
            raise commands.CommandError(
                "No tests specified and no last series was found."
            )

    test_list = []

    for test_id in args.tests:
        # Series
        if test_id.startswith('s'):
            try:
                test_list.extend(series.TestSeries.from_id(pav_cfg,
                                                           test_id).tests)
            except series.TestSeriesError as err:
                output.fprint(
                    "Suite {} could not be found.\n{}"
                    .format(test_id, err),
                    file=errfile,
                    color=output.RED
                )
                continue
        # Test
        else:
            test_list.append(test_id)

    test_list = list(map(int, test_list))
    return test_list
Ejemplo n.º 8
0
def configs_to_tests(pav_cfg,
                     proto_tests: List[test_config.ProtoTest],
                     mb_tracker: Union[MultiBuildTracker, None] = None,
                     build_only: bool = False,
                     rebuild: bool = False,
                     outfile: TextIO = None) -> List[TestRun]:
    """Convert configs/var_man tuples into actual
    tests.

    :param pav_cfg: The Pavilion config
    :param proto_tests: A list of test configs.
    :param mb_tracker: The build tracker.
    :param build_only: Whether to only build these tests.
    :param rebuild: After figuring out what build to use, rebuild it.
    :param outfile: Output file for printing messages
    """

    test_list = []
    progress = 0
    tot_tests = len(proto_tests)

    for ptest in proto_tests:
        try:
            test_list.append(
                TestRun(pav_cfg=pav_cfg,
                        config=ptest.config,
                        var_man=ptest.var_man,
                        build_tracker=mb_tracker,
                        build_only=build_only,
                        rebuild=rebuild))
            progress += 1.0 / tot_tests
            if outfile is not None:
                output.fprint("Creating Test Runs: {:.0%}".format(progress),
                              file=outfile,
                              end='\r')
        except (TestRunError, TestConfigError) as err:
            raise commands.CommandError(err.args[0])

    if outfile is not None:
        output.fprint('', file=outfile)

    return test_list
Ejemplo n.º 9
0
def get_tests(pav_cfg, tests: List['str'], errfile: TextIO) -> List[int]:
    """Convert a list of test id's and series id's into a list of test id's.

    :param pav_cfg: The pavilion config
    :param tests: A list of tests or test series names.
    :param errfile: stream to output errors as needed
    :return: List of test objects
    """

    tests = [str(test) for test in tests.copy()]

    if not tests:
        # Get the last series ran by this user
        series_id = series_util.load_user_series_id(pav_cfg)
        if series_id is not None:
            tests.append(series_id)
        else:
            raise commands.CommandError(
                "No tests specified and no last series was found.")

    test_list = []

    for test_id in tests:
        # Series start with 's', like 'snake'.
        if test_id.startswith('s'):
            try:
                test_list.extend(
                    series.TestSeries.from_id(pav_cfg, test_id).tests)
            except series_util.TestSeriesError as err:
                output.fprint("Suite {} could not be found.\n{}".format(
                    test_id, err),
                              file=errfile,
                              color=output.RED)
                continue
        # Test
        else:
            test_list.append(test_id)

    return list(map(int, test_list))
Ejemplo n.º 10
0
    def _get_tests(self, pav_cfg, host, test_files, tests, modes, overrides,
                   sys_vars):
        """Translate a general set of pavilion test configs into the final,
        resolved configurations. These objects will be organized in a
        dictionary by scheduler, and have a scheduler object instantiated and
        attached.
        :param pav_cfg: The pavilion config
        :param str host: The host config to target these tests with
        :param list(str) modes: The mode configs to use.
        :param list(Path) test_files: Files containing a newline separated
            list of tests.
        :param list(str) tests: The tests to run.
        :param list(str) overrides: Overrides to apply to the configurations.
        :param system_variables.SysVarDict sys_vars: The system variables dict.
        :returns: A dictionary (by scheduler type name) of lists of test
            configs.
        """
        self.logger.debug("Finding Configs")

        # Use the sys_host if a host isn't specified.
        if host is None:
            host = sys_vars.get('sys_name')

        tests = list(tests)
        for file in test_files:
            try:
                with file.open() as test_file:
                    for line in test_file.readlines():
                        line = line.strip()
                        if line and not line.startswith('#'):
                            tests.append(line)
            except (OSError, IOError) as err:
                msg = "Could not read test file {}: {}".format(file, err)
                self.logger.error(msg)
                raise commands.CommandError(msg)

        try:
            raw_tests = test_config.load_test_configs(pav_cfg, host, modes,
                                                      tests)
        except test_config.TestConfigError as err:
            self.logger.error(str(err))
            raise commands.CommandError(str(err))

        raw_tests_by_sched = defaultdict(lambda: [])
        tests_by_scheduler = defaultdict(lambda: [])

        # Apply config overrides.
        for test_cfg in raw_tests:
            # Apply the overrides to each of the config values.
            try:
                test_config.apply_overrides(test_cfg, overrides)
            except test_config.TestConfigError as err:
                msg = 'Error applying overrides to test {} from {}: {}'\
                      .format(test_cfg['name'], test_cfg['suite_path'], err)
                self.logger.error(msg)
                raise commands.CommandError(msg)

            # Resolve all configuration permutations.
            try:
                p_cfg, permutes = test_config.resolve_permutations(
                    test_cfg, pav_cfg.pav_vars, sys_vars)
                for p_var_man in permutes:
                    sched = p_cfg['scheduler'].resolve(p_var_man)
                    raw_tests_by_sched[sched].append((p_cfg, p_var_man))
            except test_config.TestConfigError as err:
                msg = 'Error resolving permutations for test {} from {}: {}'\
                      .format(test_cfg['name'], test_cfg['suite_path'], err)
                self.logger.error(msg)
                raise commands.CommandError(msg)

        # Get the schedulers for the tests, and the scheduler variables.
        # The scheduler variables are based on all of the
        for sched_name in raw_tests_by_sched.keys():
            try:
                sched = schedulers.get_scheduler_plugin(sched_name)
            except KeyError:
                msg = "Could not find scheduler '{}'.".format(sched_name)
                self.logger.error(msg)
                raise commands.CommandError(msg)

            nondeferred_cfg_sctns = schedulers.list_scheduler_plugins()

            # Builds must have the values of all their variables now.
            nondeferred_cfg_sctns.append('build')

            # Set the scheduler variables for each test.
            for test_cfg, test_var_man in raw_tests_by_sched[sched_name]:
                test_var_man.add_var_set('sched', sched.get_vars(test_cfg))

                # Resolve all variables for the test.
                try:
                    resolved_config = test_config.resolve_all_vars(
                        test_cfg,
                        test_var_man,
                        no_deferred_allowed=nondeferred_cfg_sctns)

                except (ResolveError, KeyError) as err:
                    msg = "Error resolving variables in config at '{}': {}"\
                          .format(test_cfg['suite_path'].resolve(test_var_man),
                                  err)
                    self.logger.error(msg)
                    raise commands.CommandError(msg)

                tests_by_scheduler[sched.name].append(resolved_config)

        return tests_by_scheduler
Ejemplo n.º 11
0
    def run(self, pav_cfg, args):
        """Run this command."""

        cutoff_date = datetime.today() - timedelta(days=30)

        if args.older_than:
            args.older_than = args.older_than.split()

            if len(args.older_than) == 2:

                if not args.older_than[0].isdigit():
                    raise commands.CommandError(
                        "Invalid `--older-than` value."
                    )

                if args.older_than[1] in ['minute', 'minutes']:
                    cutoff_date = datetime.today() - timedelta(
                        minutes=int(args.older_than[0]))
                elif args.older_than[1] in ['hour', 'hours']:
                    cutoff_date = datetime.today() - timedelta(
                        hours=int(args.older_than[0]))
                elif args.older_than[1] in ['day', 'days']:
                    cutoff_date = datetime.today() - timedelta(
                        days=int(args.older_than[0]))
                elif args.older_than[1] in ['week', 'weeks']:
                    cutoff_date = datetime.today() - timedelta(
                        weeks=int(args.older_than[0]))
                elif args.older_than[1] in ['month', 'months']:
                    cutoff_date = datetime.today() - timedelta(
                        days=30*int(args.older_than[0]))
            elif len(args.older_than) == 3:
                date = ' '.join(args.older_than)
                try:
                    cutoff_date = datetime.strptime(date, '%b %d %Y')
                except (TypeError, ValueError):
                    output.fprint("{} is not a valid date."
                                  .format(args.older_than),
                                  file=self.errfile, color=output.RED)
                    return errno.EINVAL
            else:
                output.fprint(
                    "Invalid `--older-than` value.", file=self.errfile,
                    color=output.RED
                )
                return errno.EINVAL
        elif args.all:
            cutoff_date = datetime.today()

        tests_dir = pav_cfg.working_dir / 'test_runs'     # type: Path
        series_dir = pav_cfg.working_dir / 'series'       # type: Path
        build_dir = pav_cfg.working_dir / 'builds'        # type: Path

        removed_tests = 0
        removed_series = 0
        removed_builds = 0

        used_builds = set()

        # Clean Tests
        output.fprint("Removing Tests...", file=self.outfile,
                      color=output.GREEN)
        for test_path in tests_dir.iterdir():
            test = test_path.name
            try:
                int(test)
            except ValueError:
                # Skip files that aren't numeric
                continue

            # Skip non-directories.
            if not test_path.is_dir():
                continue

            try:
                test_time = datetime.fromtimestamp(test_path.lstat().st_mtime)
            except FileNotFoundError:
                # The file no longer exists. This is a race condition.
                continue

            build_origin_symlink = test_path/'build_origin'
            # 'None' will probably end up in used_builds, but that's ok.
            build_origin = None
            if (build_origin_symlink.exists() and
                    build_origin_symlink.is_symlink() and
                    build_origin_symlink.resolve().exists()):
                build_origin = build_origin_symlink.resolve()

            if test_time > cutoff_date:
                used_builds.add(build_origin)
                continue

            state = None
            try:
                test_obj = TestRun.load(pav_cfg, int(test))
                state = test_obj.status.current().state
            except (TestRunError, TestRunNotFoundError):
                # It's ok if this happens, we'll still remove by date.
                # It is possible the test isn't completely written (a race
                # condition).
                pass
            except PermissionError as err:
                err = str(err).split("'")
                output.fprint("Permission Error: {} cannot be removed"
                              .format(err[1]), file=self.errfile, color=31)
                continue

            if state in (STATES.RUNNING, STATES.SCHEDULED):
                used_builds.add(build_origin)
                continue

            try:
                shutil.rmtree(test_path.as_posix())
                if args.verbose:
                    output.fprint("Removed test {}".format(test_path),
                                  file=self.outfile)
                removed_tests += 1
            except OSError as err:
                output.fprint(
                    "Could not remove test {}: {}"
                    .format(test_path, err),
                    color=output.YELLOW, file=self.errfile)

        # Clean Series
        output.fprint("Removing Series...", file=self.outfile,
                      color=output.GREEN)
        for series in series_dir.iterdir():
            for test in series.iterdir():
                if (test.is_symlink() and
                        test.exists() and
                        test.resolve().exists()):
                    # This test is still present, so keep the series.
                    break
            else:
                # This series has no remaining tests, we can delete it.
                try:
                    shutil.rmtree(series.as_posix())
                    removed_series += 1
                except OSError as err:
                    output.fprint(
                        "Could not remove series {}: {}"
                        .format(series, err),
                        color=output.YELLOW, file=self.errfile
                    )

        # Clean Builds
        output.fprint("Removing Builds...", file=self.outfile,
                      color=output.GREEN)
        for build in build_dir.iterdir():
            if build in used_builds:
                continue

            try:
                shutil.rmtree(build.as_posix())
                if args.verbose:
                    output.fprint("Removed build", build, file=self.outfile)
            except OSError as err:
                output.fprint(
                    "Could not remove build {}: {}"
                    .format(build, err),
                    color=output.YELLOW, file=self.errfile)

        output.fprint("Removed {tests} tests, {series} series, and {builds} "
                      "builds."
                      .format(tests=removed_tests, series=removed_series,
                              builds=removed_builds),
                      color=output.GREEN, file=self.outfile)
        return 0
Ejemplo n.º 12
0
    def get_tests(self, pav_config, args):
        """Translate a general set of pavilion test configs into the final,
        resolved configuration objects. These objects will be organized in a
        dictionary by scheduler, and have a scheduler object instantiated and
        attached.
        :returns: A dictionary (by scheduler type name) of lists of test
            objects
        """
        self.logger.DEBUG("Finding Configs")

        # Use the sys_host if a host isn't specified.
        if args.host is None:
            host = pav_config.sys_vars.get('sys_host')
        else:
            host = args.host

        tests = args.tests
        for file in args.files:
            try:
                with open(file) as test_file:
                    for line in test_file.readlines():
                        line = line.strip()
                        if line and not line.startswith('#'):
                            tests.append(line)
            except (OSError, IOError) as err:
                msg = "Could not read test file {}: {}".format(file, err)
                self.logger.error(msg)
                raise commands.CommandError(msg)

        raw_tests = config_utils.get_tests(pav_config, host, args.mode, tests)
        raw_tests_by_sched = defaultdict(lambda: [])
        tests_by_scheduler = defaultdict(lambda: [])

        # Apply config overrides.
        for test_cfg in raw_tests:
            # Apply the overrides to each of the config values.
            try:
                config_utils.apply_overrides(test_cfg, args.overrides)
            except config_utils.TestConfigError as err:
                msg = 'Error applying overrides to test {} from {}: {}'\
                      .format(test_cfg['name'], test_cfg['suite_path'], err)
                self.logger.error(msg)
                raise commands.CommandError(msg)

            # Resolve all configuration permutations.
            try:
                for p_cfg, p_var_man in config_utils.resolve_permutations(
                        test_cfg, pav_config.pav_vars, pav_config.sys_vars):

                    sched = p_cfg['scheduler']
                    raw_tests_by_sched[sched].append((p_cfg, p_var_man))
            except config_utils.TestConfigError as err:
                msg = 'Error resolving permutations for test {} from {}: {}'\
                      .format(test_cfg['name'], test_cfg['suite_path'], err)
                self.logger.error(msg)
                raise commands.CommandError(msg)

        # Get the schedulers for the tests, and the scheduler variables. 
        # The scheduler variables are based on all of the
        for sched_name in raw_tests_by_sched.keys():
            try:
                sched = schedulers.get_scheduler_plugin(sched_name)
            except KeyError:
                msg = "Could not find scheduler '{}'.".format(sched_name)
                self.logger.error(msg)
                raise commands.CommandError(msg)

            nondeferred_cfg_sctns = schedulers.list_scheduler_plugins()

            # Builds must have the values of all their variables now.
            nondeferred_cfg_sctns.append('build')

            # Set the echeduler variables for each test.
            for test_cfg, test_var_man in raw_tests_by_sched[sched_name]:
                test_var_man.add_var_set('sched', sched)

                # Resolve all variables for the test.
                try:
                    resolved_config = config_utils.resolve_all_vars(
                        test_cfg,
                        test_var_man,
                        no_deferred_allowed=nondeferred_cfg_sctns)

                except (ResolveError, KeyError) as err:
                    msg = 'Error resolving variables in config: {}'.format(err)
                    self.logger.error(msg)
                    raise commands.CommandError(msg)

                test = PavTest(pav_config, resolved_config)

                tests_by_scheduler[sched.name].append(test)

        return tests_by_scheduler