Example #1
0
    def network_finish(self, config):
        log.debug("network config: \n%s",
                  yaml.dump(sanitize_config(config), default_flow_style=False))

        netplan_path = self.netplan_path
        while True:
            try:
                tmppath = '%s.%s' % (netplan_path, random.randrange(0, 1000))
                fd = os.open(tmppath, os.O_WRONLY | os.O_EXCL | os.O_CREAT,
                             0o0600)
            except FileExistsError:
                continue
            else:
                break
        w = os.fdopen(fd, 'w')
        with w:
            w.write("# This is the network config written by '{}'\n".format(
                self.opts.project))
            w.write(yaml.dump(config))
        os.rename(tmppath, netplan_path)
        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:
            tasks = [
                ('generate', BackgroundProcess(['/lib/netplan/generate'])),
                ('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()
Example #2
0
    def network_finish(self, config):
        log.debug("network config: \n%s",
                  yaml.dump(config, default_flow_style=False))

        if self.opts.dry_run:
            if hasattr(self, 'tried_once'):
                tasks = [
                    ('one', BackgroundProcess(['sleep', '0.1'])),
                    ('two', PythonSleep(0.1)),
                    ('three', BackgroundProcess(['sleep', '0.1'])),
                ]
            else:
                self.tried_once = True
                tasks = [
                    ('timeout', WaitForDefaultRouteTask(30)),
                    ('one', BackgroundProcess(['sleep', '0.1'])),
                    ('two', BackgroundProcess(['sleep', '0.1'])),
                    ('three', BackgroundProcess(['false'])),
                    ('four', BackgroundProcess(['sleep', '0.1'])),
                ]
        else:
            with open('/etc/netplan/00-snapd-config.yaml', 'w') as w:
                w.write(
                    "# This is the network config written by 'console-conf'\n")
                w.write(yaml.dump(config))
            tasks = [
                ('generate', BackgroundProcess(['/lib/netplan/generate'])),
                ('apply', BackgroundProcess(['netplan', 'apply'])),
                ('timeout', WaitForDefaultRouteTask(30)),
            ]

        def cancel():
            self.cs.cancel()
            self.task_error('canceled')

        self.acw = ApplyingConfigWidget(len(tasks), cancel)
        self.ui.frame.body.show_overlay(self.acw)

        self.cs = TaskSequence(self.loop, tasks, self)
        self.cs.run()
Example #3
0
    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()
Example #4
0
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))
Example #5
0
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'))
Example #6
0
class NetworkController(BaseController):
    signals = [
        ('menu:network:main:start', 'start'),
        ('network:finish', 'network_finish'),
        ('menu:network:main:configure-interface',
         'network_configure_interface'),
        ('menu:network:main:configure-ipv4-interface',
         'network_configure_ipv4_interface'),
        ('menu:network:main:configure-wlan-interface',
         'network_configure_wlan_interface'),
        ('menu:network:main:set-default-v4-route', 'set_default_v4_route'),
        ('menu:network:main:set-default-v6-route', 'set_default_v6_route'),
    ]

    def __init__(self, common):
        super().__init__(common)
        self.model = NetworkModel(self.prober, self.opts)

    def default(self):
        self.model.reset()
        log.info("probing for network devices")
        self.model.probe_network()
        self.signal.emit_signal('menu:network:main:start')

    def start(self):
        title = "Network connections"
        excerpt = ("Configure at least the main interface this server will "
                   "use to receive updates.")
        footer = ("Additional networking info here")
        self.ui.set_header(title, excerpt)
        self.ui.set_footer(footer, 20)
        self.ui.set_body(NetworkView(self.model, self.signal))

    def network_finish(self, config):
        log.debug("network config: \n%s",
                  yaml.dump(config, default_flow_style=False))

        if self.opts.dry_run:
            if hasattr(self, 'tried_once'):
                tasks = [
                    ('one', BackgroundProcess(['sleep', '0.1'])),
                    ('two', PythonSleep(0.1)),
                    ('three', BackgroundProcess(['sleep', '0.1'])),
                ]
            else:
                self.tried_once = True
                tasks = [
                    ('timeout', WaitForDefaultRouteTask(30)),
                    ('one', BackgroundProcess(['sleep', '0.1'])),
                    ('two', BackgroundProcess(['sleep', '0.1'])),
                    ('three', BackgroundProcess(['false'])),
                    ('four', BackgroundProcess(['sleep', '0.1'])),
                ]
        else:
            with open('/etc/netplan/00-snapd-config.yaml', 'w') as w:
                w.write(
                    "# This is the network config written by 'console-conf'\n")
                w.write(yaml.dump(config))
            tasks = [
                ('generate', BackgroundProcess(['/lib/netplan/generate'])),
                ('apply', BackgroundProcess(['netplan', 'apply'])),
                ('timeout', WaitForDefaultRouteTask(30)),
            ]

        def cancel():
            self.cs.cancel()
            self.task_error('canceled')

        self.acw = ApplyingConfigWidget(len(tasks), cancel)
        self.ui.frame.body.show_overlay(self.acw)

        self.cs = TaskSequence(self.loop, tasks, self)
        self.cs.run()

    def task_complete(self, stage):
        self.acw.advance()

    def task_error(self, stage):
        self.ui.frame.body.remove_overlay(self.acw)
        self.ui.frame.body.show_network_error(stage)

    def tasks_finished(self):
        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, netifaces.AF_INET,
                                       self.signal))

    def set_default_v6_route(self):
        self.ui.set_header("Default route")
        self.ui.set_body(
            NetworkSetDefaultRouteView(self.model, netifaces.AF_INET6,
                                       self.signal))

    def bond_interfaces(self):
        self.ui.set_header("Bond interfaces")
        self.ui.set_body(NetworkBondInterfacesView(self.model, self.signal))

    def network_configure_interface(self, iface):
        self.ui.set_header("Network interface {}".format(iface))
        self.ui.set_body(
            NetworkConfigureInterfaceView(self.model, self.signal, iface))

    def network_configure_ipv4_interface(self, iface):
        self.model.prev_signal = ('Back to configure interface menu',
                                  'network:configure-interface-menu',
                                  'network_configure_interface')
        self.ui.set_header("Network interface {} manual IPv4 "
                           "configuration".format(iface))
        self.ui.set_body(
            NetworkConfigureIPv4InterfaceView(self.model, self.signal, iface))

    def network_configure_wlan_interface(self, iface):
        self.model.prev_signal = ('Back to configure interface menu',
                                  'network:configure-interface-menu',
                                  'network_configure_interface')
        self.ui.set_header("Network interface {} manual IPv4 "
                           "configuration".format(iface))
        self.ui.set_body(
            NetworkConfigureWLANView(self.model, self.signal, iface))

    def network_configure_ipv6_interface(self, iface):
        self.model.prev_signal = ('Back to configure interface menu',
                                  'network:configure-interface-menu',
                                  'network_configure_interface')
        self.ui.set_body(DummyView(self.signal))

    def install_network_driver(self):
        self.ui.set_body(DummyView(self.signal))
