def export_repos(self, envs=[]):
        """Dump JuicerRepo() objects for all repos in all environments.

        Note that this has undefined results should a repo exist with
        different configurations in different environments.
        """
        all_envs = envs
        juicer.utils.Log.log_notice("Only exporting repos in environment(s): %s", ", ".join(all_envs))
        all_pulp_repo_names = self.list_repos(envs=all_envs)
        all_pulp_repo_names_uniqued = set()
        num_repos = 0

        # Track name of all processed repos. Update when we've found
        # all environments a PulpRepo lives in.
        repos_processed = []

        for env, repos in all_pulp_repo_names.iteritems():
            juicer.utils.Log.log_debug("Uniqued environment: %s with %s repos", env, int(len(repos)))
            all_pulp_repo_names_uniqued.update(set(repos))

        num_repos += len(all_pulp_repo_names_uniqued)

        widgets = [
            "Exporting: ",
            progressbar.Percentage(),
            " ",
            "(",
            progressbar.SimpleProgress(),
            ") ",
            progressbar.ETA()
        ]
        progress_bar = JuiceBar(num_repos, widgets)

        # Hacky way to get around not easily being able to pass
        # multiple arguments to a function in a multiprocessing pool
        lookup_objects = []

        for repo in all_pulp_repo_names_uniqued:
            lookup_args = juicer.admin.ThreaddedQuery.LookupObject()
            setattr(lookup_args, 'progress_bar', progress_bar)
            setattr(lookup_args, 'all_pulp_repo_names', all_pulp_repo_names)
            setattr(lookup_args, 'all_envs', all_envs)
            setattr(lookup_args, 'ja', self)
            setattr(lookup_args, 'pulp_repo', repo)
            setattr(lookup_args, 'repos_processed', repos_processed)
            lookup_objects.append(lookup_args)

        # TODO: Add the serial/concurrent logic here

        try:
            # Make our thread pool
            p = ThreadPool()
            # Get an AsyncResult object
            r = p.map_async(juicer.admin.ThreaddedQuery.concurrent_pulp_lookup, lookup_objects)
            # TODO: We should probably use p.apply_async here to avoid the crappy lookup_objects hack

            while not r.ready():
                r.wait(1)
        except KeyboardInterrupt:
            juicer.utils.Log.log_error("User pressed ^C during repo export")
            juicer.utils.Log.log_error("Terminating %s worker threads and then exiting", len(p._pool))
            # Prevents any more tasks from being submitted to the
            # pool. Once all the tasks have been completed the worker
            # threads will exit.
            #p.close()
            p.terminate()
            p.join()

        # XXX: End serial/concurrent logic

        progress_bar.finish()
        juicer_repos = [pr.to_juicer_repo() for pr in repos_processed]
        return sorted(juicer_repos, key=lambda d: d['name'].lower())