def unhandled_input_dry_run(self, key): if key == 'ctrl g': import asyncio from systemd import journal async def mock_install(): async with self.install_lock_file.exclusive(): self.install_lock_file.write_content("nowhere") journal.send("starting install", SYSLOG_IDENTIFIER="subiquity") await asyncio.sleep(5) schedule_task(mock_install()) elif key in ['ctrl e', 'ctrl r']: interrupt = key == 'ctrl e' try: 1 / 0 except ZeroDivisionError: self.make_apport_report(ErrorReportKind.UNKNOWN, "example", interrupt=interrupt) elif key == 'ctrl u': 1 / 0 else: super().unhandled_input(key)
async def _probe(self): with self.context.child("_probe") as context: self._crash_reports = {} if isinstance(self.ui.body, ProbingFailed): self.ui.set_body(SlowProbing(self)) schedule_task(self._wait_for_probing()) for (restricted, kind) in [ (False, ErrorReportKind.BLOCK_PROBE_FAIL), (True, ErrorReportKind.DISK_PROBE_FAIL), ]: try: desc = "restricted={}".format(restricted) with context.child("probe_once", desc): await self._probe_once_task.start(restricted) # We wait on the task directly here, not # self._probe_once_task.wait as if _probe_once_task # gets cancelled, we should be cancelled too. await asyncio.wait_for(self._probe_once_task.task, 5.0) except Exception: block_discover_log.exception( "block probing failed restricted=%s", restricted) report = self.app.make_apport_report( kind, "block probing", interrupt=False) self._crash_reports[restricted] = report continue break log.debug("self.ai_data = %s", self.ai_data) if 'layout' in self.ai_data: with self.context.child("applying_autoinstall"): meth = getattr( self, "guided_" + self.ai_data['layout']['name']) disks = self.model.all_disks() disks.sort(key=lambda x: x.size) meth(disks[-1])
async def _probe(self): with self.context.child("_probe") as context: async with self.app.install_lock_file.shared(): self._crash_reports = {} if isinstance(self.ui.body, ProbingFailed): self.ui.set_body(SlowProbing(self)) schedule_task(self._wait_for_probing()) for (restricted, kind) in [ (False, ErrorReportKind.BLOCK_PROBE_FAIL), (True, ErrorReportKind.DISK_PROBE_FAIL), ]: try: desc = "restricted={}".format(restricted) with context.child("probe_once", desc): await self._probe_once_task.start(restricted) # We wait on the task directly here, not # self._probe_once_task.wait as if _probe_once_task # gets cancelled, we should be cancelled too. await asyncio.wait_for(self._probe_once_task.task, 15.0) except asyncio.CancelledError: # asyncio.CancelledError is a subclass of Exception in # Python 3.6 (sadface) raise except Exception: block_discover_log.exception( "block probing failed restricted=%s", restricted) report = self.app.make_apport_report(kind, "block probing", interrupt=False) self._crash_reports[restricted] = report continue break
def start_ui(self): if self._probe_task.task is None or not self._probe_task.task.done(): self.ui.set_body(SlowProbing(self)) schedule_task(self._wait_for_probing()) elif True in self._crash_reports: self.ui.set_body(ProbingFailed(self)) self.ui.body.show_error() else: # Once we've shown the filesystem UI, we stop listening for udev # events as merging system changes with configuration the user has # performed would be tricky. Possibly worth doing though! Just # not today. self.convert_autoinstall_config() self.stop_listening_udev() self.ui.set_body(GuidedDiskSelectionView(self)) pr = self._crash_reports.get(False) if pr is not None: self.app.show_error_report(pr) if self.answers['guided']: disk = self.model.all_disks()[self.answers['guided-index']] method = self.answers.get('guided-method') self.ui.body.form.guided_choice.value = { 'disk': disk, 'use_lvm': method == "lvm", } self.ui.body.done(self.ui.body.form) elif self.answers['manual']: self.manual()
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))
def select_screen(self, new): if new.interactive(): super().select_screen(new) elif self.autoinstall_config and not new.autoinstall_applied: schedule_task(self._apply(new)) else: raise Skip
def load_info(self): t = self.parent.controller.get_snap_info_task(self.snap) if t.done(): self.loaded() return fi = FetchingInfo(self.parent, self.snap, self.parent.controller.app.aio_loop) self.parent.show_overlay(fi, width=fi.width) schedule_task(self.wait(t, fi))
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))
async def _start(self): with self.context: task = self.tasks[None] = schedule_task(self._load_list()) await task self.pending_snaps = self.model.get_snap_list() log.debug("fetched list of %s snaps", len(self.pending_snaps)) while self.pending_snaps: snap = self.pending_snaps.pop(0) task = self.tasks[snap] = schedule_task( self._fetch_info_for_snap(snap=snap)) await task
def scan_crash_dir(self): filenames = os.listdir(self.crash_directory) to_load = [] for filename in sorted(filenames, reverse=True): base, ext = os.path.splitext(filename) if ext != ".crash": continue path = os.path.join(self.crash_directory, filename) r = ErrorReport.from_file(self, path) self.reports.append(r) to_load.append(r) schedule_task(self._load_reports(to_load))
def start(self): if self.ai_data is not None: self.model.override_config = {'network': self.ai_data} self.apply_config() if self.interactive(): # If interactive, we want edits in the UI to override # the provided config. If not, we just splat the # autoinstall config onto the target system. schedule_task(self.unset_override_config()) elif not self.interactive(): self.initial_config = schedule_task(self.wait_for_initial_config()) super().start()
def subiquity_event(self, event): if event["MESSAGE"] == "starting install": if event["_PID"] == os.getpid(): return if not self.install_lock_file.is_exclusively_locked(): return from subiquity.ui.views.installprogress import ( InstallRunning, ) tty = self.install_lock_file.read_content() install_running = InstallRunning(self.ui.body, self, tty) self.add_global_overlay(install_running) schedule_task(self._hide_install_running(install_running))
def start_ui(self): if self.install_state in [ InstallState.NOT_STARTED, InstallState.RUNNING, ]: self.progress_view.title = _("Installing system") elif self.install_state == InstallState.DONE: self.progress_view.title = _("Install complete!") elif self.install_state == InstallState.ERROR: self.progress_view.title = ( _('An error occurred during installation')) self.ui.set_body(self.progress_view) schedule_task(self.move_on())
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())
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())
def load_reports(self): os.makedirs(self.crash_directory, exist_ok=True) filenames = os.listdir(self.crash_directory) to_load = [] for filename in sorted(filenames, reverse=True): base, ext = os.path.splitext(filename) if ext != ".crash": continue if base not in self._reports_by_base: path = os.path.join(self.crash_directory, filename) r = ErrorReport.from_file(self, path) self.reports.append(r) self._reports_by_base[base] = r to_load.append(r) schedule_task(self._load_reports(to_load))
def __init__(self, opts, block_log_dir): if not opts.bootloader == 'none' and platform.machine() != 's390x': self.controllers.remove("Zdev") super().__init__(opts) self.block_log_dir = block_log_dir if opts.snaps_from_examples: connection = FakeSnapdConnection( os.path.join(os.path.dirname(os.path.dirname(__file__)), "examples", "snaps")) else: connection = SnapdConnection(self.root, self.snapd_socket_path) self.snapd = AsyncSnapd(connection) self.signal.connect_signals([ ('network-proxy-set', lambda: schedule_task(self._proxy_set())), ('network-change', self._network_change), ]) self._apport_data = [] self._apport_files = [] self.autoinstall_config = {} self.merged_autoinstall_path = os.path.join(self.root, 'autoinstall.yaml') self.note_data_for_apport("SnapUpdated", str(self.updated)) self.note_data_for_apport("UsingAnswers", str(bool(self.answers))) self.reboot_on_exit = False
def __init__(self, opts, block_log_dir): if not opts.bootloader == 'none' and platform.machine() != 's390x': self.controllers.remove("Zdev") self.journal_fd, self.journal_watcher = journald_listener( ["subiquity"], self.subiquity_event, seek=True) super().__init__(opts) self.event_listeners = [] self.install_lock_file = Lockfile(self.state_path("installing")) self.global_overlays = [] self.block_log_dir = block_log_dir self.kernel_cmdline = shlex.split(opts.kernel_cmdline) if opts.snaps_from_examples: connection = FakeSnapdConnection( os.path.join(os.path.dirname(os.path.dirname(__file__)), "examples", "snaps"), self.scale_factor) else: connection = SnapdConnection(self.root, self.snapd_socket_path) self.snapd = AsyncSnapd(connection) self.signal.connect_signals([ ('network-proxy-set', lambda: schedule_task(self._proxy_set())), ('network-change', self._network_change), ]) self._apport_data = [] self._apport_files = [] self.autoinstall_config = {} self.report_to_show = None self.show_progress_handle = None self.progress_shown_time = self.aio_loop.time() self.progress_showing = False self.note_data_for_apport("SnapUpdated", str(self.updated)) self.note_data_for_apport("UsingAnswers", str(bool(self.answers))) self.install_confirmed = False
def __init__(self, opts, block_log_dir): super().__init__(opts) self.block_log_dir = block_log_dir self.cloud_init_ok = None self._state = ApplicationState.STARTING_UP self.state_event = asyncio.Event() self.interactive = None self.confirming_tty = '' self.fatal_error = None self.running_error_commands = False self.echo_syslog_id = 'subiquity_echo.{}'.format(os.getpid()) self.event_syslog_id = 'subiquity_event.{}'.format(os.getpid()) self.log_syslog_id = 'subiquity_log.{}'.format(os.getpid()) self.error_reporter = ErrorReporter( self.context.child("ErrorReporter"), self.opts.dry_run, self.root) self.prober = Prober(opts.machine_config, self.debug_flags) self.kernel_cmdline = shlex.split(opts.kernel_cmdline) if opts.snaps_from_examples: connection = FakeSnapdConnection( os.path.join( os.path.dirname(os.path.dirname( os.path.dirname(__file__))), "examples", "snaps"), self.scale_factor) else: connection = SnapdConnection(self.root, self.snapd_socket_path) self.snapd = AsyncSnapd(connection) self.note_data_for_apport("SnapUpdated", str(self.updated)) self.event_listeners = [] self.autoinstall_config = None self.signal.connect_signals([ ('network-proxy-set', lambda: schedule_task(self._proxy_set())), ('network-change', self._network_change), ])
def start(self): if self.model.bootloader == Bootloader.PREP: self.supports_resilient_boot = False else: release = lsb_release()['release'] self.supports_resilient_boot = release >= '20.04' self._start_task = schedule_task(self._start())
def start(self): if not self.active: return self.configure_task = schedule_task(self.configure_snapd()) self.check_task = SingleInstanceTask(self.check_for_update, propagate_errors=False) self.check_task.start_sync()
def get_snap_info_task(self, snap): if snap not in self.tasks: if snap in self.pending_snaps: self.pending_snaps.remove(snap) self.tasks[snap] = schedule_task( self._fetch_info_for_snap(snap=snap)) return self.tasks[snap]
def add_info(self, _bg_attach_hook, wait=False): def _bg_add_info(): _bg_attach_hook() # Add basic info to report. self.pr.add_proc_info() self.pr.add_os_info() self.pr.add_hooks_info(None) apport.hookutils.attach_hardware(self.pr) # Because apport-cli will in general be run on a different # machine, we make some slightly obscure alterations to the report # to make this go better. # apport-cli gets upset if neither of these are present. self.pr['Package'] = 'subiquity ' + os.environ.get( "SNAP_REVISION", "SNAP_REVISION") self.pr['SourcePackage'] = 'subiquity' # If ExecutableTimestamp is present, apport-cli will try to check # that ExecutablePath hasn't changed. But it won't be there. del self.pr['ExecutableTimestamp'] # apport-cli gets upset at the probert C extensions it sees in # here. /proc/maps is very unlikely to be interesting for us # anyway. del self.pr['ProcMaps'] self.pr.write(self._file) async def add_info(): with self._context.child("add_info") as context: try: await run_in_thread(_bg_add_info) except Exception: self.state = ErrorReportState.ERROR_GENERATING log.exception("adding info to problem report failed") else: context.description = "written to " + self.path self.state = ErrorReportState.DONE self._file.close() self._file = None urwid.emit_signal(self, "changed") if wait: with self._context.child("add_info") as context: _bg_add_info() context.description = "written to " + self.path else: schedule_task(add_info())
def run_command_in_foreground(self, cmd, before_hook=None, after_hook=None, **kw): if self.fg_proc is not None: raise Exception("cannot run two fg processes at once") screen = self.urwid_loop.screen async def _run(): self.fg_proc = proc = await astart_command(cmd, stdin=None, stdout=None, stderr=None, **kw) await proc.communicate() self.fg_proc = None # One of the main use cases for this function is to run interactive # bash in a subshell. Interactive bash of course creates a process # group for itself and sets it as the foreground process group for # the controlling terminal. Usually on exit, our process group # becomes the foreground process group again but when the subshell # is killed for some reason it does not. This causes the tcsetattr # that screen.start() does to either cause SIGTTOU to be sent, or # if that is ignored (either because there is no shell around to do # job control things or we are ignoring it) fail with EIO. So we # force our process group back into the foreground with this # call. That's not quite enough though because tcsetpgrp *also* # causes SIGTTOU to be sent to a background process that calls it, # but fortunately if we ignore that (done in start_urwid below), # the call still succeeds. # # I would now like a drink. os.tcsetpgrp(0, os.getpgrp()) screen.start() if after_hook is not None: after_hook() screen.stop() urwid.emit_signal(screen, urwid.display_common.INPUT_DESCRIPTORS_CHANGED) if before_hook is not None: before_hook() schedule_task(_run())
def run_command_in_foreground(self, cmd, before_hook=None, after_hook=None, **kw): screen = self.urwid_loop.screen async def _run(): await arun_command(cmd, stdin=None, stdout=None, stderr=None, **kw) screen.start() if after_hook is not None: after_hook() screen.stop() urwid.emit_signal(screen, urwid.display_common.INPUT_DESCRIPTORS_CHANGED) if before_hook is not None: before_hook() schedule_task(_run())
def select_screen(self, new): if new.interactive(): self._cancel_show_progress() if self.progress_showing: shown_for = self.aio_loop.time() - self.progress_shown_time remaining = 1.0 - shown_for if remaining > 0.0: self.aio_loop.call_later(remaining, self.select_screen, new) return self.progress_showing = False super().select_screen(new) elif self.autoinstall_config and not new.autoinstall_applied: if self.interactive() and self.show_progress_handle is None: self.ui.block_input = True self.show_progress_handle = self.aio_loop.call_later( 0.1, self._show_progress) schedule_task(self._apply(new)) else: new.configured() raise Skip
def make_ui(self): if self._probe_task.task is None or not self._probe_task.task.done(): schedule_task(self._wait_for_probing()) return SlowProbing(self) elif True in self._crash_reports: pr = self._crash_reports[True] if pr is not None: self.app.show_error_report(pr.ref()) return ProbingFailed(self) else: # Once we've shown the filesystem UI, we stop listening for udev # events as merging system changes with configuration the user has # performed would be tricky. Possibly worth doing though! Just # not today. self.convert_autoinstall_config() self.stop_listening_udev() pr = self._crash_reports.get(False) if pr is not None: self.app.show_error_report(pr.ref()) if self.answers: self.app.aio_loop.call_soon(self._start_answers) return GuidedDiskSelectionView(self)
def run_command_in_foreground(self, cmd, before_hook=None, after_hook=None, **kw): screen = self.urwid_loop.screen # Calling screen.stop() sends the INPUT_DESCRIPTORS_CHANGED # signal. This calls _reset_input_descriptors() which calls # unhook_event_loop / hook_event_loop on the screen. But this all # happens before _started is set to False on the screen and so this # does not actually do anything -- we end up attempting to read from # stdin while in a background process group, something that gets the # kernel upset at us. # # The cleanest fix seems to be to just send the signal again once # stop() has returned which, now that screen._started is False, # correctly stops listening from stdin. # # There is an exactly analagous problem with screen.start() except # there the symptom is that we are running in the foreground but not # listening to stdin! The fix is the same. async def _run(): await arun_command(cmd, stdin=None, stdout=None, stderr=None, **kw) screen.start() urwid.emit_signal(screen, urwid.display_common.INPUT_DESCRIPTORS_CHANGED) self.setraw() if after_hook is not None: after_hook() screen.stop() urwid.emit_signal(screen, urwid.display_common.INPUT_DESCRIPTORS_CHANGED) if before_hook is not None: before_hook() schedule_task(_run())
def __init__(self, opts, block_log_dir): if is_linux_tty(): self.input_filter = KeyCodesFilter() else: self.input_filter = DummyKeycodesFilter() self.help_menu = HelpMenu(self) super().__init__(opts) self.event_syslog_id = 'subiquity_event.{}'.format(os.getpid()) self.log_syslog_id = 'subiquity_log.{}'.format(os.getpid()) self.server_updated = None self.restarting_server = False self.prober = Prober(opts.machine_config, self.debug_flags) journald_listen(self.aio_loop, ["subiquity"], self.subiquity_event, seek=True) self.event_listeners = [] self.install_lock_file = Lockfile(self.state_path("installing")) self.global_overlays = [] self.block_log_dir = block_log_dir self.kernel_cmdline = shlex.split(opts.kernel_cmdline) if opts.snaps_from_examples: connection = FakeSnapdConnection( os.path.join(os.path.dirname(os.path.dirname(__file__)), "examples", "snaps"), self.scale_factor) else: connection = SnapdConnection(self.root, self.snapd_socket_path) self.snapd = AsyncSnapd(connection) self.signal.connect_signals([ ('network-proxy-set', lambda: schedule_task(self._proxy_set())), ('network-change', self._network_change), ]) self.conn = aiohttp.UnixConnector(self.opts.socket) self.client = make_client_for_conn(API, self.conn, self.resp_hook) self.autoinstall_config = {} self.error_reporter = ErrorReporter( self.context.child("ErrorReporter"), self.opts.dry_run, self.root, self.client) self.note_data_for_apport("SnapUpdated", str(self.updated)) self.note_data_for_apport("UsingAnswers", str(bool(self.answers)))
def fetch_ssh_keys(self, ssh_import_id, ssh_data): self._fetch_task = schedule_task( self._fetch_ssh_keys(ssh_import_id=ssh_import_id, ssh_data=ssh_data))