def __init__(self, context): super(RpmStatusRenderer, self).__init__(context) self.publish_steps_renderer = PublishStepStatusRenderer(context) # Sync Steps self.metadata_last_state = constants.STATE_NOT_STARTED self.download_last_state = constants.STATE_NOT_STARTED self.distribution_sync_last_state = constants.STATE_NOT_STARTED self.errata_last_state = constants.STATE_NOT_STARTED self.comps_last_state = constants.STATE_NOT_STARTED # Publish Steps self.publish_steps_last_state = dict.fromkeys( constants.PUBLISH_STEPS, constants.STATE_NOT_STARTED) self.distribution_publish_last_state = constants.STATE_NOT_STARTED self.generate_metadata_last_state = constants.STATE_NOT_STARTED self.publish_http_last_state = constants.STATE_NOT_STARTED self.publish_https_last_state = constants.STATE_NOT_STARTED # UI Widgets self.metadata_spinner = self.prompt.create_spinner() self.download_bar = self.prompt.create_progress_bar() self.distribution_sync_bar = self.prompt.create_progress_bar() self.errata_spinner = self.prompt.create_spinner() self.comps_spinner = self.prompt.create_spinner() self.packages_bar = self.prompt.create_progress_bar() self.distribution_publish_bar = self.prompt.create_progress_bar() self.generate_metadata_spinner = self.prompt.create_spinner() self.publish_http_spinner = self.prompt.create_spinner() self.publish_https_spinner = self.prompt.create_spinner()
def setUp(self): super(TestRefreshContentSourcesCommand, self).setUp() self.context = MagicMock( config={'output': { 'poll_frequency_in_seconds': 1 }}) self.prompt = Mock() self.context.prompt = self.prompt self.renderer = PublishStepStatusRenderer(self.context) self.command = RefreshContentSourcesCommand(self.context, self.renderer) step_details = { 'source_id': '1', 'succeeded': None, 'url': 'mock-url', 'added_count': '2', 'deleted_count': '0', 'errors': [] } self.step = { reporting_constants.PROGRESS_STEP_TYPE_KEY: u'foo_step', reporting_constants.PROGRESS_STEP_UUID: u'abcde', reporting_constants.PROGRESS_DESCRIPTION_KEY: u'foo description', reporting_constants.PROGRESS_DETAILS_KEY: [step_details], reporting_constants.PROGRESS_STATE_KEY: reporting_constants.STATE_NOT_STARTED, reporting_constants.PROGRESS_ITEMS_TOTAL_KEY: 1, reporting_constants.PROGRESS_NUM_PROCESSED_KEY: 0, reporting_constants.PROGRESS_ERROR_DETAILS_KEY: [] }
def __init__(self, context): super(RpmStatusRenderer, self).__init__(context) self.publish_steps_renderer = PublishStepStatusRenderer(context) # Sync Steps self.metadata_last_state = constants.STATE_NOT_STARTED self.download_last_state = constants.STATE_NOT_STARTED self.distribution_sync_last_state = constants.STATE_NOT_STARTED self.errata_last_state = constants.STATE_NOT_STARTED self.comps_last_state = constants.STATE_NOT_STARTED # Publish Steps self.publish_steps_last_state = dict.fromkeys(constants.PUBLISH_STEPS, constants.STATE_NOT_STARTED) self.distribution_publish_last_state = constants.STATE_NOT_STARTED self.generate_metadata_last_state = constants.STATE_NOT_STARTED self.publish_http_last_state = constants.STATE_NOT_STARTED self.publish_https_last_state = constants.STATE_NOT_STARTED # UI Widgets self.metadata_spinner = self.prompt.create_spinner() self.download_bar = self.prompt.create_progress_bar() self.distribution_sync_bar = self.prompt.create_progress_bar() self.errata_spinner = self.prompt.create_spinner() self.comps_spinner = self.prompt.create_spinner() self.packages_bar = self.prompt.create_progress_bar() self.distribution_publish_bar = self.prompt.create_progress_bar() self.generate_metadata_spinner = self.prompt.create_spinner() self.publish_http_spinner = self.prompt.create_spinner() self.publish_https_spinner = self.prompt.create_spinner()
def __init__(self, context): super(PackageStatusRenderer, self).__init__(context) self.publish_steps_renderer = PublishStepStatusRenderer(context) # Publish Steps self.publish_steps_last_state = dict.fromkeys( constants.PUBLISH_STEPS, constants.STATE_NOT_STARTED) self.publish_http_last_state = constants.STATE_NOT_STARTED self.publish_https_last_state = constants.STATE_NOT_STARTED # UI Widgets self.packages_bar = self.prompt.create_progress_bar() self.publish_http_spinner = self.prompt.create_spinner() self.publish_https_spinner = self.prompt.create_spinner()
def initialize(context): structure.ensure_repo_structure(context.cli) upload_manager = _upload_manager(context) repo_section = structure.repo_section(context.cli) repo_section.add_command(repo_create_update.PkgRepoCreateCommand(context)) repo_section.add_command(repo_create_update.PkgRepoUpdateCommand(context)) repo_section.add_command(cudl.DeleteRepositoryCommand(context)) repo_section.add_command(repo_list.RepoListCommand(context)) repo_section.add_command( RepoSearchCommand(context, constants.REPO_NOTE_PKG)) copy_section = structure.repo_copy_section(context.cli) copy_section.add_command(copy_commands.MsiCopyCommand(context)) copy_section.add_command(copy_commands.MsmCopyCommand(context)) copy_section.add_command(copy_commands.AllCopyCommand(context)) remove_section = structure.repo_remove_section(context.cli) remove_section.add_command(remove.MsiRemoveCommand(context)) remove_section.add_command(remove.MsmRemoveCommand(context)) contents_section = structure.repo_contents_section(context.cli) contents_section.add_command(contents.SearchMsiCommand(context)) contents_section.add_command(contents.SearchMsmCommand(context)) uploads_section = structure.repo_uploads_section(context.cli) for cls_ in [ package.CreateMsiCommand, package.CreateMsmCommand, upload.ResumeCommand, upload.CancelCommand, upload.ListCommand ]: uploads_section.add_command(cls_(context, upload_manager)) sync_section = structure.repo_sync_section(context.cli) renderer = status.PackageStatusRenderer(context) sync_section.add_command( sync_publish.RunSyncRepositoryCommand(context, renderer)) sync_section.add_command(sync_publish.SyncStatusCommand(context, renderer)) publish_section = structure.repo_publish_section(context.cli) renderer = PublishStepStatusRenderer(context) distributor_id = ids.TYPE_ID_DISTRIBUTOR_WIN publish_section.add_command( sync_publish.RunPublishRepositoryCommand(context, renderer, distributor_id)) publish_section.add_command( sync_publish.PublishStatusCommand(context, renderer)) sync_schedules_section = structure.repo_sync_schedules_section(context.cli) sync_schedules_section.add_command( sync_schedules.PkgCreateScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.PkgUpdateScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.PkgDeleteScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.PkgListScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.PkgNextRunCommand(context))
def __init__(self, context): """ :param context: The client context. :type context: pulp.client.extensions.core.ClientContext """ super(SourcesSection, self).__init__(self.NAME, self.DESCRIPTION) self.add_command(ListCommand(context)) renderer = PublishStepStatusRenderer(context) self.add_command(RefreshContentSourcesCommand(context, renderer))
class PackageStatusRenderer(StatusRenderer): def __init__(self, context): super(PackageStatusRenderer, self).__init__(context) self.publish_steps_renderer = PublishStepStatusRenderer(context) # Publish Steps self.publish_steps_last_state = dict.fromkeys( constants.PUBLISH_STEPS, constants.STATE_NOT_STARTED) self.publish_http_last_state = constants.STATE_NOT_STARTED self.publish_https_last_state = constants.STATE_NOT_STARTED # UI Widgets self.packages_bar = self.prompt.create_progress_bar() self.publish_http_spinner = self.prompt.create_spinner() self.publish_https_spinner = self.prompt.create_spinner() def display_report(self, progress_report): """ Displays the contents of the progress report to the user. This will aggregate the calls to render individual sections of the report. """ # There's a small race condition where the task will indicate it's # begun running but the importer has yet to submit a progress report # (or it has yet to be saved into the task). This should be alleviated # by the if statements below. try: # Sync Steps if ids.TYPE_ID_IMPORTER_WIN in progress_report: pass # Publish Steps if ids.TYPE_ID_DISTRIBUTOR_WIN in progress_report: # Proxy to the standard renderer self.publish_steps_renderer.display_report(progress_report) except CancelException: self.prompt.render_failure_message(_('Operation canceled.'))
class PackageStatusRenderer(StatusRenderer): def __init__(self, context): super(PackageStatusRenderer, self).__init__(context) self.publish_steps_renderer = PublishStepStatusRenderer(context) # Publish Steps self.publish_steps_last_state = dict.fromkeys( constants.PUBLISH_STEPS, constants.STATE_NOT_STARTED) self.publish_http_last_state = constants.STATE_NOT_STARTED self.publish_https_last_state = constants.STATE_NOT_STARTED # UI Widgets self.packages_bar = self.prompt.create_progress_bar() self.publish_http_spinner = self.prompt.create_spinner() self.publish_https_spinner = self.prompt.create_spinner() def display_report(self, progress_report): """ Displays the contents of the progress report to the user. This will aggregate the calls to render individual sections of the report. """ # There's a small race condition where the task will indicate it's # begun running but the importer has yet to submit a progress report # (or it has yet to be saved into the task). This should be alleviated # by the if statements below. try: # Sync Steps if ids.TYPE_ID_IMPORTER in progress_report: pass # Publish Steps if ids.TYPE_ID_DISTRIBUTOR in progress_report: # Proxy to the standard renderer self.publish_steps_renderer.display_report(progress_report) except CancelException: self.prompt.render_failure_message(_('Operation canceled.'))
def __init__(self, context): """ @param context: @type context: pulp.client.extensions.core.ClientContext """ PulpCliSection.__init__(self, 'repo', _('list repositories and manage repo groups')) self.context = context self.prompt = context.prompt # for easier access self.add_command(repo_commands.ListRepositoriesCommand(context, include_all_flag=False)) self.add_command(DownloadRepositoryCommand(context, PublishStepStatusRenderer(context))) # Subsections self.add_subsection(RepoGroupSection(context)) self.add_subsection(RepoHistorySection(context))
def initialize(context): structure.ensure_repo_structure(context.cli) upload_manager = _upload_manager(context) repo_section = structure.repo_section(context.cli) repo_section.add_command(repo_create_update.RpmRepoCreateCommand(context)) repo_section.add_command(repo_create_update.RpmRepoUpdateCommand(context)) repo_section.add_command(cudl.DeleteRepositoryCommand(context)) repo_section.add_command(repo_list.RpmRepoListCommand(context)) repo_section.add_command( RepoSearchCommand(context, constants.REPO_NOTE_RPM)) copy_section = structure.repo_copy_section(context.cli) copy_section.add_command(copy_commands.RpmCopyCommand(context)) copy_section.add_command(copy_commands.ErrataCopyCommand(context)) copy_section.add_command(copy_commands.DistributionCopyCommand(context)) copy_section.add_command(copy_commands.PackageGroupCopyCommand(context)) copy_section.add_command(copy_commands.PackageCategoryCopyCommand(context)) copy_section.add_command( copy_commands.PackageEnvironmentCopyCommand(context)) copy_section.add_command(copy_commands.AllCopyCommand(context)) copy_section.add_command(copy_commands.SrpmCopyCommand(context)) copy_section.add_command(copy_commands.YumRepoMetadataFileCommand(context)) copy_section.add_command(copy_commands.DrpmCopyCommand(context)) # Disabled as per 950690. We'll likely be able to add these back once the new # yum importer is finished and DRPMs are properly handled. # copy_section.add_command(copy_commands.DrpmCopyCommand(context)) remove_section = structure.repo_remove_section(context.cli) remove_section.add_command(remove.RpmRemoveCommand(context)) remove_section.add_command(remove.SrpmRemoveCommand(context)) remove_section.add_command(remove.DrpmRemoveCommand(context)) remove_section.add_command(remove.ErrataRemoveCommand(context)) remove_section.add_command(remove.PackageGroupRemoveCommand(context)) remove_section.add_command(remove.PackageCategoryRemoveCommand(context)) remove_section.add_command(remove.PackageEnvironmentRemoveCommand(context)) remove_section.add_command(remove.DistributionRemoveCommand(context)) remove_section.add_command(remove.YumMetadataFileRemoveCommand(context)) contents_section = structure.repo_contents_section(context.cli) contents_section.add_command(contents.SearchRpmsCommand(context)) contents_section.add_command(contents.SearchDrpmsCommand(context)) contents_section.add_command(contents.SearchSrpmsCommand(context)) contents_section.add_command(contents.SearchPackageGroupsCommand(context)) contents_section.add_command( contents.SearchPackageCategoriesCommand(context)) contents_section.add_command( contents.SearchPackageEnvironmentsCommand(context)) contents_section.add_command(contents.SearchDistributionsCommand(context)) contents_section.add_command(contents.SearchErrataCommand(context)) contents_section.add_command( contents.SearchYumMetadataFileCommand(context)) # Add the group section, all its subsections, and commands group_export_section = structure.repo_group_export_section(context.cli) renderer = PublishStepStatusRenderer(context) group_export_section.add_command( export.RpmGroupExportCommand(context, renderer)) group_export_section.add_command( export.GroupExportStatusCommand(context, renderer)) uploads_section = structure.repo_uploads_section(context.cli) uploads_section.add_command( package.CreateRpmCommand(context, upload_manager)) uploads_section.add_command( package.CreateSrpmCommand(context, upload_manager)) uploads_section.add_command( errata.CreateErratumCommand(context, upload_manager)) uploads_section.add_command( package_group.CreatePackageGroupCommand(context, upload_manager)) uploads_section.add_command( category.CreatePackageCategoryCommand(context, upload_manager)) uploads_section.add_command( comps.CreateCompsCommand(context, upload_manager)) uploads_section.add_command( environment.CreatePackageEnvironmentCommand(context, upload_manager)) uploads_section.add_command(upload.ResumeCommand(context, upload_manager)) uploads_section.add_command(upload.CancelCommand(context, upload_manager)) uploads_section.add_command(upload.ListCommand(context, upload_manager)) sync_section = structure.repo_sync_section(context.cli) renderer = status.RpmStatusRenderer(context) sync_section.add_command( sync_publish.RunSyncRepositoryCommand(context, renderer)) sync_section.add_command(sync_publish.SyncStatusCommand(context, renderer)) publish_section = structure.repo_publish_section(context.cli) renderer = PublishStepStatusRenderer(context) distributor_id = ids.TYPE_ID_DISTRIBUTOR_YUM publish_section.add_command( sync_publish.RunPublishRepositoryCommand(context, renderer, distributor_id)) publish_section.add_command( sync_publish.PublishStatusCommand(context, renderer)) repo_export_section = structure.repo_export_section(context.cli) renderer = PublishStepStatusRenderer(context) repo_export_section.add_command(export.RpmExportCommand(context, renderer)) repo_export_section.add_command( sync_publish.PublishStatusCommand(context, renderer, description=DESC_EXPORT_STATUS)) sync_schedules_section = structure.repo_sync_schedules_section(context.cli) sync_schedules_section.add_command( sync_schedules.RpmCreateScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.RpmUpdateScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.RpmDeleteScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.RpmListScheduleCommand(context)) sync_schedules_section.add_command( sync_schedules.RpmNextRunCommand(context))
class RpmStatusRenderer(StatusRenderer): def __init__(self, context): super(RpmStatusRenderer, self).__init__(context) self.publish_steps_renderer = PublishStepStatusRenderer(context) # Sync Steps self.metadata_last_state = constants.STATE_NOT_STARTED self.download_last_state = constants.STATE_NOT_STARTED self.distribution_sync_last_state = constants.STATE_NOT_STARTED self.errata_last_state = constants.STATE_NOT_STARTED self.comps_last_state = constants.STATE_NOT_STARTED self.purge_duplicates_last_state = constants.STATE_NOT_STARTED # Publish Steps self.publish_steps_last_state = dict.fromkeys(constants.PUBLISH_STEPS, constants.STATE_NOT_STARTED) self.distribution_publish_last_state = constants.STATE_NOT_STARTED self.generate_metadata_last_state = constants.STATE_NOT_STARTED self.publish_http_last_state = constants.STATE_NOT_STARTED self.publish_https_last_state = constants.STATE_NOT_STARTED # UI Widgets self.metadata_spinner = self.prompt.create_spinner() self.download_bar = self.prompt.create_progress_bar() self.download_spinner = self.prompt.create_spinner() self.distribution_sync_bar = self.prompt.create_progress_bar() self.errata_spinner = self.prompt.create_spinner() self.comps_spinner = self.prompt.create_spinner() self.purge_duplicates_spinner = self.prompt.create_spinner() self.packages_bar = self.prompt.create_progress_bar() self.distribution_publish_bar = self.prompt.create_progress_bar() self.generate_metadata_spinner = self.prompt.create_spinner() self.publish_http_spinner = self.prompt.create_spinner() self.publish_https_spinner = self.prompt.create_spinner() def display_report(self, progress_report): """ Displays the contents of the progress report to the user. This will aggregate the calls to render individual sections of the report. """ # There's a small race condition where the task will indicate it's # begun running but the importer has yet to submit a progress report # (or it has yet to be saved into the task). This should be alleviated # by the if statements below. try: # Sync Steps if 'yum_importer' in progress_report: self.render_metadata_step(progress_report) self.render_download_step(progress_report) self.render_distribution_sync_step(progress_report) self.render_errata_step(progress_report) self.render_comps_step(progress_report) self.render_purge_duplicates_step(progress_report) # Publish Steps if ids.YUM_DISTRIBUTOR_ID in progress_report: # Proxy to the standard renderer self.publish_steps_renderer.display_report(progress_report) except CancelException: self.prompt.render_failure_message(_('Operation canceled.')) def check_for_cancelled_state(self, state): if state == constants.STATE_CANCELLED: raise CancelException # -- render sync steps ----------------------------------------------------- def render_metadata_step(self, progress_report): # Example Data: # "metadata": { # "state": "FINISHED" # } current_state = progress_report['yum_importer']['metadata']['state'] self.check_for_cancelled_state(current_state) def update_func(new_state): self.metadata_last_state = new_state render_general_spinner_step(self.prompt, self.metadata_spinner, current_state, self.metadata_last_state, _('Downloading metadata...'), update_func) if self.metadata_last_state == constants.STATE_FAILED: self.prompt.render_failure_message(progress_report['yum_importer']['metadata']['error']) def render_distribution_sync_step(self, progress_report): data = progress_report['yum_importer']['distribution'] state = data['state'] self.check_for_cancelled_state(state) # Render nothing if we haven't begun yet or if this step is skipped if state in (constants.STATE_NOT_STARTED, constants.STATE_SKIPPED): return # Only render this on the first non-not-started state if self.distribution_sync_last_state == constants.STATE_NOT_STARTED: self.prompt.write(_('Downloading distribution files...')) if (state in (constants.STATE_RUNNING, constants.STATE_COMPLETE) and self.distribution_sync_last_state not in constants.COMPLETE_STATES): render_itemized_in_progress_state(self.prompt, data, _('distributions'), self.distribution_sync_bar, state) elif state in constants.STATE_FAILED and \ self.distribution_sync_last_state not in constants.COMPLETE_STATES: self.prompt.render_spacer() self.prompt.render_failure_message(_('Errors encountered during distribution sync:')) # TODO: read this from config # display_error_count = self.context.extension_config.getint('main', # 'num_display_errors') display_error_count = 5 num_errors = min(len(data['error_details']), display_error_count) if num_errors > 0: # Each error is a list of filename and dict of details # Example: # "error_details": [ # [ # "file:///mnt/iso/f18/images/boot.iso", # { # "response_code": 0, # "error_message": "Couldn't open file /mnt/iso/f18/images/boot.iso", # "error_code": 37 # } # ] # ], for i in range(0, num_errors): error = data['error_details'][i] message_data = { 'filename': error[0], 'message': error[1].get('error_message'), 'code': error[1].get('error_code'), } template = 'File: %(filename)s\n' template += 'Error Code: %(code)s\n' template += 'Error Message: %(message)s' message = template % message_data self.prompt.render_failure_message(message) self.prompt.render_spacer() self.distribution_sync_last_state = state def render_download_step(self, progress_report): """ :param progress_report: A dictionary containing a key called 'yum_importer' that is a dictionary that has a key that is 'content' that indexes a pulp_rpm.plugins.importers.yum.report.ContentReport. :type progress_report: dict """ data = progress_report['yum_importer']['content'] state = data['state'] self.check_for_cancelled_state(state) # Render nothing if we haven't begun yet or if this step is skipped if state in (constants.STATE_NOT_STARTED, constants.STATE_SKIPPED): return details = data['details'] # Only render this on the first non-not-started state if self.download_last_state == constants.STATE_NOT_STARTED: self.prompt.write(_('Downloading repository content...')) # If it's running or finished, the output is still the same. This way, # if the status is viewed after this step, the content download # summary is still available. if state in (constants.STATE_RUNNING, constants.STATE_COMPLETE) and \ self.download_last_state not in constants.COMPLETE_STATES: self.download_last_state = state template = _('RPMs: %(rpm_done)s/%(rpm_total)s items\n' 'Delta RPMs: %(drpm_done)s/%(drpm_total)s items\n') bar_message = template % details overall_done = data['size_total'] - data['size_left'] overall_total = data['size_total'] try: self.download_bar.render(overall_done, overall_total, message=bar_message) except ZeroDivisionError: # overall_total is 0 -- render a spinner until we know enough to make a bar self.download_spinner.next() if state == constants.STATE_COMPLETE: # If all of the packages are already downloaded and up to date, the overall total # will be 0. In that event, fill the download bar since the state is complete. if overall_total == 0: overall_total = overall_done = 1 self.download_bar.render(overall_done, overall_total, message=bar_message) self.prompt.write(_('... completed')) self.prompt.render_spacer() # If there are any errors, write them out here # TODO: read this from config # display_error_count = self.context.extension_config.getint('main', # 'num_display_errors') display_error_count = 5 num_errors = min(len(data['error_details']), display_error_count) if num_errors > 0: self.prompt.render_failure_message( _('Individual package errors encountered during sync:')) for i in range(0, num_errors): error = data['error_details'][i] if error.get(constants.ERROR_CODE) == constants.ERROR_CHECKSUM_TYPE_UNKNOWN: message_data = { 'name': error[constants.NAME], 'checksum_type': error[constants.CHECKSUM_TYPE], 'accepted': ','.join( error.get(constants.ACCEPTED_CHECKSUM_TYPES, [])) } template = _('Package: %(name)s\nError: An invalid checksum type ' '(%(checksum_type)s) was detected.\n' 'Accepted checksum types: %(accepted)s') elif error.get(constants.ERROR_CODE) == constants.ERROR_INVALID_PACKAGE_SIG: message_data = { 'count': error['count'], } template = _('%(count)s packages failed signature filter and were not ' 'imported.') elif error.get( constants.ERROR_CODE) == constants.ERROR_CHECKSUM_VERIFICATION: message_data = { 'name': error[constants.NAME], } template = _('Package: %(name)s\nError: An invalid checksum was ' 'detected.') elif error.get(constants.ERROR_CODE) == constants.ERROR_SIZE_VERIFICATION: message_data = { 'name': error[constants.NAME], } template = _('Package: %(name)s\nError: The size did not match the ' 'value specified in the repository metadata.') else: error_msg = error.get('error', '') traceback = '\n'.join(error.get('traceback', [])) message_data = { 'name': error['url'], 'error': error_msg, 'traceback': traceback } template = 'Package: %(name)s\n' template += 'Error: %(error)s\n' if message_data["traceback"]: template += 'Traceback:\n' template += '%(traceback)s' message = template % message_data self.prompt.render_failure_message(message) self.prompt.render_spacer() elif state == constants.STATE_FAILED and self.download_last_state not in \ constants.COMPLETE_STATES: # This state means something went horribly wrong. There won't be # individual package error details which is why they are only # displayed above and not in this case. self.prompt.write(_('... failed')) self.download_last_state = constants.STATE_FAILED def render_errata_step(self, progress_report): # Example Data: # "errata": { # "state": "FINISHED", # "num_errata": 0 # } current_state = progress_report['yum_importer']['errata']['state'] self.check_for_cancelled_state(current_state) if current_state in (constants.STATE_NOT_STARTED, constants.STATE_SKIPPED): return def update_func(new_state): self.errata_last_state = new_state render_general_spinner_step(self.prompt, self.errata_spinner, current_state, self.errata_last_state, _('Importing errata...'), update_func) def render_comps_step(self, progress_report): # Example Data: # "comps": { # "state": "FINISHED", # "num_available_groups": 0, # "num_available_categories": 0, # "num_orphaned_groups": 0, # "num_orphaned_categories": 0, # "num_new_groups": 0, # "num_new_categories": 0, # } current_state = progress_report['yum_importer']['comps']['state'] def update_func(new_state): self.comps_last_state = new_state render_general_spinner_step(self.prompt, self.comps_spinner, current_state, self.comps_last_state, _('Importing package groups/categories...'), update_func) def render_purge_duplicates_step(self, progress_report): # Example Data: # "purge_duplicates": { # "state": "FINISHED" # } current_state = progress_report['yum_importer']['purge_duplicates']['state'] def update_func(new_state): self.purge_duplicates_last_state = new_state render_general_spinner_step(self.prompt, self.purge_duplicates_spinner, current_state, self.purge_duplicates_last_state, _('Cleaning duplicate packages...'), update_func)
class RpmStatusRenderer(StatusRenderer): def __init__(self, context): super(RpmStatusRenderer, self).__init__(context) self.publish_steps_renderer = PublishStepStatusRenderer(context) # Sync Steps self.metadata_last_state = constants.STATE_NOT_STARTED self.download_last_state = constants.STATE_NOT_STARTED self.distribution_sync_last_state = constants.STATE_NOT_STARTED self.errata_last_state = constants.STATE_NOT_STARTED self.comps_last_state = constants.STATE_NOT_STARTED # Publish Steps self.publish_steps_last_state = dict.fromkeys( constants.PUBLISH_STEPS, constants.STATE_NOT_STARTED) self.distribution_publish_last_state = constants.STATE_NOT_STARTED self.generate_metadata_last_state = constants.STATE_NOT_STARTED self.publish_http_last_state = constants.STATE_NOT_STARTED self.publish_https_last_state = constants.STATE_NOT_STARTED # UI Widgets self.metadata_spinner = self.prompt.create_spinner() self.download_bar = self.prompt.create_progress_bar() self.distribution_sync_bar = self.prompt.create_progress_bar() self.errata_spinner = self.prompt.create_spinner() self.comps_spinner = self.prompt.create_spinner() self.packages_bar = self.prompt.create_progress_bar() self.distribution_publish_bar = self.prompt.create_progress_bar() self.generate_metadata_spinner = self.prompt.create_spinner() self.publish_http_spinner = self.prompt.create_spinner() self.publish_https_spinner = self.prompt.create_spinner() def display_report(self, progress_report): """ Displays the contents of the progress report to the user. This will aggregate the calls to render individual sections of the report. """ # There's a small race condition where the task will indicate it's # begun running but the importer has yet to submit a progress report # (or it has yet to be saved into the task). This should be alleviated # by the if statements below. try: # Sync Steps if 'yum_importer' in progress_report: self.render_metadata_step(progress_report) self.render_download_step(progress_report) self.render_distribution_sync_step(progress_report) self.render_errata_step(progress_report) self.render_comps_step(progress_report) # Publish Steps if ids.YUM_DISTRIBUTOR_ID in progress_report: # Proxy to the standard renderer self.publish_steps_renderer.display_report(progress_report) except CancelException: self.prompt.render_failure_message(_('Operation canceled.')) def check_for_cancelled_state(self, state): if state == constants.STATE_CANCELLED: raise CancelException # -- render sync steps ----------------------------------------------------- def render_metadata_step(self, progress_report): # Example Data: # "metadata": { # "state": "FINISHED" # } current_state = progress_report['yum_importer']['metadata']['state'] self.check_for_cancelled_state(current_state) def update_func(new_state): self.metadata_last_state = new_state render_general_spinner_step(self.prompt, self.metadata_spinner, current_state, self.metadata_last_state, _('Downloading metadata...'), update_func) if self.metadata_last_state == constants.STATE_FAILED: self.prompt.render_failure_message( progress_report['yum_importer']['metadata']['error']) def render_distribution_sync_step(self, progress_report): data = progress_report['yum_importer']['distribution'] state = data['state'] self.check_for_cancelled_state(state) # Render nothing if we haven't begun yet or if this step is skipped if state in (constants.STATE_NOT_STARTED, constants.STATE_SKIPPED): return # Only render this on the first non-not-started state if self.distribution_sync_last_state == constants.STATE_NOT_STARTED: self.prompt.write(_('Downloading distribution files...')) if (state in (constants.STATE_RUNNING, constants.STATE_COMPLETE) and self.distribution_sync_last_state not in constants.COMPLETE_STATES): render_itemized_in_progress_state(self.prompt, data, _('distributions'), self.distribution_sync_bar, state) elif state in constants.STATE_FAILED and \ self.distribution_sync_last_state not in constants.COMPLETE_STATES: self.prompt.render_spacer() self.prompt.render_failure_message( _('Errors encountered during distribution sync:')) # TODO: read this from config # display_error_count = self.context.extension_config.getint('main', # 'num_display_errors') display_error_count = 5 num_errors = min(len(data['error_details']), display_error_count) if num_errors > 0: # Each error is a list of filename and dict of details # Example: # "error_details": [ # [ # "file:///mnt/iso/f18/images/boot.iso", # { # "response_code": 0, # "error_message": "Couldn't open file /mnt/iso/f18/images/boot.iso", # "error_code": 37 # } # ] # ], for i in range(0, num_errors): error = data['error_details'][i] message_data = { 'filename': error[0], 'message': error[1].get('error_message'), 'code': error[1].get('error_code'), } template = 'File: %(filename)s\n' template += 'Error Code: %(code)s\n' template += 'Error Message: %(message)s' message = template % message_data self.prompt.render_failure_message(message) self.prompt.render_spacer() self.distribution_sync_last_state = state def render_download_step(self, progress_report): """ :param progress_report: A dictionary containing a key called 'yum_importer' that is a dictionary that has a key that is 'content' that indexes a pulp_rpm.plugins.importers.yum.report.ContentReport. :type progress_report: dict """ data = progress_report['yum_importer']['content'] state = data['state'] self.check_for_cancelled_state(state) # Render nothing if we haven't begun yet or if this step is skipped if state in (constants.STATE_NOT_STARTED, constants.STATE_SKIPPED): return details = data['details'] # Only render this on the first non-not-started state if self.download_last_state == constants.STATE_NOT_STARTED: self.prompt.write(_('Downloading repository content...')) # If it's running or finished, the output is still the same. This way, # if the status is viewed after this step, the content download # summary is still available. if state in (constants.STATE_RUNNING, constants.STATE_COMPLETE) and \ self.download_last_state not in constants.COMPLETE_STATES: self.download_last_state = state template = _('RPMs: %(rpm_done)s/%(rpm_total)s items\n' 'Delta RPMs: %(drpm_done)s/%(drpm_total)s items\n') bar_message = template % details overall_done = data['size_total'] - data['size_left'] overall_total = data['size_total'] # If all of the packages are already downloaded and up to date, # the total bytes to process will be 0. This means the download # step is basically finished, so fill the progress bar. if overall_total == 0: overall_total = overall_done = 1 self.download_bar.render(overall_done, overall_total, message=bar_message) if state == constants.STATE_COMPLETE: self.prompt.write(_('... completed')) self.prompt.render_spacer() # If there are any errors, write them out here # TODO: read this from config # display_error_count = self.context.extension_config.getint('main', # 'num_display_errors') display_error_count = 5 num_errors = min(len(data['error_details']), display_error_count) if num_errors > 0: self.prompt.render_failure_message( _('Individual package errors encountered during sync:') ) for i in range(0, num_errors): error = data['error_details'][i] if error.get( constants.ERROR_CODE ) == constants.ERROR_CHECKSUM_TYPE_UNKNOWN: message_data = { 'name': error[constants.NAME], 'checksum_type': error[constants.CHECKSUM_TYPE], 'accepted': ','.join( error.get( constants.ACCEPTED_CHECKSUM_TYPES, [])) } template = _( 'Package: %(name)s\nError: An invalid checksum type ' '(%(checksum_type)s) was detected.\n' 'Accepted checksum types: %(accepted)s') elif error.get( constants.ERROR_CODE ) == constants.ERROR_CHECKSUM_VERIFICATION: message_data = { 'name': error[constants.NAME], } template = _( 'Package: %(name)s\nError: An invalid checksum was ' 'detected.') elif error.get(constants.ERROR_CODE ) == constants.ERROR_SIZE_VERIFICATION: message_data = { 'name': error[constants.NAME], } template = _( 'Package: %(name)s\nError: The size did not match the ' 'value specified in the repository metadata.') else: error_msg = error.get('error', '') traceback = '\n'.join(error.get('traceback', [])) message_data = { 'name': error['url'], 'error': error_msg, 'traceback': traceback } template = 'Package: %(name)s\n' template += 'Error: %(error)s\n' if message_data["traceback"]: template += 'Traceback:\n' template += '%(traceback)s' message = template % message_data self.prompt.render_failure_message(message) self.prompt.render_spacer() elif state == constants.STATE_FAILED and self.download_last_state not in \ constants.COMPLETE_STATES: # This state means something went horribly wrong. There won't be # individual package error details which is why they are only # displayed above and not in this case. self.prompt.write(_('... failed')) self.download_last_state = constants.STATE_FAILED def render_errata_step(self, progress_report): # Example Data: # "errata": { # "state": "FINISHED", # "num_errata": 0 # } current_state = progress_report['yum_importer']['errata']['state'] self.check_for_cancelled_state(current_state) if current_state in (constants.STATE_NOT_STARTED, constants.STATE_SKIPPED): return def update_func(new_state): self.errata_last_state = new_state render_general_spinner_step(self.prompt, self.errata_spinner, current_state, self.errata_last_state, _('Importing errata...'), update_func) def render_comps_step(self, progress_report): # Example Data: # "comps": { # "state": "FINISHED", # "num_available_groups": 0, # "num_available_categories": 0, # "num_orphaned_groups": 0, # "num_orphaned_categories": 0, # "num_new_groups": 0, # "num_new_categories": 0, # } current_state = progress_report['yum_importer']['comps']['state'] def update_func(new_state): self.comps_last_state = new_state render_general_spinner_step( self.prompt, self.comps_spinner, current_state, self.comps_last_state, _('Importing package groups/categories...'), update_func)