Beispiel #1
0
 def process_sriov_config(config_manager, exit_on_error=True):  # pragma: nocover (covered in autopkgtest)
     try:
         apply_sriov_config(config_manager)
     except (ConfigurationError, RuntimeError) as e:
         logging.error(str(e))
         if exit_on_error:
             sys.exit(1)
    def test_apply_sriov_config(self, gim, gidn, apply_vlan, quirks,
                                set_numvfs, get_counts, netifs):
        # set up the environment
        with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"),
                  'w') as fd:
            print('''network:
  version: 2
  renderer: networkd
  ethernets:
    enp1:
      mtu: 9000
    enpx:
      match:
        name: enp[2-3]
    enp1s16f1:
      link: enp1
      macaddress: 01:02:03:04:05:00
    enp1s16f2:
      link: enp1
    customvf1:
      match:
        name: enp[2-3]s16f[1-4]
      link: enpx
  vlans:
    vf1.15:
      renderer: sriov
      id: 15
      link: customvf1
    vf1.16:
      renderer: sriov
      id: 16
      link: foobar
''',
                  file=fd)
        self.configmanager.parse()
        interfaces = ['enp1', 'enp2', 'enp5', 'wlp6s0']
        # set up all the mock objects
        netifs.return_value = [
            'enp1', 'enp2', 'enp5', 'wlp6s0', 'enp1s16f1', 'enp1s16f2',
            'enp2s16f1'
        ]
        get_counts.side_effect = mock_set_counts
        set_numvfs.side_effect = lambda pf, _: False if pf == 'enp2' else True
        gidn.return_value = 'foodriver'
        gim.return_value = '00:01:02:03:04:05'

        # call method under test
        sriov.apply_sriov_config(interfaces, self.configmanager)

        # check if the config got applied as expected
        # we had 2 PFs, one having two VFs and the other only one
        self.assertEqual(set_numvfs.call_count, 2)
        self.assertListEqual(
            set_numvfs.call_args_list,
            [call('enp1', 2), call('enp2', 1)])
        # one of the pfs already had sufficient VFs allocated, so only enp1
        # changed the vf count and only that one should trigger quirks
        quirks.assert_called_once_with('enp1')
        # only one had a hardware vlan
        apply_vlan.assert_called_once_with('enp2', 'enp2s16f1', 'vf1.15', 15)
    def test_apply_sriov_config_too_many_vlans(self, gim, gidn, apply_vlan,
                                               quirks, set_numvfs, get_counts,
                                               netifs):
        # set up the environment
        with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"),
                  'w') as fd:
            print('''network:
  version: 2
  renderer: networkd
  ethernets:
    enp1:
      mtu: 9000
    enpx:
      match:
        name: enp[2-3]
    enp1s16f1:
      link: enp1
      macaddress: 01:02:03:04:05:00
    enp1s16f2:
      link: enp1
    customvf1:
      match:
        name: enp[2-3]s16f[1-4]
      link: enpx
  vlans:
    vf1.15:
      renderer: sriov
      id: 15
      link: customvf1
    vf1.16:
      renderer: sriov
      id: 16
      link: customvf1
''',
                  file=fd)
        self.configmanager.parse()
        interfaces = ['enp1', 'enp2', 'enp5', 'wlp6s0']
        # set up all the mock objects
        netifs.return_value = [
            'enp1', 'enp2', 'enp5', 'wlp6s0', 'enp1s16f1', 'enp1s16f2',
            'enp2s16f1'
        ]
        get_counts.side_effect = mock_set_counts
        set_numvfs.side_effect = lambda pf, _: False if pf == 'enp2' else True
        gidn.return_value = 'foodriver'
        gim.return_value = '00:01:02:03:04:05'

        # call method under test
        with self.assertRaises(ConfigurationError) as e:
            sriov.apply_sriov_config(interfaces, self.configmanager)

        self.assertIn(
            'interface enp2s16f1 for netplan device customvf1 (vf1.16) already has an SR-IOV vlan defined',
            str(e.exception))
        self.assertEqual(apply_vlan.call_count, 1)
Beispiel #4
0
    def test_apply_sriov_config_many_match(self, gim, gidn, apply_vlan, quirks,
                                           set_numvfs, get_counts, netifs):
        # set up the environment
        with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"),
                  'w') as fd:
            print('''network:
  version: 2
  renderer: networkd
  ethernets:
    enp1:
      mtu: 9000
    enpx:
      match:
        name: enp[2-3]
    enp1s16f1:
      link: enp1
      macaddress: 01:02:03:04:05:00
    enp1s16f2:
      link: enp1
    customvf1:
      match:
        name: enp*s16f[1-4]
      link: enpx
''',
                  file=fd)
        # set up all the mock objects
        netifs.return_value = [
            'enp1', 'enp2', 'enp5', 'wlp6s0', 'enp1s16f1', 'enp1s16f2',
            'enp2s16f1'
        ]
        get_counts.side_effect = mock_set_counts
        set_numvfs.side_effect = lambda pf, _: False if pf == 'enp2' else True
        gidn.return_value = 'foodriver'
        gim.return_value = '00:01:02:03:04:05'

        # call method under test
        with self.assertRaises(ConfigurationError) as e:
            sriov.apply_sriov_config(self.configmanager)

        self.assertIn(
            'matched more than one interface for a VF device: customvf1',
            str(e.exception))
Beispiel #5
0
    def command_apply(
            run_generate=True,
            sync=False,
            exit_on_error=True):  # pragma: nocover (covered in autopkgtest)
        # if we are inside a snap, then call dbus to run netplan apply instead
        if "SNAP" in os.environ:
            # TODO: maybe check if we are inside a classic snap and don't do
            # this if we are in a classic snap?
            busctl = shutil.which("busctl")
            if busctl is None:
                raise RuntimeError("missing busctl utility")
            res = subprocess.call([
                busctl,
                "call",
                "--quiet",
                "--system",
                "io.netplan.Netplan",  # the service
                "/io/netplan/Netplan",  # the object
                "io.netplan.Netplan",  # the interface
                "Apply",  # the method
            ])

            if res != 0:
                if exit_on_error:
                    sys.exit(res)
                elif res == 130:
                    raise PermissionError(
                        "failed to communicate with dbus service")
                elif res == 1:
                    raise RuntimeError(
                        "failed to communicate with dbus service")
            else:
                return

        old_files_networkd = bool(glob.glob('/run/systemd/network/*netplan-*'))
        old_files_nm = bool(
            glob.glob('/run/NetworkManager/system-connections/netplan-*'))

        generator_call = []
        generate_out = None
        if 'NETPLAN_PROFILE' in os.environ:
            generator_call.extend(['valgrind', '--leak-check=full'])
            generate_out = subprocess.STDOUT

        generator_call.append(utils.get_generator_path())
        if run_generate and subprocess.call(generator_call,
                                            stderr=generate_out) != 0:
            if exit_on_error:
                sys.exit(os.EX_CONFIG)
            else:
                raise ConfigurationError(
                    "the configuration could not be generated")

        config_manager = ConfigManager()
        devices = netifaces.interfaces()

        # Re-start service when
        # 1. We have configuration files for it
        # 2. Previously we had config files for it but not anymore
        # Ideally we should compare the content of the *netplan-* files before and
        # after generation to minimize the number of re-starts, but the conditions
        # above works too.
        restart_networkd = bool(glob.glob('/run/systemd/network/*netplan-*'))
        if not restart_networkd and old_files_networkd:
            restart_networkd = True
        restart_nm = bool(
            glob.glob('/run/NetworkManager/system-connections/netplan-*'))
        if not restart_nm and old_files_nm:
            restart_nm = True

        # stop backends
        if restart_networkd:
            logging.debug(
                'netplan generated networkd configuration changed, restarting networkd'
            )
            # Running 'systemctl daemon-reload' will re-run the netplan systemd generator,
            # so let's make sure we only run it iff we're willing to run 'netplan generate'
            if run_generate:
                utils.systemctl_daemon_reload()
            wpa_services = ['netplan-wpa-*.service']
            # Historically (up to v0.98) we had netplan-wpa@*.service files, in case of an
            # upgraded system, we need to make sure to stop those.
            if utils.systemctl_is_active('netplan-wpa@*.service'):
                wpa_services.insert(0, 'netplan-wpa@*.service')
            utils.systemctl_networkd('stop',
                                     sync=sync,
                                     extra_services=wpa_services)

        else:
            logging.debug('no netplan generated networkd configuration exists')

        if restart_nm:
            logging.debug(
                'netplan generated NM configuration changed, restarting NM')
            if utils.nm_running():
                # restarting NM does not cause new config to be applied, need to shut down devices first
                for device in devices:
                    # ignore failures here -- some/many devices might not be managed by NM
                    try:
                        utils.nmcli(['device', 'disconnect', device])
                    except subprocess.CalledProcessError:
                        pass

                utils.systemctl_network_manager('stop', sync=sync)
        else:
            logging.debug('no netplan generated NM configuration exists')

        # Refresh devices now; restarting a backend might have made something appear.
        devices = netifaces.interfaces()

        # evaluate config for extra steps we need to take (like renaming)
        # for now, only applies to non-virtual (real) devices.
        config_manager.parse()
        changes = NetplanApply.process_link_changes(devices, config_manager)

        # apply any SR-IOV related changes, if applicable
        try:
            apply_sriov_config(devices, config_manager)
        except (ConfigurationError, RuntimeError) as e:
            logging.error(str(e))
            if exit_on_error:
                sys.exit(1)

        # if the interface is up, we can still apply some .link file changes
        devices = netifaces.interfaces()
        for device in devices:
            logging.debug('netplan triggering .link rules for %s', device)
            try:
                subprocess.check_call([
                    'udevadm', 'test-builtin', 'net_setup_link',
                    '/sys/class/net/' + device
                ],
                                      stdout=subprocess.DEVNULL,
                                      stderr=subprocess.DEVNULL)
            except subprocess.CalledProcessError:
                logging.debug('Ignoring device without syspath: %s', device)

        # apply renames to "down" devices
        for iface, settings in changes.items():
            if settings.get('name'):
                subprocess.check_call([
                    'ip', 'link', 'set', 'dev', iface, 'name',
                    settings.get('name')
                ],
                                      stdout=subprocess.DEVNULL,
                                      stderr=subprocess.DEVNULL)

        subprocess.check_call(['udevadm', 'settle'])

        # (re)start backends
        if restart_networkd:
            netplan_wpa = [
                os.path.basename(f) for f in glob.glob(
                    '/run/systemd/system/*.wants/netplan-wpa-*.service')
            ]
            utils.systemctl_networkd('start',
                                     sync=sync,
                                     extra_services=netplan_wpa)
        if restart_nm:
            utils.systemctl_network_manager('start', sync=sync)