def _clean_running_config_from_removed_nets(self): # Cleanup running config from networks that have been actually # removed but not yet removed from running config. running_config = RunningConfig() nets2remove = (six.viewkeys(running_config.networks) - six.viewkeys(self.runningConfig.networks)) for net in nets2remove: running_config.removeNetwork(net) running_config.save()
class PyrouteTwo(Iproute2): def __init__(self, inRollback=False): self.unifiedPersistence = True super(Iproute2, self).__init__(ConfigApplier(), inRollback) self.runningConfig = RunningConfig() def commit(self): self.configApplier.releaseSocket() self.configApplier = None self.runningConfig.save() self.runningConfig = None
def _bond_hwaddr_should_be_enforced(bondname): """ Bond MAC address is to be enforced under these conditions: - Bond device exists already. - One of these conditions exists (OR): - Unowned by VDSM (not in running config). - Owned by VDSM and HWADDR is specified in the config. """ bond_dev = bond.Bond(bondname) if bond_dev.exists(): running_bonds = RunningConfig().bonds bondattr = running_bonds.get(bondname) return not bondattr or bondattr.get('hwaddr') return False
def _upgrade_volatile_running_config(rconfig): """ Relocate the volatile version of running config (if exists) to the persisted version. This procedure is required in order to support upgrades to the new persisted version of running config. """ if not rconfig.config_exists(): volatile_rconfig = RunningConfig(volatile=True) if volatile_rconfig.config_exists(): rconfig.networks = volatile_rconfig.networks rconfig.bonds = volatile_rconfig.bonds rconfig.save() volatile_rconfig.delete()
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 __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 _copy_persistent_over_running_config(): pconfig = PersistentConfig() rconfig = RunningConfig() rconfig.delete() rconfig.networks = pconfig.networks rconfig.bonds = pconfig.bonds rconfig.save()
def upgrade(): rconfig = RunningConfig() pconfig = PersistentConfig() libvirt_networks = libvirtnetwork.networks() _upgrade_volatile_running_config(rconfig) if rconfig.config_exists() or pconfig.config_exists(): _upgrade_unified_configuration(rconfig) _upgrade_unified_configuration(pconfig) else: # In case unified config has not existed before, it is assumed that # the networks existance have been persisted in libvirt db. vdsmnets = libvirt_vdsm_nets(libvirt_networks) _create_unified_configuration(rconfig, NetInfo(netinfo(vdsmnets))) _cleanup_libvirt_networks(libvirt_networks)
def _create_unified_configuration(rconfig, net_info): """ Given netinfo report, generate unified configuration and persist it. Networks and Bonds detected by the network caps/netinfo are recorded. In case there are external bonds (not owned), they are still counted for in this regard. Note: Unified persistence mode is not activated in this context, it is left as a separate step/action. """ kconfig = KernelConfig(net_info) rconfig.networks = kconfig.networks rconfig.bonds = _filter_owned_bonds(kconfig.bonds) rconfig.save() ConfigWriter.clearBackups() RunningConfig.store()
def _update_running_config(networks, bonds): """ Recreate RunningConfig so that following setSafeNetworkConfig will persist a valid configuration. """ running_config = RunningConfig() for net, net_attr in six.viewitems(networks): running_config.setNetwork(net, net_attr) for bonding, bonding_attr in six.viewitems(bonds): running_config.setBonding(bonding, bonding_attr) running_config.save()
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) 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: address.flush(bonding.name) 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: address.flush(nic.name) 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)
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 = ConfigWriter(self.unifiedPersistence) if self.unifiedPersistence and self.runningConfig is None: self.runningConfig = RunningConfig()
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)
def start(self): requestQueues = config.get('addresses', 'request_queues') requestQueue = requestQueues.split(",")[0] self.vdscli = jsonrpcvdscli.connect(requestQueue, xml_compat=False) self.netinfo = self._get_netinfo() self.config = RunningConfig()
def begin(self): if self.configApplier is None: self.configApplier = ConfigApplier() if self.runningConfig is None: self.runningConfig = RunningConfig()
def _persist(networks, bondings): runningConfig = RunningConfig() runningConfig.delete() for network, attributes in networks.iteritems(): runningConfig.setNetwork(network, attributes) for bond, attributes in bondings.iteritems(): runningConfig.setBonding(bond, attributes) runningConfig.save() runningConfig.store()
def __init__(self, nets, bonds, net_info): self._nets = nets self._bonds = bonds self._net_info = NetInfo(net_info) self._desired_config = self._create_desired_config() self._running_config = RunningConfig()
def _analyze_netinfo_bonds(self, netinfo): rconfig = RunningConfig() for bond, bond_attr in six.viewitems(netinfo.bondings): bond_rconf = rconfig.bonds.get(bond) yield bond, _translate_netinfo_bond(bond_attr, bond_rconf)
def net2vlan(network_name): # Using RunningConfig avoids the need to require root access. net_attr = RunningConfig().networks.get(network_name) return net_attr.get('vlan') if net_attr else None
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))
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): self.normalize_device_filename(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): self.normalize_device_filename(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): self.normalize_device_filename(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): self.normalize_device_filename(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): self.normalize_device_filename(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): self.normalize_device_filename(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): self.normalize_device_filename(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): self.normalize_device_filename(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): self.normalize_device_filename(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 normalize_device_filename(device): """ Attempts to detect a device ifcfg file and rename it to a vdsm supported format. In case of multiple ifcfg files that treat the same device, all except the first are deleted. """ device_files = [] paths = glob.iglob(NET_CONF_PREF + '*') for ifcfg_file in paths: with open(ifcfg_file) as f: for line in f: if line.startswith('#'): continue key, value = line.rstrip().split('=', 1) if value and value[0] == '\"' and value[-1] == '\"': value = value[1:-1] if key.upper() == 'DEVICE': if value == device: device_files.append(ifcfg_file) break if device_files: os.rename(device_files[0], NET_CONF_PREF + device) for filepath in device_files[1:]: utils.rmFile(filepath)
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) if link_iface.iface(bridge.name).exists(): 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 link_vlan.is_base_device(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 bond_attr = { 'options': bond.options, 'nics': sorted(s.name for s in bond.slaves), 'switch': 'legacy' } if bond.hwaddr: bond_attr['hwaddr'] = bond.hwaddr self.runningConfig.setBonding(bond.name, bond_attr) 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 owned_bond = self.owned_device(bond.name) if not owned_bond: 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 or not owned_bond: 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) bond_attr = { 'options': bond.options, 'nics': sorted(s.name for s in bond.slaves), 'switch': 'legacy' } if bond.hwaddr: bond_attr['hwaddr'] = bond.hwaddr self.runningConfig.setBonding(bond.name, bond_attr) 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 link_vlan.is_base_device(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) _remove_device(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) if vlan.master: _remove_device(vlan.name) else: 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: vlans = link_vlan.get_vlans_on_base_device(bonding.name) set_mtu = self._setNewMtu(bonding, vlans) # 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) bond_used_by_net = self.net_info.getNetworkForIface(bonding.name) bond_info = self.net_info.bondings[bonding.name] if (not bond_used_by_net and (bond_info['ipv4addrs'] or bond_info['ipv6addrs'])): ipwrapper.addrFlush(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: vlans = link_vlan.get_vlans_on_base_device(nic.name) set_mtu = self._setNewMtu(nic, vlans) # 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 mode 5 & 6 is excluded from the mac sync check. """ bond = vlan.device bond_mode = Ifcfg._get_bond_mode(bond) if not bond.slaves or bond_mode == '5' or bond_mode == '6': _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) vlan_hwaddr = link_iface.iface(vlan.name).address() slaves_hwaddr = [ link_iface.iface(slave.name).address() for slave in bond.slaves ] if slaves_hwaddr[0] == vlan_hwaddr: return bond_hwaddr = link_iface.iface(bond.name).address() logging.info( '%s. vlan@bond hwaddr is not in sync (v/b/[s]): %s/%s/%s', attempt, vlan_hwaddr, bond_hwaddr, slaves_hwaddr) 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)) @staticmethod def _get_bond_mode(bond): for option in bond.options.split(): key, value = option.split('=', 1) if key == 'mode': return numerize_bond_mode(value) return '0'
def _net_with_default_route_from_config(): for net, attrs in six.iteritems(RunningConfig().networks): if attrs.get('defaultRoute', False): return net, attrs return None
def call_and_update(self, *args, **kwargs): ret = func(self, *args, **kwargs) self.netinfo = self._get_netinfo() if self.config is not None: self.config = RunningConfig() return ret
def refresh_running_config(self): self.running_config = RunningConfig()
def persist(): ConfigWriter.clearBackups() RunningConfig.store()
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 = IPv4() bonding.ipv6 = 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)
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 persist(): RunningConfig.store()
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)