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)
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)
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))
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)
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))