diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py index e173e569..7bed6ee8 100644 --- a/subiquity/controllers/filesystem.py +++ b/subiquity/controllers/filesystem.py @@ -86,6 +86,13 @@ class FilesystemController(ControllerPolicy): 'curtin_write_preserved_actions') return None + # mark that we've writting out curtin config + self.signal.emit_signal('installprogress:wrote-install') + + # start curtin install in background + self.signal.emit_signal('installprogress:curtin-install') + + # switch to identity view self.signal.emit_signal('menu:identity:main') # Filesystem/Disk partition ----------------------------------------------- diff --git a/subiquity/controllers/installprogress.py b/subiquity/controllers/installprogress.py index a82390d9..5e116945 100644 --- a/subiquity/controllers/installprogress.py +++ b/subiquity/controllers/installprogress.py @@ -14,94 +14,133 @@ # along with this program. If not, see . import logging +import os +import subprocess from tornado.gen import coroutine import subiquity.utils as utils from subiquity.models import InstallProgressModel from subiquity.ui.views import ProgressView from subiquity.controller import ControllerPolicy from subiquity.curtin import (CURTIN_CONFIGS, + CURTIN_INSTALL_LOG, + CURTIN_POSTINSTALL_LOG, curtin_reboot, - curtin_install_cmd, - curtin_write_postinst_config) + curtin_install_cmd) + log = logging.getLogger("subiquity.controller.installprogress") class InstallProgressController(ControllerPolicy): - KITT = [ - "-......", - ".-.....", - "..-....", - "...-...", - "....-..", - ".....-.", - "......-", - ".....-.", - "....-..", - "...-...", - "..-....", - ".-.....", - "-......"] - def __init__(self, common): super().__init__(common) self.model = InstallProgressModel() self.progress_view = None - self.is_complete = False self.alarm = None - self.kitt_pos = 0 - self.kitt_len = len(self.KITT) + self.install_log = None + + # state flags + self.install_config = False + self.install_spawned = False + self.install_complete = False + self.postinstall_config = False + self.postinstall_spawned = False + self.postinstall_complete = False + + def curtin_wrote_install(self): + self.install_config = True + + def curtin_wrote_postinstall(self): + self.postinstall_config = True + + @property + def is_complete(self): + log.debug('Checking is_complete: {} and {}'.format( + self.install_complete, + self.postinstall_complete)) + return (self.install_complete and self.postinstall_complete) @coroutine - def curtin_install(self, postconfig): - self.signal.emit_signal('menu:installprogress:main') - + def curtin_install(self): log.debug('Curtin Install: calling curtin with ' 'storage/net/postinstall config') - try: - curtin_write_postinst_config(postconfig) - except PermissionError: - log.exception('Failed to write curtin post-install config') - self.signal.emit_signal('filesystem:error', - 'curtin_write_postinst_config') - return None + if self.install_config is False: + log.error('Attempting to spawn curtin install without a config') + raise Exception('AIEEE!') - configs = [CURTIN_CONFIGS['network'], - CURTIN_CONFIGS['storage'], - CURTIN_CONFIGS['postinstall']] - curtin_cmd = curtin_install_cmd(configs) + self.install_spawned = True + self.install_log = CURTIN_INSTALL_LOG + if self.opts.dry_run: + log.debug("Installprogress: this is a dry-run") + curtin_cmd = ["top", "-d", "0.5", "-n", "20", "-b", "-p", + str(os.getpid()), ">", self.install_log] + else: + log.debug("Installprogress: this is the *REAL* thing") + configs = [CURTIN_CONFIGS['network'], + CURTIN_CONFIGS['storage']] + curtin_cmd = curtin_install_cmd(configs) log.debug('Curtin install cmd: {}'.format(curtin_cmd)) + result = yield utils.run_command_async(" ".join(curtin_cmd)) + if result['status'] > 0: + msg = ("Problem with curtin " + "install: {}".format(result)) + log.error(msg) + self.progress_view.text.set_text(msg) + self.loop.remove_alarm(self.alarm) + log.debug('After curtin install OK') + self.install_complete = True + + @coroutine + def curtin_postinstall(self): + log.debug('Curtin Post Install: calling curtin ' + 'with postinstall config') + + if self.postinstall_config is False: + log.error('Attempting to spawn curtin install without a config') + raise Exception('AIEEE!') + + self.postinstall_spawned = True + self.install_log = CURTIN_POSTINSTALL_LOG if self.opts.dry_run: - log.debug("Filesystem: this is a dry-run") - result = yield utils.run_command_async('cat /var/log/syslog') - self.is_complete = True + log.debug("Installprogress: this is a dry-run") + curtin_cmd = ["top", "-d", "0.5", "-n", "20", "-b", "-p", + str(os.getpid()), ">", self.install_log] else: - log.debug("filesystem: this is the *real* thing") - result = yield utils.run_command_async(" ".join(curtin_cmd)) - if result['status'] > 0: - msg = ("Problem with curtin " - "install: {}".format(result)) - log.error(msg) - self.progress_view.text.set_text(msg) - self.loop.remove_alarm(self.alarm) - self.is_complete = True + log.debug("Installprogress: this is the *REAL* thing") + configs = [CURTIN_CONFIGS['postinstall']] + curtin_cmd = curtin_install_cmd(configs) + + log.debug('Curtin postinstall cmd: {}'.format(curtin_cmd)) + result = yield utils.run_command_async(" ".join(curtin_cmd)) + if result['status'] > 0: + msg = ("Problem with curtin " + "install: {}".format(result)) + log.error(msg) + self.progress_view.text.set_text(msg) + self.loop.remove_alarm(self.alarm) + self.postinstall_complete = True def progress_indicator(self, *args, **kwargs): if self.is_complete: - self.progress_view.text.set_text( - "Finished install!") + 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) + return + elif (self.postinstall_config and + self.install_complete and + not self.postinstall_spawned): + # kick off postinstall + self.signal.emit_signal('installprogress:curtin-postinstall') else: - cur_kitt = self.KITT[self.kitt_pos % self.kitt_len] - self.progress_view.text.set_text( - "Installing <{}>".format( - cur_kitt)) - self.alarm = self.loop.set_alarm_in(0.3, self.progress_indicator) - self.kitt_pos += 1 + tail_cmd = ['tail', '-n', '10', self.install_log] + log.debug('tail cmd: {}'.format(" ".join(tail_cmd))) + install_tail = subprocess.check_output(tail_cmd) + self.progress_view.text.set_text(install_tail) + + self.alarm = self.loop.set_alarm_in(0.3, self.progress_indicator) def reboot(self): if self.opts.dry_run: diff --git a/subiquity/curtin.py b/subiquity/curtin.py index 3be25430..f44711ad 100644 --- a/subiquity/curtin.py +++ b/subiquity/curtin.py @@ -25,6 +25,8 @@ log = logging.getLogger("subiquity.curtin") TMPDIR = '/tmp' CURTIN_SEARCH_PATH = ['/usr/local/curtin/bin', '/usr/bin'] CURTIN_INSTALL_PATH = ['/media/root-ro', '/'] +CURTIN_INSTALL_LOG = '/tmp/subiquity-curtin-install.log' +CURTIN_POSTINSTALL_LOG = '/tmp/subiquity-curtin-postinstall.log' CONF_PREFIX = os.path.join(TMPDIR, 'subiquity-config-') CURTIN_NETWORK_CONFIG_FILE = CONF_PREFIX + 'network.yaml' CURTIN_STORAGE_CONFIG_FILE = CONF_PREFIX + 'storage.yaml' @@ -37,15 +39,20 @@ CURTIN_CONFIGS = { 'preserved': CURTIN_PRESERVED_CONFIG_FILE, } CURTIN_CONFIG_HEADER = """ -reporter: +reporting: subiquity: - path: /tmp/curtin_progress_subiquity - progress: True + type: print partitioning_commands: builtin: curtin block-meta custom """ + +CURTIN_LOG_HEADER = """ +install: + log_file: {} +""" + CURTIN_CONFIG_REBOOT = """ power_state: message: s-Ubiquity install complete. Rebooting @@ -103,6 +110,10 @@ def curtin_write_postinst_config(userinfo): conf.close() +def curtin_log_header(logfile=CURTIN_INSTALL_LOG): + return CURTIN_LOG_HEADER.format(logfile) + + def curtin_write_storage_actions(actions): curtin_config = yaml.dump(actions, default_flow_style=False) curtin_config = " " + "\n ".join(curtin_config.splitlines()) @@ -110,7 +121,9 @@ def curtin_write_storage_actions(actions): str(datetime.datetime.utcnow())) with open(CURTIN_STORAGE_CONFIG_FILE, 'w') as conf: conf.write(datestr) - conf.write(CURTIN_CONFIG_HEADER + CURTIN_STORAGE_CONFIG_HEADER) + conf.write(CURTIN_CONFIG_HEADER) + conf.write(curtin_log_header(logfile=CURTIN_INSTALL_LOG)) + conf.write(CURTIN_STORAGE_CONFIG_HEADER) conf.write(curtin_config) conf.close() @@ -136,7 +149,9 @@ def curtin_write_preserved_actions(actions): str(datetime.datetime.utcnow())) with open(CURTIN_PRESERVED_CONFIG_FILE, 'w') as conf: conf.write(datestr) - conf.write(CURTIN_CONFIG_HEADER + CURTIN_STORAGE_CONFIG_HEADER) + conf.write(CURTIN_CONFIG_HEADER) + conf.write(curtin_log_header(logfile=CURTIN_POSTINSTALL_LOG)) + conf.write(CURTIN_STORAGE_CONFIG_HEADER) conf.write(curtin_config) conf.close() @@ -161,12 +176,12 @@ def curtin_find_install_path(): def curtin_install_cmd(configs): ''' - curtin -v --showtrace install -c $CONFIGS cp:/// + curtin -vvv --showtrace install -c $CONFIGS cp:/// ''' curtin = curtin_find_curtin() install_path = curtin_find_install_path() - install_cmd = [curtin, '-v', '--showtrace'] + install_cmd = [curtin, '-vvv', '--showtrace'] if configs: install_cmd += ['-c {}'.format(c) for c in configs] install_cmd += ['install', 'cp://{}'.format(install_path)] diff --git a/subiquity/models/installprogress.py b/subiquity/models/installprogress.py index 0e168762..1cb566ee 100644 --- a/subiquity/models/installprogress.py +++ b/subiquity/models/installprogress.py @@ -30,6 +30,15 @@ class InstallProgressModel(ModelPolicy): ("Run curtin", 'installprogress:curtin-install', 'curtin_install'), + ("Run curtin postinstall", + 'installprogress:curtin-postinstall', + 'curtin_postinstall'), + ("Indicate curtin config written", + 'installprogress:wrote-install', + 'curtin_wrote_install'), + ("Indicate curtin postconfig written", + 'installprogress:wrote-postinstall', + 'curtin_wrote_postinstall'), ("Install progress final view", 'menu:installprogress:main', 'show_progress'), diff --git a/subiquity/ui/views/identity.py b/subiquity/ui/views/identity.py index 6c03fd37..0bfd0f21 100644 --- a/subiquity/ui/views/identity.py +++ b/subiquity/ui/views/identity.py @@ -26,6 +26,8 @@ from subiquity.ui.interactive import (PasswordEditor, UsernameEditor) from subiquity.ui.utils import Padding, Color from subiquity.view import ViewPolicy +from subiquity.curtin import curtin_write_postinst_config + log = logging.getLogger("subiquity.views.identity") @@ -148,9 +150,18 @@ class IdentityView(ViewPolicy): "password": cpassword, "confirm_password": cpassword, } - log.debug("User input: {}".format(result)) - self.signal.emit_signal('installprogress:curtin-install', result) + try: + curtin_write_postinst_config(result) + except PermissionError: + log.exception('Failed to write curtin post-install config') + self.signal.emit_signal('filesystem:error', + 'curtin_write_postinst_config') + return None + + self.signal.emit_signal('installprogress:wrote-postinstall') + # show progress view + self.signal.emit_signal('menu:installprogress:main') def cancel(self, button): self.signal.prev_signal()