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:
Ryan Harper 2015-11-16 14:59:59 -06:00
parent 9c28c27201
commit e3d8f9003a
5 changed files with 144 additions and 63 deletions

View File

@ -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 -----------------------------------------------

View File

@ -14,71 +14,74 @@
# 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!')
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_CONFIGS['postinstall']]
CURTIN_CONFIGS['storage']]
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')
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:
msg = ("Problem with curtin "
@ -86,22 +89,58 @@ class InstallProgressController(ControllerPolicy):
log.error(msg)
self.progress_view.text.set_text(msg)
self.loop.remove_alarm(self.alarm)
self.is_complete = True
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("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['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))
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)
self.kitt_pos += 1
def reboot(self):
if self.opts.dry_run:

View File

@ -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)]

View File

@ -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'),

View File

@ -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()