Beispiel #1
0
        def fix_timestamp(ts_str: str) -> str:
            """Read the timestamp text and get a minimized, formatted value."""
            try:
                when = datetime.datetime.strptime(ts_str,
                                                  '%Y-%m-%d %H:%M:%S.%f')
            except ValueError:
                return ''

            return output.get_relative_timestamp(when)
Beispiel #2
0
    def build_local(self, tests, max_threads, mb_tracker,
                    build_verbosity):
        """Build all tests that request for their build to occur on the
        kickoff host.

        :param list[TestRun] tests: The list of tests to potentially build.
        :param int max_threads: Maximum number of build threads to start.
        :param int build_verbosity: How much info to print during building.
            See the -b/--build-verbose argument for more info.
        :param MultiBuildTracker mb_tracker: The tracker for all builds.
        """

        test_threads = []   # type: [(threading.Thread, None)]
        remote_builds = []

        cancel_event = threading.Event()

        # Generate new build names for each test that is rebuilding.
        # We do this here, even for non_local tests, because otherwise the
        # non-local tests can't tell what was built fresh either on a
        # front-end or by other tests rebuilding on nodes.
        for test in tests:
            if test.rebuild and test.builder.exists():
                test.builder.deprecate()
                test.builder.rename_build()
                test.save_build_name()

        # We don't want to start threads that are just going to wait on a lock,
        # so we'll rearrange the builds so that the uniq build names go first.
        # We'll use this as a stack, so tests that should build first go at
        # the end of the list.
        build_order = []
        # If we've seen a build name, the build can go later.
        seen_build_names = set()

        for test in tests:
            if not test.build_local:
                remote_builds.append(test)
            elif test.builder.name not in seen_build_names:
                build_order.append(test)
                seen_build_names.add(test.builder.name)
            else:
                build_order.insert(0, test)

        # Keep track of what the last message printed per build was.
        # This is for double build verbosity.
        message_counts = {test.id: 0 for test in tests}

        # Used to track which threads are for which tests.
        test_by_threads = {}

        # The length of the last line printed when verbosity == 0.
        last_line_len = None

        if build_verbosity > 0:
            fprint(self.BUILD_STATUS_PREAMBLE
                   .format(when='When', test_id='TestID',
                           state_len=STATES.max_length, state='State'),
                   'Message', file=self.outfile, width=None)

        builds_running = 0
        # Run and track <max_threads> build threads, giving output according
        # to the verbosity level. As threads finish, new ones are started until
        # either all builds complete or a build fails, in which case all tests
        # are aborted.
        while build_order or test_threads:
            # Start a new thread if we haven't hit our limit.
            if build_order and builds_running < max_threads:
                test = build_order.pop()

                test_thread = threading.Thread(
                    target=test.build,
                    args=(cancel_event,)
                )
                test_threads.append(test_thread)
                test_by_threads[test_thread] = test
                test_thread.start()

            # Check if all our threads are alive, and join those that aren't.
            for i in range(len(test_threads)):
                thread = test_threads[i]
                if not thread.is_alive():
                    thread.join()
                    builds_running -= 1
                    test_threads[i] = None
                    test = test_by_threads[thread]

                    # Only output test status after joining a thread.
                    if build_verbosity == 1:
                        notes = mb_tracker.get_notes(test.builder)
                        when, state, msg = notes[-1]
                        when = output.get_relative_timestamp(when)
                        preamble = (self.BUILD_STATUS_PREAMBLE
                                    .format(when=when, test_id=test.id,
                                            state_len=STATES.max_length,
                                            state=state))
                        fprint(preamble, msg, wrap_indent=len(preamble),
                               file=self.outfile, width=None)

            test_threads = [thr for thr in test_threads if thr is not None]

            if cancel_event.is_set():
                for thread in test_threads:
                    thread.join()

                for test in build_order + remote_builds:
                    test.status.set(STATES.ABORTED,
                                    "Build aborted due to failures in other "
                                    "builds.")

                fprint("Build error while building tests. Cancelling runs.",
                       color=output.RED, file=self.outfile)

                for failed_build in mb_tracker.failures():
                    fprint(
                        "Build error for test {f.test.name} (#{f.test.id})."
                        .format(f=failed_build), file=self.errfile)
                    fprint(
                        "See test status file (pav cat {id} status) and/or "
                        "the test build log (pav log build {id})"
                        .format(id=failed_build.test.id), file=self.errfile)

                return errno.EINVAL

            state_counts = mb_tracker.state_counts()
            if build_verbosity == 0:
                # Print a self-clearing one-liner of the counts of the
                # build statuses.
                parts = []
                for state in sorted(state_counts.keys()):
                    parts.append("{}: {}".format(state, state_counts[state]))
                line = ' | '.join(parts)
                if last_line_len is not None:
                    fprint(' '*last_line_len, end='\r', file=self.outfile,
                           width=None)
                last_line_len = len(line)
                fprint(line, end='\r', file=self.outfile, width=None)
            elif build_verbosity > 1:
                for test in tests:
                    seen = message_counts[test.id]
                    msgs = mb_tracker.messages[test.builder][seen:]
                    for when, state, msg in msgs:
                        when = output.get_relative_timestamp(when)
                        state = '' if state is None else state
                        preamble = self.BUILD_STATUS_PREAMBLE.format(
                            when=when, test_id=test.id,
                            state_len=STATES.max_length, state=state)

                        fprint(preamble, msg, wrap_indent=len(preamble),
                               file=self.outfile, width=None)
                    message_counts[test.id] += len(msgs)

            time.sleep(self.BUILD_SLEEP_TIME)

        if build_verbosity == 0:
            # Print a newline after our last status update.
            fprint(width=None, file=self.outfile)

        return 0