Example #7
0
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))
Example #8
0
class NetworkController(BaseController):
    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):
        code = subprocess.call(['udevadm', 'settle', '-t', '0'])
        if code != 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):
        title = _("Network connections")
        excerpt = _(
            "Configure at least one interface this server can use to talk to "
            "other machines, and which preferably provides sufficient access for "
            "updates.")
        footer = _(
            "Select an interface to configure it or select Done to continue")
        self.ui.set_header(title, excerpt)
        self.ui.set_footer(footer)
        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))

        netplan_path = self.netplan_path
        while True:
            try:
                tmppath = '%s.%s' % (netplan_path, random.randrange(0, 1000))
                fd = os.open(tmppath, os.O_WRONLY | os.O_EXCL | os.O_CREAT,
                             0o0600)
            except FileExistsError:
                continue
            else:
                break
        w = os.fdopen(fd, 'w')
        with w:
            w.write("# This is the network config written by '{}'\n".format(
                self.opts.project))
            w.write(yaml.dump(config))
        os.rename(tmppath, netplan_path)
        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:
            tasks = [
                ('generate', BackgroundProcess(['/lib/netplan/generate'])),
                ('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.loop.set_alarm_in(
            0.0, lambda loop, ud: 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_header("Bond interfaces")
        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))

    def install_network_driver(self):
        self.ui.set_body(DummyView(self))