Rework async curtin install commands
Return to spawning curtin in two stages, and include additional signals and flags to control when we spawn the postinstall config operation. Signed-off-by: Ryan Harper <ryan.harper@canonical.com>
This commit is contained in:
parent
9c28c27201
commit
e3d8f9003a
|
@ -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 -----------------------------------------------
|
||||
|
|
|
@ -14,94 +14,133 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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:
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue