diff --git a/subiquity/controllers/error.py b/subiquity/controllers/error.py index b4e3a8aa..2810c7e1 100644 --- a/subiquity/controllers/error.py +++ b/subiquity/controllers/error.py @@ -55,6 +55,7 @@ class ErrorReportKind(enum.Enum): DISK_PROBE_FAIL = _("Disk probe failure") INSTALL_FAIL = _("Install failure") UI = _("Installer crash") + NETWORK_FAIL = _("Network error") UNKNOWN = _("Unknown error") diff --git a/subiquity/controllers/network.py b/subiquity/controllers/network.py index 4394c402..65816c0a 100644 --- a/subiquity/controllers/network.py +++ b/subiquity/controllers/network.py @@ -14,11 +14,16 @@ # along with this program. If not, see . import asyncio +import logging from subiquitycore.async_helpers import schedule_task from subiquitycore.controllers.network import NetworkController from subiquity.controller import SubiquityController +from subiquity.controllers.error import ErrorReportKind + + +log = logging.getLogger("subiquity.controllers.network") class NetworkController(NetworkController, SubiquityController): @@ -42,6 +47,10 @@ class NetworkController(NetworkController, SubiquityController): }, } + def __init__(self, app): + super().__init__(app) + app.note_file_for_apport("NetplanConfig", self.netplan_path) + def load_autoinstall_data(self, data): self.ai_data = data @@ -74,6 +83,20 @@ class NetworkController(NetworkController, SubiquityController): return r return super().render_config() + async def _apply_config(self, silent): + try: + await super()._apply_config(silent) + except asyncio.CancelledError: + # asyncio.CancelledError is a subclass of Exception in + # Python 3.6 (sadface) + raise + except Exception: + log.exception("_apply_config failed") + self.model.has_network = False + self.app.make_apport_report( + ErrorReportKind.NETWORK_FAIL, "applying network", + interrupt=True) + def done(self): self.configured() super().done() diff --git a/subiquity/core.py b/subiquity/core.py index ddbdbede..b148909f 100644 --- a/subiquity/core.py +++ b/subiquity/core.py @@ -350,14 +350,14 @@ class Subiquity(Application): else: super().unhandled_input(key) - def debug_shell(self): + def debug_shell(self, after_hook=None): def _before(): os.system("clear") print(DEBUG_SHELL_INTRO) self.run_command_in_foreground( - ["bash"], before_hook=_before, cwd='/') + ["bash"], before_hook=_before, after_hook=after_hook, cwd='/') def note_file_for_apport(self, key, path): self._apport_files.append((key, path)) diff --git a/subiquity/ui/views/error.py b/subiquity/ui/views/error.py index 7785567c..670a2cba 100644 --- a/subiquity/ui/views/error.py +++ b/subiquity/ui/views/error.py @@ -69,6 +69,9 @@ Sorry, there was a problem examining the storage devices on this system. """), ErrorReportKind.INSTALL_FAIL: _(""" Sorry, there was a problem completing the installation. +"""), + ErrorReportKind.NETWORK_FAIL: _(""" +Sorry, there was a problem applying the network configuration. """), ErrorReportKind.UI: _(""" Sorry, the installer has restarted because of an error. @@ -106,6 +109,10 @@ devices manually. You may be able to fix the issue by switching to a shell and reconfiguring the system's block devices manually. """), ['debug_shell', 'continue']), + ErrorReportKind.NETWORK_FAIL: (_(""" +You can continue with the installation but it will be assumed the network +is not functional. +"""), ['continue']), ErrorReportKind.INSTALL_FAIL: (_(""" Do you want to try starting the installation again? """), ['restart', 'close']), @@ -241,7 +248,6 @@ class ErrorReportStretchy(Stretchy): def debug_shell(self, sender): self.parent.remove_overlay() - self.app.debug_shell() def restart(self, sender): self.app.restart() diff --git a/subiquitycore/controllers/network.py b/subiquitycore/controllers/network.py index b29d1c13..18f8fc58 100644 --- a/subiquitycore/controllers/network.py +++ b/subiquitycore/controllers/network.py @@ -149,13 +149,16 @@ class NetworkController(BaseController): os.makedirs(netplan_dir) with open(netplan_path, 'w') as fp: fp.write(default_netplan) - self.model.parse_netplan_configs(self.root) + self.parse_netplan_configs() self._watching = False self.network_event_receiver = SubiquityNetworkEventReceiver(self.model) self.network_event_receiver.add_default_route_watcher( self.route_watcher) + def parse_netplan_configs(self): + self.model.parse_netplan_configs(self.root) + def route_watcher(self, routes): if routes: self.signal.emit_signal('network-change') @@ -349,7 +352,7 @@ class NetworkController(BaseController): self.model.stringify_config(config), omode="w") - self.model.parse_netplan_configs(self.root) + self.parse_netplan_configs() async def _apply_config(self, silent): with self.context.child( @@ -383,58 +386,60 @@ class NetworkController(BaseController): if not silent and self.view: self.view.show_apply_spinner() - def error(stage): - if not silent and self.view: - self.view.show_network_error(stage) + try: + def error(stage): + if not silent and self.view: + self.view.show_network_error(stage) - if self.opts.dry_run: - delay = 1/self.app.scale_factor - await arun_command(['sleep', str(delay)]) - if os.path.exists('/lib/netplan/generate'): - # If netplan appears to be installed, run generate to at - # least test that what we wrote is acceptable to netplan. - await arun_command( - ['netplan', 'generate', '--root', self.root], - check=True) - else: - if devs_to_down or devs_to_delete: + if self.opts.dry_run: + delay = 1/self.app.scale_factor + await arun_command(['sleep', str(delay)]) + if os.path.exists('/lib/netplan/generate'): + # If netplan appears to be installed, run generate to + # at least test that what we wrote is acceptable to + # netplan. + await arun_command( + ['netplan', 'generate', '--root', self.root], + check=True) + else: + if devs_to_down or devs_to_delete: + try: + await arun_command( + ['systemctl', 'mask', '--runtime', + 'systemd-networkd.service', + 'systemd-networkd.socket'], + check=True) + await arun_command( + ['systemctl', 'stop', + 'systemd-networkd.service', + 'systemd-networkd.socket'], + check=True) + except subprocess.CalledProcessError: + error("stop-networkd") + raise + if devs_to_down: + await self._down_devs(devs_to_down) + if devs_to_delete: + await self._delete_devs(devs_to_delete) + if devs_to_down or devs_to_delete: + await arun_command( + ['systemctl', 'unmask', '--runtime', + 'systemd-networkd.service', + 'systemd-networkd.socket'], + check=True) try: - await arun_command( - ['systemctl', 'mask', '--runtime', - 'systemd-networkd.service', - 'systemd-networkd.socket'], - check=True) - await arun_command( - ['systemctl', 'stop', - 'systemd-networkd.service', - 'systemd-networkd.socket'], - check=True) + await arun_command(['netplan', 'apply'], check=True) except subprocess.CalledProcessError: - error("stop-networkd") + error("apply") raise - if devs_to_down: - await self._down_devs(devs_to_down) - if devs_to_delete: - await self._delete_devs(devs_to_delete) - if devs_to_down or devs_to_delete: - await arun_command( - ['systemctl', 'unmask', '--runtime', - 'systemd-networkd.service', - 'systemd-networkd.socket'], - check=True) - try: - await arun_command(['netplan', 'apply'], check=True) - except subprocess.CalledProcessError: - error("apply") - raise - if devs_to_down or devs_to_delete: - # It's probably running already, but just in case. - await arun_command( - ['systemctl', 'start', 'systemd-networkd.socket'], - check=False) - - if not silent and self.view: - self.view.hide_apply_spinner() + if devs_to_down or devs_to_delete: + # It's probably running already, but just in case. + await arun_command( + ['systemctl', 'start', 'systemd-networkd.socket'], + check=False) + finally: + if not silent and self.view: + self.view.hide_apply_spinner() if self.answers.get('accept-default', False): self.done()