class FetchingInfo(WidgetWrap): def __init__(self, parent, snap, loop): self.parent = parent self.spinner = Spinner(loop, style='dots') self.spinner.start() self.closed = False text = _("Fetching info for {}").format(snap.name) # | text | # 12 34 self.width = len(text) + 4 cancel = cancel_btn(label=_("Cancel"), on_press=self.close) super().__init__( LineBox( Pile([ ('pack', Text(' ' + text)), ('pack', self.spinner), ('pack', button_pile([cancel])), ]))) def close(self, sender=None): if self.closed: return self.closed = True self.spinner.stop() self.parent.remove_overlay()
def wait_load(self): spinner = Spinner(self.controller.app.aio_loop, style='dots') spinner.start() self._w = screen( [spinner], [ok_btn(label=_("Continue"), on_press=self.done)], excerpt=_("Loading server snaps from store, please wait...")) schedule_task(self._wait_load(spinner))
class LoadingDialog(WidgetWrap): def __init__(self, parent, aio_loop, msg, task_to_cancel): self.parent = parent self.spinner = Spinner(aio_loop, style='dots') self.spinner.start() self.closed = False # | text | # 12 34 self.width = len(msg) + 4 widgets = [ ('pack', Text(' ' + msg)), ('pack', self.spinner), ] if task_to_cancel is not None: self.task_to_cancel = task_to_cancel cancel = cancel_btn(label=_("Cancel"), on_press=self.close) widgets.append(('pack', button_pile([cancel]))) super().__init__(LineBox(Pile(widgets))) def close(self, sender=None): if self.closed: return if sender is not None: self.task_to_cancel.cancel() self.closed = True self.spinner.stop() self.parent.remove_overlay()
def event_start(self, context_id, context_parent_id, message): self.event_finish(context_parent_id) walker = self.event_listbox.base_widget.body spinner = Spinner(self.controller.app.aio_loop) spinner.start() new_line = Columns([ ('pack', Text(message)), ('pack', spinner), ], dividechars=1) self.ongoing[context_id] = len(walker) self._add_line(self.event_listbox, new_line)
def load(self, sender=None): t = self.controller.get_snap_list_task() if t.done(): self.loaded() return spinner = Spinner(self.controller.app.aio_loop, style='dots') spinner.start() self._w = screen( [spinner], [ok_btn(label=_("Continue"), on_press=self.done)], excerpt=_("Loading server snaps from store, please wait...")) schedule_task(self._wait(t, spinner))
def __init__(self, aio_loop): spinner = Spinner(aio_loop, style='dots') spinner.start() text = _("Applying config") # | text | # 12 34 self.width = len(text) + 4 super().__init__( LineBox(Pile([ ('pack', Text(' ' + text)), ('pack', spinner), ])))
def show_apply_spinner(self): s = Spinner(self.controller.loop) s.start() c = TablePile([ TableRow([ Text(_("Applying changes")), s, ]), ], align='center') self.bottom.contents[0:0] = [ (c, self.bottom.options()), (Text(""), self.bottom.options()), ]
def event_start(self, context, message): self.event_finish(context.parent) walker = self.event_listbox.base_widget.body indent = context.full_name().count('/') - 2 if context.get('is-install-context'): indent -= 1 spinner = Spinner(self.controller.app.aio_loop) spinner.start() new_line = Columns([ ('pack', Text(' ' * indent + message)), ('pack', spinner), ], dividechars=1) self.ongoing[context] = len(walker) self._add_line(self.event_listbox, new_line)
def __init__(self, parent): self.parent = parent spinner = Spinner(parent.controller.app.aio_loop, style='dots') spinner.start() text = _("Fetching SSH keys...") button = cancel_btn(label=_("Cancel"), on_press=self.cancel) # | text | # 12 34 self.width = len(text) + 4 super().__init__( LineBox( Pile([ ('pack', Text(' ' + text)), ('pack', spinner), ('pack', button_pile([button])), ])))
class SlowProbing(BaseView): title = _("Waiting for storage probing to complete") def __init__(self, controller): self.controller = controller self.spinner = Spinner(loop=controller.loop, style="dots") self.spinner.start() super().__init__( screen([ Text( _("The installer is probing for block devices to install " "to. Please wait until it completes.")), Text(""), self.spinner, ]))
def _address_rows_for_device(self, dev): address_info = [] dhcp_addresses = dev.dhcp_addresses() for v in 4, 6: if dev.dhcp_enabled(v): label = Text("DHCPv{v}".format(v=v)) addrs = dhcp_addresses.get(v) if addrs: address_info.extend([(label, Text(addr)) for addr in addrs]) elif dev.dhcp_state(v) == "PENDING": s = Spinner(self.controller.app.aio_loop, align='left') s.rate = 0.3 s.start() address_info.append((label, s)) elif dev.dhcp_state(v) == "TIMEDOUT": address_info.append((label, Text(_("timed out")))) elif dev.dhcp_state(v) == "RECONFIGURE": address_info.append((label, Text("-"))) else: address_info.append((label, Text( _("unknown state {state}".format( state=dev.dhcp_state(v)))))) else: addrs = [] for ip in dev.config.get('addresses', []): if addr_version(ip) == v: addrs.append(str(ip)) if addrs: address_info.append( # Network addressing mode (static/dhcp/disabled) (Text(_('static')), Text(', '.join(addrs)))) if len(address_info) == 0: # Do not show an interface as disabled if it is part of a bond or # has a vlan on it. if not dev.is_used: reason = dev.disabled_reason if reason is None: reason = "" # Network addressing mode (static/dhcp/disabled) address_info.append((Text(_("disabled")), Text(reason))) rows = [] for label, value in address_info: rows.append(TableRow([Text(""), label, (2, value)])) return rows
class SlowProbing(BaseView): title = _("Waiting for storage probing to complete") def __init__(self, controller): self.controller = controller self.spinner = Spinner(aio_loop=controller.app.aio_loop, style="dots") self.spinner.start() super().__init__( screen([ Text( _("The installer is probing for block devices to install " "to. Please wait until it completes.")), Text(""), self.spinner, ], [other_btn(_("Back"), on_press=self.cancel)])) def cancel(self, result=None): self.controller.cancel()
def load(self, sender=None): spinner = None called = False def callback(snap_list): nonlocal called called = True if spinner is not None: spinner.stop() if len(snap_list) == 0: self.offer_retry() else: self.make_main_screen(snap_list) self.show_main_screen() self.controller.get_snap_list(callback) if called: return spinner = Spinner(self.controller.loop, style='dots') spinner.start() self._w = screen( [spinner], [ok_btn(label=_("Continue"), on_press=self.done)], excerpt=_("Loading server snaps from store, please wait..."))
def _address_rows(self): address_info = [] for v, dhcp_status, static_config in ( (4, self.dev_info.dhcp4, self.dev_info.static4), (6, self.dev_info.dhcp6, self.dev_info.static6), ): if dhcp_status.enabled: label = Text("DHCPv{v}".format(v=v)) addrs = dhcp_status.addresses if addrs: address_info.extend([(label, Text(addr)) for addr in addrs]) elif dhcp_status.state == DHCPState.PENDING: s = Spinner(self.parent.controller.app.aio_loop, align='left') s.rate = 0.3 s.start() address_info.append((label, s)) elif dhcp_status.state == DHCPState.TIMED_OUT: address_info.append((label, Text(_("timed out")))) elif dhcp_status.state == DHCPState.RECONFIGURE: address_info.append((label, Text("-"))) elif static_config.addresses: address_info.append(( Text(_('static')), Text(', '.join(static_config.addresses)), )) if len(address_info) == 0 and not self.dev_info.is_used: reason = self.dev_info.disabled_reason if reason is None: reason = "" address_info.append((Text(_("disabled")), Text(reason))) rows = [] for label, value in address_info: rows.append(TableRow([Text(""), label, (2, value)])) return rows
class ErrorReportStretchy(Stretchy): def __init__(self, app, report, interrupting=True): self.app = app self.report = report self.interrupting = interrupting self.btns = { 'cancel': other_btn( _("Cancel upload"), on_press=self.cancel_upload), 'close': close_btn(self, _("Close report")), 'continue': close_btn(self, _("Continue")), 'debug_shell': other_btn( _("Switch to a shell"), on_press=self.debug_shell), 'restart': other_btn( _("Restart the installer"), on_press=self.restart), 'submit': other_btn( _("Send to Canonical"), on_press=self.submit), 'submitted': disabled(other_btn(_("Sent to Canonical"))), 'view': other_btn( _("View full report"), on_press=self.view_report), } w = 0 for n, b in self.btns.items(): w = max(w, widget_width(b)) for n, b in self.btns.items(): self.btns[n] = Padding(b, width=w, align='center') self.spinner = Spinner(app.aio_loop, style='dots') self.pile = Pile([]) self._report_changed() super().__init__("", [self.pile], 0, 0) connect_signal(self, 'closed', self.spinner.stop) def pb(self, upload): pb = ProgressBar( normal='progress_incomplete', complete='progress_complete', current=upload.bytes_sent, done=upload.bytes_to_send) def _progress(): pb.done = upload.bytes_to_send pb.current = upload.bytes_sent connect_signal(upload, 'progress', _progress) return pb def _pile_elements(self): btns = self.btns.copy() widgets = [ Text(rewrap(_(error_report_intros[self.report.kind]))), Text(""), ] self.spinner.stop() if self.report.state == ErrorReportState.DONE: widgets.append(btns['view']) widgets.append(Text("")) widgets.append(Text(rewrap(_(submit_text)))) widgets.append(Text("")) if self.report.uploader: if self.upload_pb is None: self.upload_pb = self.pb(self.report.uploader) widgets.append(self.upload_pb) else: if self.report.oops_id: widgets.append(btns['submitted']) else: widgets.append(btns['submit']) self.upload_pb = None fs_label, fs_loc = self.report.persistent_details if fs_label is not None: location_text = _( "The error report has been saved to\n\n {loc}\n\non the " "filesystem with label {label!r}.").format( loc=fs_loc, label=fs_label) widgets.extend([ Text(""), Text(location_text), ]) else: text, spin = error_report_state_descriptions[self.report.state] widgets.append(Text(rewrap(_(text)))) if spin: self.spinner.start() widgets.extend([ Text(""), self.spinner]) if self.report.uploader: widgets.extend([Text(""), btns['cancel']]) elif self.interrupting: if self.report.state != ErrorReportState.INCOMPLETE: text, btn_names = error_report_options[self.report.kind] if text: widgets.extend([Text(""), Text(rewrap(_(text)))]) for b in btn_names: widgets.extend([Text(""), btns[b]]) else: widgets.extend([ Text(""), btns['close'], ]) return widgets def _report_changed(self): self.pile.contents[:] = [ (w, self.pile.options('pack')) for w in self._pile_elements()] if self.pile.selectable(): while not self.pile.focus.selectable(): self.pile.focus_position += 1 def debug_shell(self, sender): self.app.debug_shell() def restart(self, sender): self.app.restart() def view_report(self, sender): self.app.run_command_in_foreground(["less", self.report.path]) def submit(self, sender): self.report.upload() def cancel_upload(self, sender): self.report.uploader.cancelled = True self.report.uploader = None self._report_changed() def opened(self): self.report.mark_seen() connect_signal(self.report, 'changed', self._report_changed) def closed(self): disconnect_signal(self.report, 'changed', self._report_changed)
class RefreshView(BaseView): checking_title = _("Checking for installer update...") checking_excerpt = _( "Contacting the snap store to check if a new version of the " "installer is available.") check_failed_title = _("Contacting the snap store failed") check_failed_excerpt = _("Contacting the snap store failed:") available_title = _("Installer update available") available_excerpt = _( "Version {new} of the installer is now available ({current} is " "currently running).") progress_title = _("Downloading update...") progress_excerpt = _( "Please wait while the updated installer is being downloaded. The " "installer will restart automatically when the download is complete.") update_failed_title = _("Update failed") update_failed_excerpt = _("Downloading and applying the update:") def __init__(self, controller): self.controller = controller self.spinner = Spinner(self.controller.app.aio_loop, style="dots") if self.controller.status.availability == RefreshCheckState.UNKNOWN: self.check_state_checking() else: self.check_state_available() super().__init__(self._w) def check_state_checking(self): self.spinner.start() rows = [self.spinner] buttons = [ done_btn(_("Continue without updating"), on_press=self.done), other_btn(_("Back"), on_press=self.cancel), ] self.title = self.checking_title self.controller.ui.set_header(self.title) self._w = screen(rows, buttons, excerpt=_(self.checking_excerpt)) schedule_task(self._wait_check_result()) async def _wait_check_result(self): try: status = await self.controller.wait_for_check() except Exception as e: self.check_state_failed(e) return if status.availability == RefreshCheckState.AVAILABLE: self.check_state_available() elif self.controller.showing: self.done() def check_state_failed(self, exc): self.spinner.stop() rows = [Text(exc_message(exc))] buttons = button_pile([ done_btn(_("Try again"), on_press=self.try_check_again), done_btn(_("Continue without updating"), on_press=self.done), other_btn(_("Back"), on_press=self.cancel), ]) buttons.base_widget.focus_position = 1 self.title = self.check_failed_title self._w = screen(rows, buttons, excerpt=_(self.check_failed_excerpt)) def try_check_again(self, sender=None): self.controller.snapd_network_changed() self.check_state_checking() def check_state_available(self, sender=None): self.spinner.stop() rows = [ Text(_("You can read the release notes for each version at:")), Text(""), Text("https://github.com/CanonicalLtd/subiquity/releases", align='center'), Text(""), Text( _("If you choose to update, the update will be downloaded " "and the installation will continue from here."), ), ] buttons = button_pile([ done_btn(_("Update to the new installer"), on_press=self.update), done_btn(_("Continue without updating"), on_press=self.done), other_btn(_("Back"), on_press=self.cancel), ]) buttons.base_widget.focus_position = 1 excerpt = _(self.available_excerpt).format( current=self.controller.status.current_snap_version, new=self.controller.status.new_snap_version) self.title = self.available_title self.controller.ui.set_header(self.available_title) self._w = screen(rows, buttons, excerpt=excerpt) if 'update' in self.controller.answers: if self.controller.answers['update']: self.update() else: self.controller.app.aio_loop.call_soon(self.controller.done) def update(self, sender=None): self.spinner.stop() self.lb_tasks = ListBox([]) self.task_to_bar = {} buttons = [ other_btn(_("Cancel update"), on_press=self.check_state_available), ] self.controller.ui.set_header(_(self.progress_title)) self._w = screen(self.lb_tasks, buttons, excerpt=_(self.progress_excerpt)) schedule_task(self._update()) async def _update(self): try: change_id = await self.controller.start_update() except aiohttp.ClientError as e: self.update_failed(exc_message(e)) return while True: change = await self.controller.get_progress(change_id) if change['status'] == 'Done': # Clearly if we got here we didn't get restarted by # snapd/systemctl (dry-run mode or logged in via SSH) self.controller.app.restart(remove_last_screen=False) return if change['status'] not in ['Do', 'Doing']: self.update_failed(change.get('err', "Unknown error")) return self.update_progress(change) await asyncio.sleep(0.1) def try_update_again(self, sender=None): self.check_state_available() def update_failed(self, msg): self.spinner.stop() rows = [Text(msg)] buttons = button_pile([ done_btn(_("Try again"), on_press=self.try_update_again), done_btn(_("Continue without updating"), on_press=self.done), other_btn(_("Back"), on_press=self.cancel), ]) buttons.base_widget.focus_position = 1 self.title = self.update_failed_title self._w = screen(rows, buttons, excerpt=_(self.update_failed_excerpt)) def update_progress(self, change): for task in change['tasks']: tid = task['id'] if task['status'] == "Done": bar = self.task_to_bar.get(tid) if bar is not None: self.lb_tasks.base_widget.body.remove(bar) del self.task_to_bar[tid] if task['status'] == "Doing": if tid not in self.task_to_bar: self.task_to_bar[tid] = bar = TaskProgress() self.lb_tasks.base_widget.body.append(bar) else: bar = self.task_to_bar[tid] bar.update(task) def done(self, result=None): self.spinner.stop() self.controller.done() def cancel(self, result=None): self.spinner.stop() self.controller.cancel()