Example #1
0
class Iproute2(Configurator):
    def __init__(self, inRollback=False):
        self.unifiedPersistence = True
        super(Iproute2, self).__init__(ConfigApplier(), inRollback)
        self.runningConfig = RunningConfig()

    def begin(self):
        if self.configApplier is None:
            self.configApplier = ConfigApplier()
        if self.runningConfig is None:
            self.runningConfig = RunningConfig()

    def commit(self):
        self.configApplier = None
        self.runningConfig.save()
        self.runningConfig = None

    def configureBridge(self, bridge, **opts):
        self.configApplier.addBridge(bridge)
        if bridge.port:
            bridge.port.configure(**opts)
            self.configApplier.addBridgePort(bridge)
        DynamicSourceRoute.addInterfaceTracking(bridge)
        self.configApplier.setIfaceConfigAndUp(bridge)
        if not bridge.ipv6.address and not bridge.ipv6.ipv6autoconf and (
                not bridge.ipv6.dhcpv6 and misc.ipv6_supported()):
            wait_for_device(bridge.name)
            sysctl.disable_ipv6(bridge.name)
        self._addSourceRoute(bridge)
        if 'custom' in opts and 'bridge_opts' in opts['custom']:
            self.configApplier._setBridgeOpts(bridge,
                                              opts['custom']['bridge_opts'])

    def configureVlan(self, vlan, **opts):
        vlan.device.configure(**opts)
        self.configApplier.addVlan(vlan)
        DynamicSourceRoute.addInterfaceTracking(vlan)
        self.configApplier.setIfaceConfigAndUp(vlan)
        self._addSourceRoute(vlan)

    def configureBond(self, bond, **opts):
        self.configApplier.addBond(bond)
        if not bond.areOptionsApplied():
            self.configApplier.ifdown(bond)
            self.configApplier.addBondOptions(bond)
        for slave in bond.slaves:
            if slave.name not in bonding.slaves(bond.name):
                self.configApplier.addBondSlave(bond, slave)
                slave.configure(**opts)
        DynamicSourceRoute.addInterfaceTracking(bond)
        self.configApplier.setIfaceConfigAndUp(bond)
        self._addSourceRoute(bond)
        self.runningConfig.setBonding(
            bond.name, {'options': bond.options,
                        'nics': [slave.name for slave in bond.slaves],
                        'switch': 'legacy'})

    def editBonding(self, bond, _netinfo):
        """
        Modifies the bond so that the bond in the system ends up with the
        same slave and options configuration that are requested. Makes a
        best effort not to interrupt connectivity.
        """
        nicsToSet = frozenset(nic.name for nic in bond.slaves)
        currentNics = frozenset(_netinfo.getNicsForBonding(bond.name))
        nicsToAdd = nicsToSet
        nicsToRemove = currentNics

        if bond.areOptionsApplied():
            nicsToAdd -= currentNics
            nicsToRemove -= nicsToSet

        for nic in nicsToRemove:
            slave = Nic(nic, self, _netinfo=_netinfo)
            self.configApplier.removeBondSlave(bond, slave)
            slave.remove()

        if not bond.areOptionsApplied():
            self.configApplier.ifdown(bond)
            self.configApplier.addBondOptions(bond)

        for slave in bond.slaves:
            if slave.name in nicsToAdd:
                self.configApplier.addBondSlave(bond, slave)

        self.configApplier.ifup(bond)
        self.runningConfig.setBonding(
            bond.name, {'options': bond.options,
                        'nics': [slave.name for slave in bond.slaves],
                        'switch': 'legacy'})

    def configureNic(self, nic, **opts):
        DynamicSourceRoute.addInterfaceTracking(nic)
        self.configApplier.setIfaceConfigAndUp(nic)
        self._addSourceRoute(nic)

        ethtool_opts = getEthtoolOpts(nic.name)
        if ethtool_opts:
            # We ignore ethtool's return code to maintain initscripts'
            # behaviour.
            execCmd(
                [_ETHTOOL_BINARY.cmd, '-K', nic.name] + ethtool_opts.split())

    def removeBridge(self, bridge):
        DynamicSourceRoute.addInterfaceTracking(bridge)
        self.configApplier.ifdown(bridge)
        self._removeSourceRoute(bridge, DynamicSourceRoute)
        self.configApplier.removeBridge(bridge)
        if bridge.port:
            bridge.port.remove()

    def removeVlan(self, vlan):
        DynamicSourceRoute.addInterfaceTracking(vlan)
        self.configApplier.ifdown(vlan)
        self._removeSourceRoute(vlan, DynamicSourceRoute)
        self.configApplier.removeVlan(vlan)
        vlan.device.remove()

    def _destroyBond(self, bonding):
        for slave in bonding.slaves:
            self.configApplier.removeBondSlave(bonding, slave)
            slave.remove()
        self.configApplier.removeBond(bonding)

    def removeBond(self, bonding):
        toBeRemoved = not ifaceUsed(bonding.name)

        if toBeRemoved:
            if bonding.master is None:
                self.configApplier.removeIpConfig(bonding)
                DynamicSourceRoute.addInterfaceTracking(bonding)
                self._removeSourceRoute(bonding, DynamicSourceRoute)

            if bonding.on_removal_just_detach_from_network:
                self.configApplier.setIfaceMtu(bonding.name,  mtus.DEFAULT_MTU)
                self.configApplier.ifdown(bonding)
            else:
                self._destroyBond(bonding)
                self.runningConfig.removeBonding(bonding.name)
        else:
            self._setNewMtu(bonding, vlans.vlan_devs_for_iface(bonding.name))

    def removeNic(self, nic, remove_even_if_used=False):
        """
        Remove a nic from the kernel. By default, do nothing if the nic is used
        When remove_even_if_used=True, remove the nic anyway
        # FIXME the caller of this method is responsible to remove
        # the nic from its users (such as bond)
        """
        toBeRemoved = not ifaceUsed(nic.name) or remove_even_if_used

        if toBeRemoved:
            if nic.master is None:
                self.configApplier.removeIpConfig(nic)
                DynamicSourceRoute.addInterfaceTracking(nic)
                self._removeSourceRoute(nic, DynamicSourceRoute)
            else:
                self.configApplier.setIfaceMtu(nic.name, mtus.DEFAULT_MTU)
                self.configApplier.ifdown(nic)
        else:
            self._setNewMtu(nic, vlans.vlan_devs_for_iface(nic.name))

    @staticmethod
    def configureSourceRoute(routes, rules, device):
        for route in routes:
            routeAdd(route)

        for rule in rules:
            ruleAdd(rule)

    @staticmethod
    def removeSourceRoute(routes, rules, device):
        for route in routes:
            try:
                routeDel(route)
            except IPRoute2Error as e:
                if 'No such process' in e.message[0]:
                    # The kernel or dhclient has won the race and removed the
                    # route already. We have yet to remove routing rules.
                    pass
                else:
                    raise

        for rule in rules:
            ruleDel(rule)
