diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py index 02a6dc97..a92f134c 100644 --- a/subiquity/controllers/filesystem.py +++ b/subiquity/controllers/filesystem.py @@ -65,8 +65,6 @@ class FilesystemController(ControllerPolicy): log.info("Rendering preserved config for post install") preserved_actions = [preserve_action(a) for a in actions] curtin_write_preserved_actions(actions=preserved_actions) - - self.signal.emit_signal('installprogress:do-initial-install') self.signal.emit_signal('identity:show') # Filesystem/Disk partition ----------------------------------------------- diff --git a/subiquity/controllers/installprogress.py b/subiquity/controllers/installprogress.py index f4dcb0b4..7b36d36a 100644 --- a/subiquity/controllers/installprogress.py +++ b/subiquity/controllers/installprogress.py @@ -17,9 +17,10 @@ import logging from tornado.gen import coroutine import subiquity.utils as utils from subiquity.models import InstallProgressModel -from subiquity.ui.views import ProgressView, ProgressOutput +from subiquity.ui.views import ProgressView from subiquity.controller import ControllerPolicy from subiquity.curtin import (CURTIN_CONFIGS, + curtin_reboot, curtin_install_cmd, curtin_write_postinst_config) @@ -27,65 +28,75 @@ log = logging.getLogger("subiquity.controller.installprogress") class InstallProgressController(ControllerPolicy): + KIRBY = ["(>'-')>", + "<('-'<)", + "<('-')>", + "(>'-')>", + "<('-'<)", + "<('-')>", + "(>'-')>", + "<('-'<)", + "<('-')>", + "(>'-')>", + "<('-'<)"] + def __init__(self, common): super().__init__(common) self.model = InstallProgressModel() - self.progress_output_w = ProgressOutput(self.signal, "Waiting...") - - def install_progress_status(self, data): - self.progress_output_w.set_text(data) - self.signal.emit_signal('refresh') + self.progress_view = None + self.is_complete = False + self.alarm = None + self.kirby_pos = 0 @coroutine - def curtin_dispatch(self, postconfig): - ''' one time curtin dispatch requires the use of - the preserved storage config which allows executing - in-target commands by remounting up the configured - storage. - ''' - write_fd = self.loop.watch_pipe(self.install_progress_status) + def curtin_install(self, postconfig): + self.signal.emit_signal('installprogress:show') + + log.debug('Curtin Install: calling curtin with ' + 'storage/net/postinstall config') - log.debug('writing out postinst config') curtin_write_postinst_config(postconfig) - configs = [CURTIN_CONFIGS['preserved'], CURTIN_CONFIGS['postinstall']] - curtin_cmd = curtin_install_cmd(configs) - log.debug('Curtin postinstall install cmd: {}'.format(curtin_cmd)) - if self.opts.dry_run: - log.debug("Install Progress: Curtin dispatch dry-run") - result = yield utils.run_command_async('cat /var/log/syslog', - write_fd) - else: - try: - result = yield utils.run_command_async(" ".join(curtin_cmd), - write_fd) - except: - log.error("Problem with curtin dispatch run") - raise Exception("Problem with curtin dispatch run") - return result - - @coroutine - def initial_install(self): - log.debug('Initial Install: calling curtin with storage/net config') - write_fd = self.loop.watch_pipe(self.install_progress_status) - - configs = [CURTIN_CONFIGS['network'], CURTIN_CONFIGS['storage']] + configs = [CURTIN_CONFIGS['network'], + CURTIN_CONFIGS['storage'], + CURTIN_CONFIGS['postinstall']] curtin_cmd = curtin_install_cmd(configs) log.debug('Curtin install cmd: {}'.format(curtin_cmd)) if self.opts.dry_run: log.debug("Filesystem: this is a dry-run") - result = yield utils.run_command_async('cat /var/log/syslog', - write_fd) + result = yield utils.run_command_async('cat /var/log/syslog') + self.is_complete = True + else: + log.debug("filesystem: this is the *real* thing") + result = yield utils.run_command_async(" ".join(curtin_cmd)) + if result['status'] > 0: + log.error("Problem with curtin " + "install: {}".format(result)) + raise Exception("Problem with curtin install") + self.is_complete = True + + def progress_indicator(self, *args, **kwargs): + if self.is_complete: + self.progress_view.text.set_text( + "Finished install!") + self.ui.set_footer("", 100) + self.progress_view.show_finished_button() + self.loop.remove_alarm(self.alarm) else: try: - log.debug("filesystem: this is the *real* thing") - result = yield utils.run_command_async(" ".join(curtin_cmd), - write_fd) - except: - log.error("Problem with initial curtin install") - raise Exception("Problem with initial curtin install") - return result + cur_kirby = self.KIRBY[self.kirby_pos] + except IndexError: + self.kirby_pos = 0 + cur_kirby = self.KIRBY[self.kirby_pos] + self.progress_view.text.set_text( + "Still installing, watch kirby dance, {}".format( + cur_kirby)) + self.alarm = self.loop.set_alarm_in(0.3, self.progress_indicator) + self.kirby_pos += 1 + + def reboot(self): + curtin_reboot() @coroutine def show_progress(self): @@ -96,7 +107,8 @@ class InstallProgressController(ControllerPolicy): footer = ("Thank you for using Ubuntu!") self.ui.set_header(title, excerpt) self.ui.set_footer(footer, 90) - self.ui.set_body(ProgressView(self.signal, self.progress_output_w)) + self.progress_view = ProgressView(self.signal) + self.ui.set_body(self.progress_view) if self.opts.dry_run: banner = [ @@ -107,5 +119,8 @@ class InstallProgressController(ControllerPolicy): "", "Press (Q) to Quit." ] - self.install_progress_status("\n".join(banner)) - self.ui.set_footer(footer, 100) + self.progress_view.text.set_text("\n".join(banner)) + else: + self.alarm = self.loop.set_alarm_in(0.3, self.progress_indicator) + + self.ui.set_footer(footer, 90) diff --git a/subiquity/models/installprogress.py b/subiquity/models/installprogress.py index dc6f7ac1..1f1c7bc7 100644 --- a/subiquity/models/installprogress.py +++ b/subiquity/models/installprogress.py @@ -28,15 +28,15 @@ class InstallProgressModel(ModelPolicy): prev_signal = None signals = [ - ("Initial install routine", - 'installprogress:do-initial-install', - 'initial_install'), - ("Run once curtin commands", - 'installprogress:curtin-dispatch', - 'curtin_dispatch'), + ("Run curtin", + 'installprogress:curtin-install', + 'curtin_install'), ("Install progress final view", 'installprogress:show', - 'show_progress') + 'show_progress'), + ("Reboot curtin", + "installprogress:curtin-reboot", + "reboot") ] installprogress_menu = [] diff --git a/subiquity/ui/views/__init__.py b/subiquity/ui/views/__init__.py index 33bb93c4..3acd75e0 100644 --- a/subiquity/ui/views/__init__.py +++ b/subiquity/ui/views/__init__.py @@ -22,6 +22,6 @@ from .ceph import CephDiskView # NOQA from .iscsi import IscsiDiskView # NOQA from .network import NetworkView # NOQA from .installpath import InstallpathView # NOQA -from .installprogress import ProgressOutput, ProgressView # NOQA +from .installprogress import ProgressView # NOQA from .welcome import WelcomeView # NOQA from .identity import IdentityView # NOQA diff --git a/subiquity/ui/views/identity.py b/subiquity/ui/views/identity.py index 2f16abee..e592dee9 100644 --- a/subiquity/ui/views/identity.py +++ b/subiquity/ui/views/identity.py @@ -108,8 +108,7 @@ class IdentityView(ViewPolicy): } log.debug("User input: {}".format(result)) - self.signal.emit_signal('installprogress:curtin-dispatch', result) - self.signal.emit_signal('installprogress:show') + self.signal.emit_signal('installprogress:curtin-install', result) def cancel(self, button): self.signal.emit_signal("quit") diff --git a/subiquity/ui/views/installprogress.py b/subiquity/ui/views/installprogress.py index ce472eb4..eea20394 100644 --- a/subiquity/ui/views/installprogress.py +++ b/subiquity/ui/views/installprogress.py @@ -15,32 +15,40 @@ import logging from urwid import (Text, Filler, - ListBox, BoxAdapter) + Pile) from subiquity.view import ViewPolicy -from subiquity.ui.utils import Color, Padding +from subiquity.ui.buttons import confirm_btn +from subiquity.ui.utils import Padding, Color log = logging.getLogger("subiquity.ui.views.installprogress") -class ProgressOutput(ViewPolicy): - def __init__(self, signal, txt): - self.signal = signal - self.txt = Text(txt) - flr = Filler(Color.info_minor(self.txt), - valign="top") - super().__init__(BoxAdapter(flr, height=20)) - - def set_text(self, data): - self.txt.set_text(data) - - class ProgressView(ViewPolicy): - def __init__(self, signal, output_w): + def __init__(self, signal): """ :param output_w: Filler widget to display updated status text """ self.signal = signal + self.text = Text("Wait for it ...", align="center") self.body = [ - Padding.center_79(output_w) + Padding.center_79(self.text) ] - super().__init__(ListBox(self.body)) + self.pile = Pile(self.body) + super().__init__(Filler(self.pile, valign="middle")) + + def show_finished_button(self): + w = Padding.center_20( + Color.button(confirm_btn(label="Reboot now", + on_press=self.reboot), + focus_map='button focus')) + + self.pile.contents.append((w, self.pile.options())) + self.pile.focus_position = 1 + + def keypress(self, size, key): + super().keypress(size, key) + if key in ['q', 'Q']: + self.signal.emit_signal('installprogress:curtin-reboot') + + def reboot(self, btn): + self.signal.emit_signal('installprogress:curtin-reboot') diff --git a/subiquity/utils.py b/subiquity/utils.py index 1a4cacf7..2bbb4513 100644 --- a/subiquity/utils.py +++ b/subiquity/utils.py @@ -25,15 +25,14 @@ log = logging.getLogger("subiquity.utils") SYS_CLASS_NET = "/sys/class/net/" -def run_command_async(cmd, write_fd, timeout=None): +def run_command_async(cmd, timeout=None): log.debug('calling Async command: {}'.format(cmd)) - return Async.pool.submit(run_command, cmd, write_fd, timeout) + return Async.pool.submit(run_command, cmd, timeout) -def run_command(command, write_fd, timeout=None): +def run_command(command, timeout=None): """ Execute command through system shell :param command: command to run - :param write_fd: writable_fd for output updates :param timeout: (optional) use 'timeout' to limit time. default 300 :type command: str :returns: {status: returncode, output: stdout, err: stderr} @@ -52,7 +51,7 @@ def run_command(command, write_fd, timeout=None): try: log.debug('trying Popen...') p = Popen(command, shell=True, - stdout=write_fd, stderr=PIPE, + stdout=PIPE, stderr=PIPE, bufsize=-1, env=cmd_env, close_fds=True) except OSError as e: if e.errno == errno.ENOENT: