def start_postinstall_configuration(self): self.copy_logs_to_target() class w(TaskWatcher): def __init__(self, controller): self.controller = controller def task_complete(self, stage): pass def task_error(self, stage, info): if isinstance(info, tuple): tb = traceback.format_exception(*info) self.controller.curtin_error("".join(tb)) else: self.controller.curtin_error() def tasks_finished(self): self.controller.loop.set_alarm_in( 0.0, lambda loop, ud: self.controller.postinstall_complete()) tasks = [ ('drain', WaitForCurtinEventsTask(self)), ('cloud-init', InstallTask( self, "configuring cloud-init", self.base_model.configure_cloud_init)), ] if self.base_model.ssh.install_server: tasks.extend([ ('install-ssh', InstallTask( self, "installing OpenSSH server", self._bg_install_openssh_server)), ]) tasks.extend([ ('cleanup', InstallTask( self, "cleaning up apt configuration", self._bg_cleanup_apt)), ]) ts = TaskSequence(self.run_in_bg, tasks, w(self)) ts.run()
class NetworkController(BaseController, TaskWatcher): signals = [ ('menu:network:main:set-default-v4-route', 'set_default_v4_route'), ('menu:network:main:set-default-v6-route', 'set_default_v6_route'), ] root = "/" def __init__(self, common): super().__init__(common) self.model = self.base_model.network self.answers = self.all_answers.get("Network", {}) if self.opts.dry_run: self.root = os.path.abspath(".subiquity") self.tried_once = False netplan_path = self.netplan_path netplan_dir = os.path.dirname(netplan_path) if os.path.exists(netplan_dir): import shutil shutil.rmtree(netplan_dir) os.makedirs(netplan_dir) with open(netplan_path, 'w') as fp: fp.write(default_netplan) self.model.parse_netplan_configs(self.root) self.network_event_receiver = SubiquityNetworkEventReceiver(self.model) self.observer, fds = ( self.prober.probe_network(self.network_event_receiver)) for fd in fds: self.loop.watch_file(fd, partial(self._data_ready, fd)) def _data_ready(self, fd): cp = run_command(['udevadm', 'settle', '-t', '0']) if cp.returncode != 0: log.debug("waiting 0.1 to let udev event queue settle") self.loop.set_alarm_in(0.1, lambda loop, ud: self._data_ready(fd)) return self.observer.data_ready(fd) v = self.ui.frame.body if hasattr(v, 'refresh_model_inputs'): v.refresh_model_inputs() def start_scan(self, dev): self.observer.trigger_scan(dev.ifindex) def cancel(self): self.signal.emit_signal('prev-screen') def default(self): self.ui.set_body(NetworkView(self.model, self)) if self.answers.get('accept-default', False): self.network_finish(self.model.render()) @property def netplan_path(self): if self.opts.project == "subiquity": netplan_config_file_name = '00-installer-config.yaml' else: netplan_config_file_name = '00-snapd-config.yaml' return os.path.join(self.root, 'etc/netplan', netplan_config_file_name) def network_finish(self, config): log.debug("network config: \n%s", yaml.dump(sanitize_config(config), default_flow_style=False)) for p in netplan.configs_in_root(self.root, masked=True): if p == self.netplan_path: continue os.rename(p, p + ".dist-" + self.opts.project) write_file(self.netplan_path, '\n'.join(( ("# This is the network config written by '%s'" % self.opts.project), yaml.dump(config))), omode="w") self.model.parse_netplan_configs(self.root) if self.opts.dry_run: tasks = [ ('one', BackgroundProcess(['sleep', '0.1'])), ('two', PythonSleep(0.1)), ('three', BackgroundProcess(['sleep', '0.1'])), ] 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. tasks.append(('generate', BackgroundProcess(['netplan', 'generate', '--root', self.root]))) if not self.tried_once: tasks.append( ('timeout', WaitForDefaultRouteTask(3, self.network_event_receiver)) ) tasks.append(('fail', BackgroundProcess(['false']))) self.tried_once = True else: devs_to_down = [] for dev in self.model.get_all_netdevs(): devcfg = self.model.config.config_for_device(dev._net_info) if dev._configuration != devcfg: devs_to_down.append(dev) tasks = [] if devs_to_down: tasks.extend([ ('stop-networkd', BackgroundProcess(['systemctl', 'stop', 'systemd-networkd.service'])), ('down', DownNetworkDevices(self.observer.rtlistener, devs_to_down)), ]) tasks.extend([ ('apply', BackgroundProcess(['netplan', 'apply'])), ('timeout', WaitForDefaultRouteTask(30, self.network_event_receiver)), ]) def cancel(): self.cs.cancel() self.task_error('canceled') self.acw = ApplyingConfigWidget(len(tasks), cancel) self.ui.frame.body.show_overlay(self.acw, min_width=60) self.cs = TaskSequence(self.run_in_bg, tasks, self) self.cs.run() def task_complete(self, stage): self.acw.advance() def task_error(self, stage, info=None): self.ui.frame.body.remove_overlay() self.ui.frame.body.show_network_error(stage, info) if self.answers.get('accept-default', False): self.network_finish(self.model.render()) def tasks_finished(self): self.signal.emit_signal('network-config-written', self.netplan_path) self.signal.emit_signal('next-screen') def set_default_v4_route(self): self.ui.set_header("Default route") self.ui.set_body( NetworkSetDefaultRouteView(self.model, socket.AF_INET, self)) def set_default_v6_route(self): self.ui.set_header("Default route") self.ui.set_body( NetworkSetDefaultRouteView(self.model, socket.AF_INET6, self)) def bond_interfaces(self): self.ui.set_body(NetworkBondInterfacesView(self.model, self)) def network_configure_interface(self, iface): self.ui.set_header(_("Network interface {}").format(iface)) self.ui.set_footer("") self.ui.set_body( NetworkConfigureInterfaceView(self.model, self, iface)) def network_configure_ipv4_interface(self, iface): self.ui.set_header(_( "Network interface {} manual IPv4 configuration").format(iface)) self.ui.set_footer("") self.ui.set_body( NetworkConfigureIPv4InterfaceView(self.model, self, iface)) def network_configure_wlan_interface(self, iface): self.ui.set_header(_( "Network interface {} WIFI configuration").format(iface)) self.ui.set_footer("") self.ui.set_body(NetworkConfigureWLANView(self.model, self, iface)) def network_configure_ipv6_interface(self, iface): self.ui.set_header(_( "Network interface {} manual IPv6 configuration").format(iface)) self.ui.set_footer("") self.ui.set_body( NetworkConfigureIPv6InterfaceView(self.model, self, iface))
class NetworkController(BaseController, TaskWatcher): root = "/" def __init__(self, common): super().__init__(common) self.model = self.base_model.network self.answers = self.all_answers.get("Network", {}) if self.opts.dry_run: self.root = os.path.abspath(".subiquity") self.tried_once = False netplan_path = self.netplan_path netplan_dir = os.path.dirname(netplan_path) if os.path.exists(netplan_dir): import shutil shutil.rmtree(netplan_dir) os.makedirs(netplan_dir) with open(netplan_path, 'w') as fp: fp.write(default_netplan) self.model.parse_netplan_configs(self.root) self.network_event_receiver = SubiquityNetworkEventReceiver(self.model) self._observer_handles = [] self.observer, self._observer_fds = ( self.prober.probe_network(self.network_event_receiver)) self.start_watching() self._done_by_action = False def stop_watching(self): for handle in self._observer_handles: self.loop.remove_watch_file(handle) self._observer_handles = [] def start_watching(self): if self._observer_handles: return self._observer_handles = [ self.loop.watch_file(fd, partial(self._data_ready, fd)) for fd in self._observer_fds] def _data_ready(self, fd): cp = run_command(['udevadm', 'settle', '-t', '0']) if cp.returncode != 0: log.debug("waiting 0.1 to let udev event queue settle") self.stop_watching() self.loop.set_alarm_in(0.1, lambda loop, ud: self.start_watching()) return self.observer.data_ready(fd) v = self.ui.frame.body if hasattr(v, 'refresh_model_inputs'): v.refresh_model_inputs() def start_scan(self, dev): self.observer.trigger_scan(dev.ifindex) def cancel(self): self.signal.emit_signal('prev-screen') def _action_get(self, id): dev_spec = id[0].split() dev = None if dev_spec[0] == "interface": if dev_spec[1] == "index": dev = self.model.get_all_netdevs()[int(dev_spec[2])] elif dev_spec[1] == "name": dev = self.model.get_netdev_by_name(dev_spec[2]) if dev is None: raise Exception("could not resolve {}".format(id)) if len(id) > 1: part, index = id[1].split() if part == "part": return dev.partitions()[int(index)] else: return dev raise Exception("could not resolve {}".format(id)) def _action_clean_devices(self, devices): return [self._action_get(device) for device in devices] def _answers_action(self, action): from subiquitycore.ui.stretchy import StretchyOverlay log.debug("_answers_action %r", action) if 'obj' in action: obj = self._action_get(action['obj']) meth = getattr( self.ui.frame.body, "_action_{}".format(action['action'])) meth(obj) yield body = self.ui.frame.body._w if not isinstance(body, StretchyOverlay): return for k, v in action.items(): if not k.endswith('data'): continue form_name = "form" submit_key = "submit" if '-' in k: prefix = k.split('-')[0] form_name = prefix + "_form" submit_key = prefix + "-submit" yield from self._enter_form_data( getattr(body.stretchy, form_name), v, action.get(submit_key, True)) elif action['action'] == 'create-bond': self.ui.frame.body._create_bond() yield body = self.ui.frame.body._w yield from self._enter_form_data( body.stretchy.form, action['data'], action.get("submit", True)) elif action['action'] == 'done': self._done_by_action = True self.ui.frame.body.done() else: raise Exception("could not process action {}".format(action)) def default(self): view = NetworkView(self.model, self) self.network_event_receiver.view = view self.ui.set_body(view) if self.answers.get('accept-default', False): self.network_finish(self.model.render()) elif self.answers.get('actions', False): self._run_iterator(self._run_actions(self.answers['actions'])) @property def netplan_path(self): if self.opts.project == "subiquity": netplan_config_file_name = '00-installer-config.yaml' else: netplan_config_file_name = '00-snapd-config.yaml' return os.path.join(self.root, 'etc/netplan', netplan_config_file_name) def add_vlan(self, device, vlan): return self.model.new_vlan(device, vlan) def add_or_update_bond(self, existing, result): mode = result['mode'] params = { 'mode': mode, } if mode in BondParameters.supports_xmit_hash_policy: params['transmit-hash-policy'] = result['xmit_hash_policy'] if mode in BondParameters.supports_lacp_rate: params['lacp-rate'] = result['lacp_rate'] for device in result['devices']: device.config = {} interfaces = [d.name for d in result['devices']] if existing is None: return self.model.new_bond(result['name'], interfaces, params) else: existing.config['interfaces'] = interfaces existing.config['parameters'] = params existing.name = result['name'] return existing def network_finish(self, config): log.debug("network config: \n%s", yaml.dump(sanitize_config(config), default_flow_style=False)) for p in netplan.configs_in_root(self.root, masked=True): if p == self.netplan_path: continue os.rename(p, p + ".dist-" + self.opts.project) write_file(self.netplan_path, '\n'.join(( ("# This is the network config written by '%s'" % self.opts.project), yaml.dump(config, default_flow_style=False))), omode="w") self.model.parse_netplan_configs(self.root) if self.opts.dry_run: delay = 0.1/self.scale_factor tasks = [ ('one', BackgroundProcess(['sleep', str(delay)])), ('two', PythonSleep(delay)), ('three', BackgroundProcess(['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. tasks.append(('generate', BackgroundProcess(['netplan', 'generate', '--root', self.root]))) if not self.tried_once: tasks.append( ('timeout', WaitForDefaultRouteTask(3, self.network_event_receiver)) ) tasks.append(('fail', BackgroundProcess(['false']))) self.tried_once = True else: devs_to_delete = [] devs_to_down = [] for dev in self.model.get_all_netdevs(include_deleted=True): if dev.info is None: continue devcfg = self.model.config.config_for_device(dev.info) if dev.is_virtual: devs_to_delete.append(dev) elif dev.config != devcfg: devs_to_down.append(dev) tasks = [] if devs_to_down or devs_to_delete: tasks.extend([ ('stop-networkd', BackgroundProcess(['systemctl', 'stop', 'systemd-networkd.service'])), ('down', DownNetworkDevices(self.observer.rtlistener, devs_to_down, devs_to_delete)), ]) tasks.extend([ ('apply', BackgroundProcess(['netplan', 'apply'])), ('timeout', WaitForDefaultRouteTask(30, self.network_event_receiver)), ]) def cancel(): self.cs.cancel() self.task_error('canceled') self.acw = ApplyingConfigWidget(len(tasks), cancel) self.ui.frame.body.show_overlay(self.acw, min_width=60) self.cs = TaskSequence(self.run_in_bg, tasks, self) self.cs.run() def task_complete(self, stage): self.acw.advance() def task_error(self, stage, info=None): self.ui.frame.body.remove_overlay() self.ui.frame.body.show_network_error(stage, info) if self.answers.get('accept-default', False) or self._done_by_action: self.network_finish(self.model.render()) def tasks_finished(self): self.signal.emit_signal('network-config-written', self.netplan_path) self.loop.set_alarm_in( 0.0, lambda loop, ud: self.signal.emit_signal('next-screen'))
def apply_config(self, silent=False): log.debug("apply_config silent=%s", silent) if self.dhcp_check_handle is not None: self.loop.remove_alarm(self.dhcp_check_handle) self.dhcp_check_handle = None config = self.model.render() devs_to_delete = [] devs_to_down = [] dhcp_device_versions = [] for dev in self.model.get_all_netdevs(include_deleted=True): for v in 4, 6: if dev.dhcp_enabled(v): if not silent: dev.set_dhcp_state(v, "PENDING") self.network_event_receiver.update_link(dev.ifindex) else: dev.set_dhcp_state(v, "RECONFIGURE") dhcp_device_versions.append((dev, v)) if dev.info is None: continue if dev.is_virtual: devs_to_delete.append(dev) continue if dev.config != self.model.config.config_for_device(dev.info): devs_to_down.append(dev) log.debug("network config: \n%s", yaml.dump(sanitize_config(config), default_flow_style=False)) for p in netplan.configs_in_root(self.root, masked=True): if p == self.netplan_path: continue os.rename(p, p + ".dist-" + self.opts.project) write_file(self.netplan_path, '\n'.join((("# This is the network config written by '%s'" % self.opts.project), yaml.dump(config, default_flow_style=False))), omode="w") self.model.parse_netplan_configs(self.root) if self.opts.dry_run: delay = 0.1 / self.app.scale_factor tasks = [ ('one', BackgroundProcess(['sleep', str(delay)])), ('two', PythonSleep(delay)), ('three', BackgroundProcess(['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. tasks.append(('generate', BackgroundProcess( ['netplan', 'generate', '--root', self.root]))) else: tasks = [] if devs_to_down or devs_to_delete: tasks.extend([ ('stop-networkd', BackgroundProcess( ['systemctl', 'stop', 'systemd-networkd.service'])), ('down', DownNetworkDevices(self.observer.rtlistener, devs_to_down, devs_to_delete)), ]) tasks.extend([ ('apply', BackgroundProcess(['netplan', 'apply'])), ]) if not silent: self.view.show_apply_spinner() ts = TaskSequence(self.run_in_bg, tasks, ApplyWatcher(self.view, self)) ts.run() if dhcp_device_versions: self.dhcp_check_handle = self.loop.set_alarm_in( 10, lambda loop, ud: self.check_dchp_results(ud), dhcp_device_versions)
class NetworkController(BaseController, TaskWatcher): signals = [ ('menu:network:main:set-default-v4-route', 'set_default_v4_route'), ('menu:network:main:set-default-v6-route', 'set_default_v6_route'), ] root = "/" def __init__(self, common): super().__init__(common) self.model = self.base_model.network self.answers = self.all_answers.get("Network", {}) if self.opts.dry_run: self.root = os.path.abspath(".subiquity") self.tried_once = False netplan_path = self.netplan_path netplan_dir = os.path.dirname(netplan_path) if os.path.exists(netplan_dir): import shutil shutil.rmtree(netplan_dir) os.makedirs(netplan_dir) with open(netplan_path, 'w') as fp: fp.write(default_netplan) self.model.parse_netplan_configs(self.root) self.network_event_receiver = SubiquityNetworkEventReceiver(self.model) self._observer_handles = [] self.observer, self._observer_fds = (self.prober.probe_network( self.network_event_receiver)) self.start_watching() def stop_watching(self): for handle in self._observer_handles: self.loop.remove_watch_file(handle) self._observer_handles = [] def start_watching(self): if self._observer_handles: return self._observer_handles = [ self.loop.watch_file(fd, partial(self._data_ready, fd)) for fd in self._observer_fds ] def _data_ready(self, fd): cp = run_command(['udevadm', 'settle', '-t', '0']) if cp.returncode != 0: log.debug("waiting 0.1 to let udev event queue settle") self.stop_watching() self.loop.set_alarm_in(0.1, lambda loop, ud: self.start_watching()) return self.observer.data_ready(fd) v = self.ui.frame.body if hasattr(v, 'refresh_model_inputs'): v.refresh_model_inputs() def start_scan(self, dev): self.observer.trigger_scan(dev.ifindex) def cancel(self): self.signal.emit_signal('prev-screen') def default(self): view = NetworkView(self.model, self) self.network_event_receiver.view = view self.ui.set_body(view) if self.answers.get('accept-default', False): self.network_finish(self.model.render()) @property def netplan_path(self): if self.opts.project == "subiquity": netplan_config_file_name = '00-installer-config.yaml' else: netplan_config_file_name = '00-snapd-config.yaml' return os.path.join(self.root, 'etc/netplan', netplan_config_file_name) def add_vlan(self, device, vlan): cmd = [ 'ip', 'link', 'add', 'name', '%s.%s' % (device.name, vlan), 'link', device.name, 'type', 'vlan', 'id', str(vlan) ] try: run_command(cmd, check=True) except subprocess.CalledProcessError: self.ui.frame.body.show_network_error('add-vlan') def add_bond(self, params): cmd = [ 'ip', 'link', 'add', 'name', '%(name)s' % params, 'type', 'bond', 'mode', '%(mode)s' % params ] if params['mode'] in ['balance-xor', '802.3ad', 'balance-tlb']: cmd += ['xmit_hash_policy', '%(xmit_hash_policy)s' % params] if params['mode'] == '802.3ad': cmd += ['lacp_rate', '%(lacp_rate)s' % params] try: run_command(cmd, check=True) except subprocess.CalledProcessError: self.ui.frame.body.show_network_error('add-bond') def rm_virtual_interface(self, device): cmd = ['ip', 'link', 'delete', 'dev', device.name] try: run_command(cmd, check=True) except subprocess.CalledProcessError: self.ui.frame.body.show_network_error('rm-dev') def add_master(self, device, master_dev=None, master_name=None): # Drop ip configs for ip in [4, 6]: device.remove_ip_networks_for_version(ip) device.set_dhcp_for_version(ip, False) down_cmd = ['ip', 'link', 'set', 'dev', device.name, 'down'] cmd = ['ip', 'link', 'set', 'dev', device.name] if master_dev: master_name = master_dev.name if master_name: cmd += ['master', master_name] else: cmd += ['nomaster'] try: # Down the interface, and set new master run_command(down_cmd, check=True) run_command(cmd, check=True) except subprocess.CalledProcessError: self.ui.frame.body.show_network_error('add-master') def network_finish(self, config): log.debug("network config: \n%s", yaml.dump(sanitize_config(config), default_flow_style=False)) for p in netplan.configs_in_root(self.root, masked=True): if p == self.netplan_path: continue os.rename(p, p + ".dist-" + self.opts.project) write_file(self.netplan_path, '\n'.join((("# This is the network config written by '%s'" % self.opts.project), yaml.dump(config, default_flow_style=False))), omode="w") self.model.parse_netplan_configs(self.root) if self.opts.dry_run: tasks = [ ('one', BackgroundProcess(['sleep', '0.1'])), ('two', PythonSleep(0.1)), ('three', BackgroundProcess(['sleep', '0.1'])), ] 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. tasks.append(('generate', BackgroundProcess( ['netplan', 'generate', '--root', self.root]))) if not self.tried_once: tasks.append( ('timeout', WaitForDefaultRouteTask(3, self.network_event_receiver))) tasks.append(('fail', BackgroundProcess(['false']))) self.tried_once = True else: devs_to_down = [] for dev in self.model.get_all_netdevs(): devcfg = self.model.config.config_for_device(dev._net_info) if dev._configuration != devcfg: devs_to_down.append(dev) tasks = [] if devs_to_down: tasks.extend([ ('stop-networkd', BackgroundProcess( ['systemctl', 'stop', 'systemd-networkd.service'])), ('down', DownNetworkDevices(self.observer.rtlistener, devs_to_down)), ]) tasks.extend([ ('apply', BackgroundProcess(['netplan', 'apply'])), ('timeout', WaitForDefaultRouteTask(30, self.network_event_receiver)), ]) def cancel(): self.cs.cancel() self.task_error('canceled') self.acw = ApplyingConfigWidget(len(tasks), cancel) self.ui.frame.body.show_overlay(self.acw, min_width=60) self.cs = TaskSequence(self.run_in_bg, tasks, self) self.cs.run() def task_complete(self, stage): self.acw.advance() def task_error(self, stage, info=None): self.ui.frame.body.remove_overlay() self.ui.frame.body.show_network_error(stage, info) if self.answers.get('accept-default', False): self.network_finish(self.model.render()) def tasks_finished(self): self.signal.emit_signal('network-config-written', self.netplan_path) self.signal.emit_signal('next-screen') def set_default_v4_route(self): self.ui.set_header("Default route") self.ui.set_body( NetworkSetDefaultRouteView(self.model, socket.AF_INET, self)) def set_default_v6_route(self): self.ui.set_header("Default route") self.ui.set_body( NetworkSetDefaultRouteView(self.model, socket.AF_INET6, self))