Example #2
0
class Ifcfg(Configurator):
    # TODO: Do all the configApplier interaction from here.
    def __init__(self, inRollback=False):
        self.unifiedPersistence = \
            config.get('vars', 'net_persistence') == 'unified'
        super(Ifcfg, self).__init__(ConfigWriter(self.unifiedPersistence),
                                    inRollback)
        if self.unifiedPersistence:
            self.runningConfig = RunningConfig()

    def begin(self):
        if self.configApplier is None:
            self.configApplier = ConfigWriter(self.unifiedPersistence)
        if self.unifiedPersistence and self.runningConfig is None:
            self.runningConfig = RunningConfig()

    def rollback(self):
        """This reimplementation always returns None since Ifcfg can rollback
        on its own via restoreBackups(). This makes the general mechanism of
        API.Global._rollback redundant in this case."""
        self.configApplier.restoreBackups()
        self.configApplier = None
        if self.unifiedPersistence:
            self.runningConfig = None

    def commit(self):
        self.configApplier = None
        if self.unifiedPersistence:
            self.runningConfig.save()
            self.runningConfig = None

    def configureBridge(self, bridge, **opts):
        self.configApplier.addBridge(bridge, **opts)
        ifdown(bridge.name)
        if bridge.port:
            bridge.port.configure(**opts)
        self._addSourceRoute(bridge)
        _ifup(bridge)

    def configureVlan(self, vlan, **opts):
        self.configApplier.addVlan(vlan, **opts)
        vlan.device.configure(**opts)
        self._addSourceRoute(vlan)
        _ifup(vlan)

    def configureBond(self, bond, **opts):
        self.configApplier.addBonding(bond, **opts)
        if not vlans.is_vlanned(bond.name):
            for slave in bond.slaves:
                ifdown(slave.name)
        for slave in bond.slaves:
            slave.configure(**opts)
        self._addSourceRoute(bond)
        _ifup(bond)
        if self.unifiedPersistence:
            self.runningConfig.setBonding(
                bond.name, {
                    'options': bond.options,
                    'nics': [slave.name for slave in bond.slaves],
                    'switch': 'legacy'
                })

    def editBonding(self, bond, _netinfo):
        """
        Modifies the bond so that the bond in the system ends up with the
        same slave and options configuration that are requested. Makes a
        best effort not to interrupt connectivity.
        """
        nicsToSet = frozenset(nic.name for nic in bond.slaves)
        currentNics = frozenset(_netinfo.getNicsForBonding(bond.name))
        nicsToAdd = nicsToSet - currentNics

        # Create bond configuration in case it was a non ifcfg controlled bond.
        # Needed to be before slave configuration for initscripts to add slave
        # to bond.
        bondIfcfgWritten = False
        isIfcfgControlled = os.path.isfile(NET_CONF_PREF + bond.name)
        areOptionsApplied = bond.areOptionsApplied()
        if not isIfcfgControlled or not areOptionsApplied:
            bridgeName = _netinfo.getBridgedNetworkForIface(bond.name)
            if isIfcfgControlled and bridgeName:
                bond.master = Bridge(bridgeName, self, port=bond)
            self.configApplier.addBonding(bond)
            bondIfcfgWritten = True

        for nic in currentNics - nicsToSet:
            ifdown(nic)  # So that no users will be detected for it.
            Nic(nic, self, _netinfo=_netinfo).remove()

        for slave in bond.slaves:
            if slave.name in nicsToAdd:
                ifdown(slave.name)  # nics must be down to join a bond
                self.configApplier.addNic(slave)
                _exec_ifup(slave)
            else:
                # Let NetworkManager know that we now own the slave.
                self.configApplier.addNic(slave)

        if bondIfcfgWritten:
            ifdown(bond.name)
            _restore_default_bond_options(bond.name, bond.options)
            _exec_ifup(bond)
        if self.unifiedPersistence:
            self.runningConfig.setBonding(
                bond.name, {
                    'options': bond.options,
                    'nics': [slave.name for slave in bond.slaves],
                    'switch': 'legacy'
                })

    def configureNic(self, nic, **opts):
        self.configApplier.addNic(nic, **opts)
        self._addSourceRoute(nic)
        if nic.bond is None:
            if not vlans.is_vlanned(nic.name):
                ifdown(nic.name)
            _ifup(nic)

    def removeBridge(self, bridge):
        DynamicSourceRoute.addInterfaceTracking(bridge)
        ifdown(bridge.name)
        self._removeSourceRoute(bridge, StaticSourceRoute)
        commands.execCmd([constants.EXT_BRCTL, 'delbr', bridge.name])
        self.configApplier.removeBridge(bridge.name)
        if bridge.port:
            bridge.port.remove()

    def removeVlan(self, vlan):
        DynamicSourceRoute.addInterfaceTracking(vlan)
        ifdown(vlan.name)
        self._removeSourceRoute(vlan, StaticSourceRoute)
        self.configApplier.removeVlan(vlan.name)
        vlan.device.remove()

    def _ifaceDownAndCleanup(self, iface, remove_even_if_used=False):
        """Returns True iff the iface is to be removed."""
        DynamicSourceRoute.addInterfaceTracking(iface)
        to_be_removed = remove_even_if_used or not ifaceUsed(iface.name)
        if to_be_removed:
            ifdown(iface.name)
        self._removeSourceRoute(iface, StaticSourceRoute)
        return to_be_removed

    def _addSourceRoute(self, netEnt):
        """For ifcfg tracking can be done together with route/rule addition"""
        super(Ifcfg, self)._addSourceRoute(netEnt)
        DynamicSourceRoute.addInterfaceTracking(netEnt)

    def removeBond(self, bonding):
        to_be_removed = self._ifaceDownAndCleanup(bonding)
        if to_be_removed:
            self.configApplier.removeBonding(bonding.name)
            if bonding.on_removal_just_detach_from_network:
                # Recreate the bond with ip and master info cleared
                bonding.ipv4 = address.IPv4()
                bonding.ipv6 = address.IPv6()
                bonding.master = None
                bonding.configure()
            else:
                for slave in bonding.slaves:
                    slave.remove()
                if self.unifiedPersistence:
                    self.runningConfig.removeBonding(bonding.name)
        else:
            set_mtu = self._setNewMtu(bonding,
                                      vlans.vlan_devs_for_iface(bonding.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value.
            # Note that ip link set dev bondX mtu Y sets Y on all its links
            if set_mtu is not None:
                ipwrapper.linkSet(bonding.name, ['mtu', str(set_mtu)])

    def removeNic(self, nic, remove_even_if_used=False):
        to_be_removed = self._ifaceDownAndCleanup(nic, remove_even_if_used)
        if to_be_removed:
            self.configApplier.removeNic(nic.name)
            if nic.name in nics.nics():
                _exec_ifup(nic)
            else:
                logging.warning('host interface %s missing', nic.name)
        else:
            set_mtu = self._setNewMtu(nic, vlans.vlan_devs_for_iface(nic.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value
            if set_mtu is not None:
                ipwrapper.linkSet(nic.name, ['mtu', str(set_mtu)])

    def _getFilePath(self, fileType, device):
        return os.path.join(NET_CONF_DIR, '%s-%s' % (fileType, device))

    def _removeSourceRouteFile(self, fileType, device):
        filePath = self._getFilePath(fileType, device)
        self.configApplier._backup(filePath)
        self.configApplier._removeFile(filePath)

    def _writeConfFile(self, contents, fileType, device):
        filePath = self._getFilePath(fileType, device)

        configuration = ''
        for entry in contents:
            configuration += str(entry) + '\n'

        self.configApplier.writeConfFile(filePath, configuration)

    def configureSourceRoute(self, routes, rules, device):
        self._writeConfFile(routes, 'route', device)
        self._writeConfFile(rules, 'rule', device)

    def removeSourceRoute(self, routes, rules, device):
        self._removeSourceRouteFile('rule', device)
        self._removeSourceRouteFile('route', device)
Example #3
0
class Ifcfg(Configurator):
    # TODO: Do all the configApplier interaction from here.
    def __init__(self, net_info, inRollback=False):
        is_unipersistence = config.get('vars', 'net_persistence') == 'unified'
        super(Ifcfg, self).__init__(ConfigWriter(),
                                    net_info,
                                    is_unipersistence,
                                    inRollback)
        self.runningConfig = RunningConfig()

    def rollback(self):
        """This reimplementation always returns None since Ifcfg can rollback
        on its own via restoreBackups(). This makes the general mechanism of
        vdsm.API.Global._rollback redundant in this case."""
        self.configApplier.restoreBackups()
        self.configApplier = None
        self.runningConfig = None

    def commit(self):
        self.configApplier = None
        self.runningConfig.save()
        self.runningConfig = None

    def configureBridge(self, bridge, **opts):
        if not self.owned_device(bridge.name):
            IfcfgAcquire.acquire_device(bridge.name)
        self.configApplier.addBridge(bridge, **opts)
        ifdown(bridge.name)
        if bridge.port:
            bridge.port.configure(**opts)
        self._addSourceRoute(bridge)
        _ifup(bridge)

    def configureVlan(self, vlan, **opts):
        if not self.owned_device(vlan.name):
            IfcfgAcquire.acquire_device(vlan.name)
            IfcfgAcquire.acquire_vlan_device(vlan.name)
        self.configApplier.addVlan(vlan, **opts)
        vlan.device.configure(**opts)
        self._addSourceRoute(vlan)
        if isinstance(vlan.device, bond_model):
            Ifcfg._ifup_vlan_with_slave_bond_hwaddr_sync(vlan)
        else:
            _ifup(vlan)

    def configureBond(self, bond, **opts):
        if not self.owned_device(bond.name):
            IfcfgAcquire.acquire_device(bond.name)
        self.configApplier.addBonding(bond, self.net_info, **opts)
        if not vlans.is_vlanned(bond.name):
            for slave in bond.slaves:
                ifdown(slave.name)
        for slave in bond.slaves:
            slave.configure(**opts)
        self._addSourceRoute(bond)
        _ifup(bond)

        # When acquiring the device from NM, it may take a few seconds until
        # the bond is released by NM and loaded through initscripts.
        # Giving it a chance to come up before continuing.
        with waitfor.waitfor_linkup(bond.name):
            pass

        self.runningConfig.setBonding(
            bond.name, {'options': bond.options,
                        'nics': sorted(s.name for s in bond.slaves),
                        'switch': 'legacy'})

    def editBonding(self, bond, _netinfo):
        """
        Modifies the bond so that the bond in the system ends up with the
        same slave and options configuration that are requested. Makes a
        best effort not to interrupt connectivity.
        """
        nicsToSet = frozenset(nic.name for nic in bond.slaves)
        currentNics = frozenset(_netinfo.getNicsForBonding(bond.name))
        nicsToAdd = nicsToSet - currentNics

        if not self.owned_device(bond.name):
            IfcfgAcquire.acquire_device(bond.name)

        # Create bond configuration in case it was a non ifcfg controlled bond.
        # Needed to be before slave configuration for initscripts to add slave
        # to bond.
        bondIfcfgWritten = False
        isIfcfgControlled = os.path.isfile(NET_CONF_PREF + bond.name)
        areOptionsApplied = bond.areOptionsApplied()
        if not isIfcfgControlled or not areOptionsApplied:
            bridgeName = _netinfo.getBridgedNetworkForIface(bond.name)
            if isIfcfgControlled and bridgeName:
                bond.master = Bridge(bridgeName, self, port=bond)
            self.configApplier.addBonding(bond, _netinfo)
            bondIfcfgWritten = True

        for nic in currentNics - nicsToSet:
            ifdown(nic)  # So that no users will be detected for it.
            Nic(nic, self, _netinfo=_netinfo).remove()

        for slave in bond.slaves:
            if slave.name in nicsToAdd:
                ifdown(slave.name)  # nics must be down to join a bond
                self.configApplier.addNic(slave, _netinfo)
                _exec_ifup(slave)
            else:
                # Let NetworkManager know that we now own the slave.
                self.configApplier.addNic(slave, _netinfo)

        if bondIfcfgWritten:
            ifdown(bond.name)
            _restore_default_bond_options(bond.name, bond.options)
            _exec_ifup(bond)
        self.runningConfig.setBonding(
            bond.name, {'options': bond.options,
                        'nics': sorted(slave.name for slave in bond.slaves),
                        'switch': 'legacy'})

    def configureNic(self, nic, **opts):
        if not self.owned_device(nic.name):
            IfcfgAcquire.acquire_device(nic.name)
        self.configApplier.addNic(nic, self.net_info, **opts)
        self._addSourceRoute(nic)
        if nic.bond is None:
            if not vlans.is_vlanned(nic.name):
                ifdown(nic.name)
            _ifup(nic)

    def removeBridge(self, bridge):
        if not self.owned_device(bridge.name):
            IfcfgAcquire.acquire_device(bridge.name)
        if bridge.ipv4.bootproto == 'dhcp':
            ifacetracking.add(bridge.name)
        ifdown(bridge.name)
        self._removeSourceRoute(bridge)
        cmd.exec_sync([EXT_BRCTL, 'delbr', bridge.name])
        self.configApplier.removeBridge(bridge.name)
        self.net_info.del_bridge(bridge.name)
        if bridge.port:
            bridge.port.remove()

    def removeVlan(self, vlan):
        if not self.owned_device(vlan.name):
            IfcfgAcquire.acquire_device(vlan.name)
            IfcfgAcquire.acquire_vlan_device(vlan.name)
        if vlan.ipv4.bootproto == 'dhcp':
            ifacetracking.add(vlan.name)
        ifdown(vlan.name)
        self._removeSourceRoute(vlan)
        self.configApplier.removeVlan(vlan.name)
        self.net_info.del_vlan(vlan.name)
        vlan.device.remove()

    def _ifaceDownAndCleanup(self, iface, remove_even_if_used=False):
        """Returns True iff the iface is to be removed."""
        if iface.ipv4.bootproto == 'dhcp':
            ifacetracking.add(iface.name)
        to_be_removed = remove_even_if_used or not self.net_info.ifaceUsers(
            iface.name)
        if to_be_removed:
            ifdown(iface.name)
        self._removeSourceRoute(iface)
        return to_be_removed

    def _addSourceRoute(self, netEnt):
        """For ifcfg tracking can be done together with route/rule addition"""
        ipv4 = netEnt.ipv4
        if ipv4.bootproto != 'dhcp' and netEnt.master is None:
            valid_args = (ipv4.address and ipv4.netmask and
                          ipv4.gateway not in (None, '0.0.0.0'))
            if valid_args:
                sroute = StaticSourceRoute(netEnt.name, ipv4.address,
                                           ipv4.netmask, ipv4.gateway)
                self.configureSourceRoute(*sroute.requested_config())

            else:
                logging.warning(
                    'Invalid input for source routing: '
                    'name=%s, addr=%s, netmask=%s, gateway=%s',
                    netEnt.name, ipv4.address, ipv4.netmask, ipv4.gateway)

        if netEnt.ipv4.bootproto == 'dhcp':
            ifacetracking.add(netEnt.name)

    def _removeSourceRoute(self, netEnt):
        if netEnt.ipv4.bootproto != 'dhcp' and netEnt.master is None:
            logging.debug("Removing source route for device %s", netEnt.name)
            sroute = StaticSourceRoute(netEnt.name, None, None, None)
            self.removeSourceRoute(*sroute.current_config())

    def removeBond(self, bonding):
        if not self.owned_device(bonding.name):
            IfcfgAcquire.acquire_device(bonding.name)
        to_be_removed = self._ifaceDownAndCleanup(bonding)
        if to_be_removed:
            self.configApplier.removeBonding(bonding.name)
            if bonding.on_removal_just_detach_from_network:
                # Recreate the bond with ip and master info cleared
                bonding.ipv4 = address.IPv4()
                bonding.ipv6 = address.IPv6()
                bonding.master = None
                bonding.configure()
            else:
                for slave in bonding.slaves:
                    slave.remove()
                self.runningConfig.removeBonding(bonding.name)
        else:
            set_mtu = self._setNewMtu(bonding,
                                      vlans.vlan_devs_for_iface(bonding.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value.
            # Note that ip link set dev bondX mtu Y sets Y on all its links
            if set_mtu is not None:
                ipwrapper.linkSet(bonding.name, ['mtu', str(set_mtu)])

            # If the bond was bridged, we must remove BRIDGE parameter from its
            # ifcfg configuration file.
            if bonding.bridge:
                self.configApplier.dropBridgeParameter(bonding.name)

    def removeNic(self, nic, remove_even_if_used=False):
        if not self.owned_device(nic.name):
            IfcfgAcquire.acquire_device(nic.name)

        # If the nic is top device we should ifdown it even if it is
        # used by a VLAN. Otherwise we would keep its IP address.
        remove_even_if_used |= nic.master is None

        to_be_removed = self._ifaceDownAndCleanup(nic, remove_even_if_used)
        if to_be_removed:
            self.configApplier.removeNic(nic.name)
            if nic.name in nics.nics():
                _exec_ifup(nic)
            else:
                logging.warning('host interface %s missing', nic.name)
        else:
            set_mtu = self._setNewMtu(nic, vlans.vlan_devs_for_iface(nic.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value
            if set_mtu is not None:
                ipwrapper.linkSet(nic.name, ['mtu', str(set_mtu)])

            # If the nic was bridged, we must remove BRIDGE parameter from its
            # ifcfg configuration file.
            if nic.bridge:
                self.configApplier.dropBridgeParameter(nic.name)

    def _getFilePath(self, fileType, device):
        return os.path.join(NET_CONF_DIR, '%s-%s' % (fileType, device))

    def _removeSourceRouteFile(self, fileType, device):
        filePath = self._getFilePath(fileType, device)
        self.configApplier._backup(filePath)
        self.configApplier._removeFile(filePath)

    def _writeConfFile(self, contents, fileType, device):
        filePath = self._getFilePath(fileType, device)

        configuration = ''
        for entry in contents:
            configuration += str(entry) + '\n'

        self.configApplier.writeConfFile(filePath, configuration)

    def configureSourceRoute(self, routes, rules, device):
        self._writeConfFile(routes, 'route', device)
        self._writeConfFile(rules, 'rule', device)

    def removeSourceRoute(self, routes, rules, device):
        self._removeSourceRouteFile('rule', device)
        self._removeSourceRouteFile('route', device)

    @staticmethod
    def owned_device(device):
        try:
            with open(misc.NET_CONF_PREF + device) as conf:
                content = conf.read()
        except IOError as ioe:
            if ioe.errno == errno.ENOENT:
                return False
            else:
                raise
        else:
            return content.startswith(CONFFILE_HEADER_SIGNATURE)

    @staticmethod
    def _ifup_vlan_with_slave_bond_hwaddr_sync(vlan):
        """
        When NM is active and the network includes a vlan on top of a bond, the
        following scenario may occur:
        - VLAN over a bond with slaves is already defined in the system and
          VDSM is about to acquire it to define on it a network.
        - The VLAN iface is re-created while the bond slaves are temporary
          detached, causing the vlan to be created with the bond temporary
          mac address, which is different from the original existing address.
        Therefore, following the VLAN ifup command, its mac address is compared
        with the first bond slave. In case they differ, the vlan device is
        recreated.
        """
        bond = vlan.device
        if not bond.slaves:
            _ifup(vlan)
            return

        blocking = _blocking_action_required(vlan)
        for attempt in range(5):
            if blocking:
                _ifup(vlan, blocking=blocking)
            else:
                with waitfor.waitfor_link_exists(vlan.name):
                    _ifup(vlan, blocking=blocking)

            if (link_iface.mac_address(bond.slaves[0].name) ==
                    link_iface.mac_address(vlan.name)):
                return

            logging.info('Config vlan@bond: hwaddr not in sync (%s)', attempt)
            ifdown(vlan.name)

        raise ConfigNetworkError(
            ERR_BAD_BONDING,
            'While adding vlan {} over bond {}, '
            'the bond hwaddr was not in sync '
            'whith its slaves.'.format(vlan.name, vlan.device.name))
Example #4
0
File: ifcfg.py Project: nirs/vdsm
class Ifcfg(Configurator):
    # TODO: Do all the configApplier interaction from here.
    def __init__(self, inRollback=False):
        self.unifiedPersistence = config.get("vars", "net_persistence") == "unified"
        super(Ifcfg, self).__init__(ConfigWriter(self.unifiedPersistence), inRollback)
        if self.unifiedPersistence:
            self.runningConfig = RunningConfig()

    def begin(self):
        if self.configApplier is None:
            self.configApplier = ConfigWriter(self.unifiedPersistence)
        if self.unifiedPersistence and self.runningConfig is None:
            self.runningConfig = RunningConfig()

    def rollback(self):
        """This reimplementation always returns None since Ifcfg can rollback
        on its own via restoreBackups(). This makes the general mechanism of
        API.Global._rollback redundant in this case."""
        self.configApplier.restoreBackups()
        self.configApplier = None
        if self.unifiedPersistence:
            self.runningConfig = None

    def commit(self):
        self.configApplier = None
        if self.unifiedPersistence:
            self.runningConfig.save()
            self.runningConfig = None

    def configureBridge(self, bridge, **opts):
        if not self.owned_device(bridge.name):
            IfcfgAcquire.acquire_device(bridge.name)
        self.configApplier.addBridge(bridge, **opts)
        ifdown(bridge.name)
        if bridge.port:
            bridge.port.configure(**opts)
        self._addSourceRoute(bridge)
        _ifup(bridge)

    def configureVlan(self, vlan, **opts):
        if not self.owned_device(vlan.name):
            IfcfgAcquire.acquire_device(vlan.name)
            IfcfgAcquire.acquire_vlan_device(vlan.name)
        self.configApplier.addVlan(vlan, **opts)
        vlan.device.configure(**opts)
        self._addSourceRoute(vlan)
        _ifup(vlan)

    def configureBond(self, bond, **opts):
        if not self.owned_device(bond.name):
            IfcfgAcquire.acquire_device(bond.name)
        self.configApplier.addBonding(bond, **opts)
        if not vlans.is_vlanned(bond.name):
            for slave in bond.slaves:
                ifdown(slave.name)
        for slave in bond.slaves:
            slave.configure(**opts)
        self._addSourceRoute(bond)
        _ifup(bond)
        if self.unifiedPersistence:
            self.runningConfig.setBonding(
                bond.name, {"options": bond.options, "nics": [slave.name for slave in bond.slaves], "switch": "legacy"}
            )

    def editBonding(self, bond, _netinfo):
        """
        Modifies the bond so that the bond in the system ends up with the
        same slave and options configuration that are requested. Makes a
        best effort not to interrupt connectivity.
        """
        nicsToSet = frozenset(nic.name for nic in bond.slaves)
        currentNics = frozenset(_netinfo.getNicsForBonding(bond.name))
        nicsToAdd = nicsToSet - currentNics

        if not self.owned_device(bond.name):
            IfcfgAcquire.acquire_device(bond.name)

        # Create bond configuration in case it was a non ifcfg controlled bond.
        # Needed to be before slave configuration for initscripts to add slave
        # to bond.
        bondIfcfgWritten = False
        isIfcfgControlled = os.path.isfile(NET_CONF_PREF + bond.name)
        areOptionsApplied = bond.areOptionsApplied()
        if not isIfcfgControlled or not areOptionsApplied:
            bridgeName = _netinfo.getBridgedNetworkForIface(bond.name)
            if isIfcfgControlled and bridgeName:
                bond.master = Bridge(bridgeName, self, port=bond)
            self.configApplier.addBonding(bond)
            bondIfcfgWritten = True

        for nic in currentNics - nicsToSet:
            ifdown(nic)  # So that no users will be detected for it.
            Nic(nic, self, _netinfo=_netinfo).remove()

        for slave in bond.slaves:
            if slave.name in nicsToAdd:
                ifdown(slave.name)  # nics must be down to join a bond
                self.configApplier.addNic(slave)
                _exec_ifup(slave)
            else:
                # Let NetworkManager know that we now own the slave.
                self.configApplier.addNic(slave)

        if bondIfcfgWritten:
            ifdown(bond.name)
            _restore_default_bond_options(bond.name, bond.options)
            _exec_ifup(bond)
        if self.unifiedPersistence:
            self.runningConfig.setBonding(
                bond.name, {"options": bond.options, "nics": [slave.name for slave in bond.slaves], "switch": "legacy"}
            )

    def configureNic(self, nic, **opts):
        if not self.owned_device(nic.name):
            IfcfgAcquire.acquire_device(nic.name)
        self.configApplier.addNic(nic, **opts)
        self._addSourceRoute(nic)
        if nic.bond is None:
            if not vlans.is_vlanned(nic.name):
                ifdown(nic.name)
            _ifup(nic)

    def removeBridge(self, bridge):
        if not self.owned_device(bridge.name):
            IfcfgAcquire.acquire_device(bridge.name)
        DynamicSourceRoute.addInterfaceTracking(bridge)
        ifdown(bridge.name)
        self._removeSourceRoute(bridge, StaticSourceRoute)
        commands.execCmd([constants.EXT_BRCTL, "delbr", bridge.name])
        self.configApplier.removeBridge(bridge.name)
        if bridge.port:
            bridge.port.remove()

    def removeVlan(self, vlan):
        if not self.owned_device(vlan.name):
            IfcfgAcquire.acquire_device(vlan.name)
            IfcfgAcquire.acquire_vlan_device(vlan.name)
        DynamicSourceRoute.addInterfaceTracking(vlan)
        ifdown(vlan.name)
        self._removeSourceRoute(vlan, StaticSourceRoute)
        self.configApplier.removeVlan(vlan.name)
        vlan.device.remove()

    def _ifaceDownAndCleanup(self, iface, remove_even_if_used=False):
        """Returns True iff the iface is to be removed."""
        DynamicSourceRoute.addInterfaceTracking(iface)
        to_be_removed = remove_even_if_used or not ifaceUsed(iface.name)
        if to_be_removed:
            ifdown(iface.name)
        self._removeSourceRoute(iface, StaticSourceRoute)
        return to_be_removed

    def _addSourceRoute(self, netEnt):
        """For ifcfg tracking can be done together with route/rule addition"""
        super(Ifcfg, self)._addSourceRoute(netEnt)
        DynamicSourceRoute.addInterfaceTracking(netEnt)

    def removeBond(self, bonding):
        if not self.owned_device(bonding.name):
            IfcfgAcquire.acquire_device(bonding.name)
        to_be_removed = self._ifaceDownAndCleanup(bonding)
        if to_be_removed:
            self.configApplier.removeBonding(bonding.name)
            if bonding.on_removal_just_detach_from_network:
                # Recreate the bond with ip and master info cleared
                bonding.ipv4 = address.IPv4()
                bonding.ipv6 = address.IPv6()
                bonding.master = None
                bonding.configure()
            else:
                for slave in bonding.slaves:
                    slave.remove()
                if self.unifiedPersistence:
                    self.runningConfig.removeBonding(bonding.name)
        else:
            set_mtu = self._setNewMtu(bonding, vlans.vlan_devs_for_iface(bonding.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value.
            # Note that ip link set dev bondX mtu Y sets Y on all its links
            if set_mtu is not None:
                ipwrapper.linkSet(bonding.name, ["mtu", str(set_mtu)])

            # If the bond was bridged, we must remove BRIDGE parameter from its
            # ifcfg configuration file.
            if bonding.bridge:
                self.configApplier.dropBridgeParameter(bonding.name)

    def removeNic(self, nic, remove_even_if_used=False):
        if not self.owned_device(nic.name):
            IfcfgAcquire.acquire_device(nic.name)
        to_be_removed = self._ifaceDownAndCleanup(nic, remove_even_if_used)
        if to_be_removed:
            self.configApplier.removeNic(nic.name)
            if nic.name in nics.nics():
                _exec_ifup(nic)
            else:
                logging.warning("host interface %s missing", nic.name)
        else:
            set_mtu = self._setNewMtu(nic, vlans.vlan_devs_for_iface(nic.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value
            if set_mtu is not None:
                ipwrapper.linkSet(nic.name, ["mtu", str(set_mtu)])

            # If the nic was bridged, we must remove BRIDGE parameter from its
            # ifcfg configuration file.
            if nic.bridge:
                self.configApplier.dropBridgeParameter(nic.name)

    def _getFilePath(self, fileType, device):
        return os.path.join(NET_CONF_DIR, "%s-%s" % (fileType, device))

    def _removeSourceRouteFile(self, fileType, device):
        filePath = self._getFilePath(fileType, device)
        self.configApplier._backup(filePath)
        self.configApplier._removeFile(filePath)

    def _writeConfFile(self, contents, fileType, device):
        filePath = self._getFilePath(fileType, device)

        configuration = ""
        for entry in contents:
            configuration += str(entry) + "\n"

        self.configApplier.writeConfFile(filePath, configuration)

    def configureSourceRoute(self, routes, rules, device):
        self._writeConfFile(routes, "route", device)
        self._writeConfFile(rules, "rule", device)

    def removeSourceRoute(self, routes, rules, device):
        self._removeSourceRouteFile("rule", device)
        self._removeSourceRouteFile("route", device)

    @staticmethod
    def owned_device(device):
        try:
            with open(misc.NET_CONF_PREF + device) as conf:
                content = conf.read()
        except IOError as ioe:
            if ioe.errno == errno.ENOENT:
                return False
            else:
                raise
        else:
            return content.startswith(CONFFILE_HEADER_SIGNATURE)
Example #5
0
File: ifcfg.py Project: EdDev/vdsm
class Ifcfg(Configurator):
    # TODO: Do all the configApplier interaction from here.
    def __init__(self, net_info, inRollback=False):
        is_unipersistence = config.get('vars', 'net_persistence') == 'unified'
        super(Ifcfg, self).__init__(ConfigWriter(),
                                    net_info,
                                    is_unipersistence,
                                    inRollback)
        self.runningConfig = RunningConfig()

    def rollback(self):
        """This reimplementation always returns None since Ifcfg can rollback
        on its own via restoreBackups(). This makes the general mechanism of
        API.Global._rollback redundant in this case."""
        self.configApplier.restoreBackups()
        self.configApplier = None
        self.runningConfig = None

    def commit(self):
        self.configApplier = None
        self.runningConfig.save()
        self.runningConfig = None

    def configureBridge(self, bridge, **opts):
        if not self.owned_device(bridge.name):
            IfcfgAcquire.acquire_device(bridge.name)
        self.configApplier.addBridge(bridge, **opts)
        ifdown(bridge.name)
        if bridge.port:
            bridge.port.configure(**opts)
        self._addSourceRoute(bridge)
        _ifup(bridge)

    def configureVlan(self, vlan, **opts):
        if not self.owned_device(vlan.name):
            IfcfgAcquire.acquire_device(vlan.name)
            IfcfgAcquire.acquire_vlan_device(vlan.name)
        self.configApplier.addVlan(vlan, **opts)
        vlan.device.configure(**opts)
        self._addSourceRoute(vlan)
        if isinstance(vlan.device, bond_model):
            Ifcfg._ifup_vlan_with_slave_bond_hwaddr_sync(vlan)
        else:
            _ifup(vlan)

    def configureBond(self, bond, **opts):
        if not self.owned_device(bond.name):
            IfcfgAcquire.acquire_device(bond.name)
        self.configApplier.addBonding(bond, self.net_info, **opts)
        if not vlans.is_vlanned(bond.name):
            for slave in bond.slaves:
                ifdown(slave.name)
        for slave in bond.slaves:
            slave.configure(**opts)
        self._addSourceRoute(bond)
        _ifup(bond)

        # When acquiring the device from NM, it may take a few seconds until
        # the bond is released by NM and loaded through initscripts.
        # Giving it a chance to come up before continuing.
        with waitfor.waitfor_linkup(bond.name):
            pass

        self.runningConfig.setBonding(
            bond.name, {'options': bond.options,
                        'nics': sorted(s.name for s in bond.slaves),
                        'switch': 'legacy'})

    def editBonding(self, bond, _netinfo):
        """
        Modifies the bond so that the bond in the system ends up with the
        same slave and options configuration that are requested. Makes a
        best effort not to interrupt connectivity.
        """
        nicsToSet = frozenset(nic.name for nic in bond.slaves)
        currentNics = frozenset(_netinfo.getNicsForBonding(bond.name))
        nicsToAdd = nicsToSet - currentNics

        if not self.owned_device(bond.name):
            IfcfgAcquire.acquire_device(bond.name)

        # Create bond configuration in case it was a non ifcfg controlled bond.
        # Needed to be before slave configuration for initscripts to add slave
        # to bond.
        bondIfcfgWritten = False
        isIfcfgControlled = os.path.isfile(NET_CONF_PREF + bond.name)
        areOptionsApplied = bond.areOptionsApplied()
        if not isIfcfgControlled or not areOptionsApplied:
            bridgeName = _netinfo.getBridgedNetworkForIface(bond.name)
            if isIfcfgControlled and bridgeName:
                bond.master = Bridge(bridgeName, self, port=bond)
            self.configApplier.addBonding(bond, _netinfo)
            bondIfcfgWritten = True

        for nic in currentNics - nicsToSet:
            ifdown(nic)  # So that no users will be detected for it.
            Nic(nic, self, _netinfo=_netinfo).remove()

        for slave in bond.slaves:
            if slave.name in nicsToAdd:
                ifdown(slave.name)  # nics must be down to join a bond
                self.configApplier.addNic(slave, _netinfo)
                _exec_ifup(slave)
            else:
                # Let NetworkManager know that we now own the slave.
                self.configApplier.addNic(slave, _netinfo)

        if bondIfcfgWritten:
            ifdown(bond.name)
            _restore_default_bond_options(bond.name, bond.options)
            _exec_ifup(bond)
        self.runningConfig.setBonding(
            bond.name, {'options': bond.options,
                        'nics': sorted(slave.name for slave in bond.slaves),
                        'switch': 'legacy'})

    def configureNic(self, nic, **opts):
        if not self.owned_device(nic.name):
            IfcfgAcquire.acquire_device(nic.name)
        self.configApplier.addNic(nic, self.net_info, **opts)
        self._addSourceRoute(nic)
        if nic.bond is None:
            if not vlans.is_vlanned(nic.name):
                ifdown(nic.name)
            _ifup(nic)

    def removeBridge(self, bridge):
        if not self.owned_device(bridge.name):
            IfcfgAcquire.acquire_device(bridge.name)
        if bridge.ipv4.bootproto == 'dhcp':
            ifacetracking.add(bridge.name)
        ifdown(bridge.name)
        self._removeSourceRoute(bridge)
        cmd.exec_sync([constants.EXT_BRCTL, 'delbr', bridge.name])
        self.configApplier.removeBridge(bridge.name)
        self.net_info.del_bridge(bridge.name)
        if bridge.port:
            bridge.port.remove()

    def removeVlan(self, vlan):
        if not self.owned_device(vlan.name):
            IfcfgAcquire.acquire_device(vlan.name)
            IfcfgAcquire.acquire_vlan_device(vlan.name)
        if vlan.ipv4.bootproto == 'dhcp':
            ifacetracking.add(vlan.name)
        ifdown(vlan.name)
        self._removeSourceRoute(vlan)
        self.configApplier.removeVlan(vlan.name)
        self.net_info.del_vlan(vlan.name)
        vlan.device.remove()

    def _ifaceDownAndCleanup(self, iface, remove_even_if_used=False):
        """Returns True iff the iface is to be removed."""
        if iface.ipv4.bootproto == 'dhcp':
            ifacetracking.add(iface.name)
        to_be_removed = remove_even_if_used or not self.net_info.ifaceUsers(
            iface.name)
        if to_be_removed:
            ifdown(iface.name)
        self._removeSourceRoute(iface)
        return to_be_removed

    def _addSourceRoute(self, netEnt):
        """For ifcfg tracking can be done together with route/rule addition"""
        ipv4 = netEnt.ipv4
        if ipv4.bootproto != 'dhcp' and netEnt.master is None:
            valid_args = (ipv4.address and ipv4.netmask and
                          ipv4.gateway not in (None, '0.0.0.0'))
            if valid_args:
                sroute = StaticSourceRoute(netEnt.name, ipv4.address,
                                           ipv4.netmask, ipv4.gateway)
                self.configureSourceRoute(*sroute.requested_config())

            else:
                logging.warning(
                    'Invalid input for source routing: '
                    'name=%s, addr=%s, netmask=%s, gateway=%s',
                    netEnt.name, ipv4.address, ipv4.netmask, ipv4.gateway)

        if netEnt.ipv4.bootproto == 'dhcp':
            ifacetracking.add(netEnt.name)

    def _removeSourceRoute(self, netEnt):
        if netEnt.ipv4.bootproto != 'dhcp' and netEnt.master is None:
            logging.debug("Removing source route for device %s", netEnt.name)
            sroute = StaticSourceRoute(netEnt.name, None, None, None)
            self.removeSourceRoute(*sroute.current_config())

    def removeBond(self, bonding):
        if not self.owned_device(bonding.name):
            IfcfgAcquire.acquire_device(bonding.name)
        to_be_removed = self._ifaceDownAndCleanup(bonding)
        if to_be_removed:
            self.configApplier.removeBonding(bonding.name)
            if bonding.on_removal_just_detach_from_network:
                # Recreate the bond with ip and master info cleared
                bonding.ipv4 = address.IPv4()
                bonding.ipv6 = address.IPv6()
                bonding.master = None
                bonding.configure()
            else:
                for slave in bonding.slaves:
                    slave.remove()
                self.runningConfig.removeBonding(bonding.name)
        else:
            set_mtu = self._setNewMtu(bonding,
                                      vlans.vlan_devs_for_iface(bonding.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value.
            # Note that ip link set dev bondX mtu Y sets Y on all its links
            if set_mtu is not None:
                ipwrapper.linkSet(bonding.name, ['mtu', str(set_mtu)])

            # If the bond was bridged, we must remove BRIDGE parameter from its
            # ifcfg configuration file.
            if bonding.bridge:
                self.configApplier.dropBridgeParameter(bonding.name)

    def removeNic(self, nic, remove_even_if_used=False):
        if not self.owned_device(nic.name):
            IfcfgAcquire.acquire_device(nic.name)
        to_be_removed = self._ifaceDownAndCleanup(nic, remove_even_if_used)
        if to_be_removed:
            self.configApplier.removeNic(nic.name)
            if nic.name in nics.nics():
                _exec_ifup(nic)
            else:
                logging.warning('host interface %s missing', nic.name)
        else:
            set_mtu = self._setNewMtu(nic, vlans.vlan_devs_for_iface(nic.name))
            # Since we are not taking the device up again, ifcfg will not be
            # read at this point and we need to set the live mtu value
            if set_mtu is not None:
                ipwrapper.linkSet(nic.name, ['mtu', str(set_mtu)])

            # If the nic was bridged, we must remove BRIDGE parameter from its
            # ifcfg configuration file.
            if nic.bridge:
                self.configApplier.dropBridgeParameter(nic.name)

    def _getFilePath(self, fileType, device):
        return os.path.join(NET_CONF_DIR, '%s-%s' % (fileType, device))

    def _removeSourceRouteFile(self, fileType, device):
        filePath = self._getFilePath(fileType, device)
        self.configApplier._backup(filePath)
        self.configApplier._removeFile(filePath)

    def _writeConfFile(self, contents, fileType, device):
        filePath = self._getFilePath(fileType, device)

        configuration = ''
        for entry in contents:
            configuration += str(entry) + '\n'

        self.configApplier.writeConfFile(filePath, configuration)

    def configureSourceRoute(self, routes, rules, device):
        self._writeConfFile(routes, 'route', device)
        self._writeConfFile(rules, 'rule', device)

    def removeSourceRoute(self, routes, rules, device):
        self._removeSourceRouteFile('rule', device)
        self._removeSourceRouteFile('route', device)

    @staticmethod
    def owned_device(device):
        try:
            with open(misc.NET_CONF_PREF + device) as conf:
                content = conf.read()
        except IOError as ioe:
            if ioe.errno == errno.ENOENT:
                return False
            else:
                raise
        else:
            return content.startswith(CONFFILE_HEADER_SIGNATURE)

    @staticmethod
    def _ifup_vlan_with_slave_bond_hwaddr_sync(vlan):
        """
        When NM is active and the network includes a vlan on top of a bond, the
        following scenario may occur:
        - VLAN over a bond with slaves is already defined in the system and
          VDSM is about to acquire it to define on it a network.
        - The VLAN iface is re-created while the bond slaves are temporary
          detached, causing the vlan to be created with the bond temporary
          mac address, which is different from the original existing address.
        Therefore, following the VLAN ifup command, its mac address is compared
        with the first bond slave. In case they differ, the vlan device is
        recreated.
        """
        bond = vlan.device
        if not bond.slaves:
            _ifup(vlan)
            return

        for attempt in range(5):
            _ifup(vlan)
            if (link_iface.mac_address(bond.slaves[0].name) ==
                    link_iface.mac_address(vlan.name)):
                return

            logging.info('Config vlan@bond: hwaddr not in sync (%s)', attempt)
            ifdown(vlan.name)

        raise ConfigNetworkError(
            ERR_BAD_BONDING,
            'While adding vlan {} over bond {}, '
            'the bond hwaddr was not in sync '
            'whith its slaves.'.format(vlan.name, vlan.device.name))