def _handleBondings(bondings, configurator, in_rollback): """ Add/Edit/Remove bond interface """ logger = logging.getLogger("_handleBondings") _netinfo = CachingNetInfo() edition = [] addition = [] for name, attrs in bondings.items(): if 'remove' in attrs: if name not in _netinfo.bondings: if in_rollback: logger.error( 'Cannot remove bonding %s during rollback: ' 'does not exist', name) continue else: raise ConfigNetworkError( ne.ERR_BAD_BONDING, "Cannot remove bonding %s: does not exist" % name) bond_users = _netinfo.ifaceUsers(name) # networks removal takes place before bondings handling, therefore # all assigned networks (bond_users) should be already removed if bond_users: raise ConfigNetworkError( ne.ERR_USED_BOND, "Cannot remove bonding %s: used by another interfaces %s" % (name, bond_users)) bond = Bond.objectivize(name, configurator, attrs.get('options'), attrs.get('nics'), mtu=None, _netinfo=_netinfo, destroyOnMasterRemoval='remove' in attrs) bond.remove() _netinfo.del_bonding(name) elif name in _netinfo.bondings: edition.append((name, attrs)) else: addition.append((name, attrs)) for name, attrs in edition: bond = Bond.objectivize(name, configurator, attrs.get('options'), attrs.get('nics'), mtu=None, _netinfo=_netinfo, destroyOnMasterRemoval='remove' in attrs) if len(bond.slaves) == 0: raise ConfigNetworkError(ne.ERR_BAD_PARAMS, 'Missing required nics' ' for bonding device.') logger.debug("Editing bond %r with options %s", bond, bond.options) configurator.editBonding(bond, _netinfo) for name, attrs in addition: bond = Bond.objectivize(name, configurator, attrs.get('options'), attrs.get('nics'), mtu=None, _netinfo=_netinfo, destroyOnMasterRemoval='remove' in attrs) if len(bond.slaves) == 0: raise ConfigNetworkError(ne.ERR_BAD_PARAMS, 'Missing required nics' ' for bonding device.') logger.debug("Creating bond %r with options %s", bond, bond.options) configurator.configureBond(bond)
def __init__(self, name, configurator, ipv4=None, ipv6=None, blockingdhcp=False, mtu=None, _netinfo=None): if _netinfo is None: _netinfo = CachingNetInfo() if name not in _netinfo.nics: raise ConfigNetworkError(ne.ERR_BAD_NIC, 'unknown nic: %s' % name) if _netinfo.ifaceUsers(name): mtu = max(mtu, mtus.getMtu(name)) super(Nic, self).__init__(name, configurator, ipv4, ipv6, blockingdhcp, mtu)
def testIterNetworkHierarchy(self): _netinfo = { 'networks': {}, 'vlans': {}, 'nics': ['testnic1', 'testnic2'], 'bondings': {}, 'bridges': {} } fakeInfo = CachingNetInfo(_netinfo) # Vlanned and bonded VM network nic1 = Nic('testnic1', configurator=None, _netinfo=fakeInfo) nic2 = Nic('testnic2', configurator=None, _netinfo=fakeInfo) bond1 = Bond('bond42', configurator=None, slaves=(nic1, nic2)) vlan1 = Vlan(bond1, 4, configurator=None) bridge1 = Bridge('testbridge', configurator=None, port=vlan1) self.assertEqual([dev for dev in bridge1], [bridge1, vlan1, bond1, nic1, nic2]) self.assertEqual(bond1, hierarchy_backing_device(bridge1)) self.assertEqual(4, hierarchy_vlan_tag(bridge1)) # Nic-less VM net bridge2 = Bridge('testbridge', configurator=None, port=None) self.assertEqual([dev for dev in bridge2], [bridge2]) self.assertEqual(None, hierarchy_backing_device(bridge2)) self.assertEqual(None, hierarchy_vlan_tag(bridge2)) # vlan-less VM net bridge3 = Bridge('testbridge', configurator=None, port=bond1) self.assertEqual([dev for dev in bridge3], [bridge3, bond1, nic1, nic2]) self.assertEqual(bond1, hierarchy_backing_device(bridge3)) self.assertEqual(None, hierarchy_vlan_tag(bridge3)) # Bond-less VM net bridge4 = Bridge('testbridge', configurator=None, port=nic1) self.assertEqual([dev for dev in bridge4], [bridge4, nic1]) self.assertEqual(nic1, hierarchy_backing_device(bridge4)) self.assertEqual(None, hierarchy_vlan_tag(bridge4)) # vlanned and bonded non-VM net self.assertEqual([dev for dev in vlan1], [vlan1, bond1, nic1, nic2]) self.assertEqual(bond1, hierarchy_backing_device(vlan1)) self.assertEqual(4, hierarchy_vlan_tag(vlan1)) # vlanned, bond-less non-VM net vlan2 = Vlan(nic1, 5, configurator=None) self.assertEqual([dev for dev in vlan2], [vlan2, nic1]) self.assertEqual(nic1, hierarchy_backing_device(vlan2)) self.assertEqual(5, hierarchy_vlan_tag(vlan2)) # non-vlanned and bonded non-VM net self.assertEqual([dev for dev in bond1], [bond1, nic1, nic2]) self.assertEqual(bond1, hierarchy_backing_device(bond1)) self.assertEqual(None, hierarchy_vlan_tag(bond1)) # non-vlanned and bond-less non-VM net self.assertEqual([dev for dev in nic2], [nic2]) self.assertEqual(nic2, hierarchy_backing_device(nic2)) self.assertEqual(None, hierarchy_vlan_tag(nic2))
def _delBrokenNetwork(network, netAttr, configurator): '''Adapts the network information of broken networks so that they can be deleted via _delNetwork.''' _netinfo = CachingNetInfo() _netinfo.networks[network] = netAttr _netinfo.networks[network]['dhcpv4'] = False if _netinfo.networks[network]['bridged']: try: nets = configurator.runningConfig.networks except AttributeError: nets = {} # ifcfg does not need net definitions _netinfo.networks[network]['ports'] = persistence.configuredPorts( nets, network) elif not os.path.exists('/sys/class/net/' + netAttr['iface']): # Bridgeless broken network without underlying device libvirt.removeNetwork(network) configurator.runningConfig.removeNetwork(network) return _delNetwork(network, configurator=configurator, bypassValidation=True, implicitBonding=False, _netinfo=_netinfo)
def _add_missing_networks(configurator, networks, bondings, logger, _netinfo=None): # We need to use the newest host info if _netinfo is None: _netinfo = CachingNetInfo() else: _netinfo.updateDevices() for network, attrs in networks.iteritems(): if 'remove' in attrs: continue d = dict(attrs) if 'bonding' in d: d.update(_buildBondOptions(d['bonding'], bondings, _netinfo)) else: d['nics'] = [d.pop('nic')] if 'nic' in d else [] logger.debug("Adding network %r", network) try: _addNetwork(network, configurator=configurator, implicitBonding=True, _netinfo=_netinfo, **d) except ConfigNetworkError as cne: if cne.errCode == ne.ERR_FAILED_IFUP: logger.debug("Adding network %r failed. Running " "orphan-devices cleanup", network) _emergencyNetworkCleanup(network, attrs, configurator) raise _netinfo.updateDevices() # Things like a bond mtu can change
def showNetwork(network): _netinfo = CachingNetInfo() if network not in _netinfo.networks: print("Network %r doesn't exist" % network) return bridged = _netinfo.networks[network]['bridged'] print("Network %s(Bridged: %s):" % (network, bridged)) nics, vlan, vlan_id, bonding = _netinfo.getNicsVlanAndBondingForNetwork( network) if bridged: ipaddr = _netinfo.networks[network]['addr'] netmask = _netinfo.networks[network]['netmask'] gateway = _netinfo.networks[network]['gateway'] print("ipaddr=%s, netmask=%s, gateway=%s" % (ipaddr, netmask, gateway)) else: iface = _netinfo.networks[network]['iface'] ipaddr = _netinfo.nics[iface]['addr'] netmask = _netinfo.nics[iface]['netmask'] print("ipaddr=%s, netmask=%s" % (ipaddr, netmask)) print("vlan=%s, bonding=%s, nics=%s" % (vlan_id, bonding, nics))
def testTextualRepr(self): _netinfo = { 'networks': {}, 'vlans': {}, 'nics': ['testnic1', 'testnic2'], 'bondings': {}, 'bridges': {} } fakeInfo = CachingNetInfo(_netinfo) nic1 = Nic('testnic1', None, _netinfo=fakeInfo) nic2 = Nic('testnic2', None, _netinfo=fakeInfo) bond1 = Bond('bond42', None, slaves=(nic1, nic2)) vlan1 = Vlan(bond1, '4', None) bridge1 = Bridge('testbridge', None, port=vlan1) self.assertEqual( '%r' % bridge1, 'Bridge(testbridge: Vlan(bond42.4: ' 'Bond(bond42: (Nic(testnic1), Nic(testnic2)))))')
def _handle_setup(nets, bonds, running_config, nets_by_nic): commands = [] netinfo = CachingNetInfo() for bond, attrs in six.iteritems(bonds): if 'remove' not in attrs: _validate_bond_configuration(attrs, netinfo) if bond in running_config.bonds: commands.extend(_edit_ovs_bond(bond, attrs, running_config)) else: commands.extend(_setup_ovs_bond(bond, attrs, running_config)) for net, attrs in six.iteritems(nets): if 'remove' not in attrs: _validate_net_configuration(net, attrs, running_config, netinfo) if net in running_config.networks: commands.extend( _edit_ovs_net(net, attrs, running_config, nets_by_nic)) else: commands.extend( _setup_ovs_net(net, attrs, running_config, nets_by_nic)) return commands
def _emergencyNetworkCleanup(network, networkAttrs, configurator): """Remove all leftovers after failed setupNetwork""" _netinfo = CachingNetInfo() topNetDev = None if 'bonding' in networkAttrs: if networkAttrs['bonding'] in _netinfo.bondings: topNetDev = Bond.objectivize(networkAttrs['bonding'], configurator, None, None, None, _netinfo, True) elif 'nic' in networkAttrs: if networkAttrs['nic'] in _netinfo.nics: topNetDev = Nic(networkAttrs['nic'], configurator, _netinfo=_netinfo) if 'vlan' in networkAttrs and topNetDev: vlan_name = '%s.%s' % (topNetDev.name, networkAttrs['vlan']) if vlan_name in _netinfo.vlans: topNetDev = Vlan(topNetDev, networkAttrs['vlan'], configurator) if networkAttrs.get('bridged', True): if network in _netinfo.bridges: topNetDev = Bridge(network, configurator, port=topNetDev) if topNetDev: topNetDev.remove()
def _objectivizeNetwork(bridge=None, vlan=None, vlan_id=None, bonding=None, bondingOptions=None, nics=None, mtu=None, ipaddr=None, netmask=None, gateway=None, bootproto=None, ipv6addr=None, ipv6gateway=None, ipv6autoconf=None, dhcpv6=None, defaultRoute=None, _netinfo=None, configurator=None, blockingdhcp=None, implicitBonding=None, opts=None): """ Constructs an object hierarchy that describes the network configuration that is passed in the parameters. :param bridge: name of the bridge. :param vlan: vlan device name. :param vlan_id: vlan tag id. :param bonding: name of the bond. :param bondingOptions: bonding options separated by spaces. :param nics: list of nic names. :param mtu: the desired network maximum transmission unit. :param ipaddr: IPv4 address in dotted decimal format. :param netmask: IPv4 mask in dotted decimal format. :param gateway: IPv4 address in dotted decimal format. :param bootproto: protocol for getting IP config for the net, e.g., 'dhcp' :param ipv6addr: IPv6 address in format address[/prefixlen]. :param ipv6gateway: IPv6 address in format address[/prefixlen]. :param ipv6autoconf: whether to use IPv6's stateless autoconfiguration. :param dhcpv6: whether to use DHCPv6. :param _netinfo: network information snapshot. :param configurator: instance to use to apply the network configuration. :param blockingdhcp: whether to acquire dhcp IP config in a synced manner. :param implicitBonding: whether the bond's existance is tied to it's master's. :param defaultRoute: Should this network's gateway be set in the main routing table? :param opts: misc options received by the callee, e.g., {'stp': True}. this function can modify the dictionary. :returns: the top object of the hierarchy. """ if configurator is None: configurator = ConfiguratorClass() if _netinfo is None: _netinfo = CachingNetInfo() if opts is None: opts = {} if bootproto == 'none': bootproto = None if bondingOptions and not bonding: raise ConfigNetworkError(ne.ERR_BAD_BONDING, 'Bonding options ' 'specified without bonding') topNetDev = None if bonding: topNetDev = Bond.objectivize(bonding, configurator, bondingOptions, nics, mtu, _netinfo, implicitBonding) elif nics: try: nic, = nics except ValueError: raise ConfigNetworkError(ne.ERR_BAD_BONDING, 'Multiple nics ' 'require a bonding device') else: bond = _netinfo.getBondingForNic(nic) if bond: raise ConfigNetworkError(ne.ERR_USED_NIC, 'nic %s already ' 'enslaved to %s' % (nic, bond)) topNetDev = Nic(nic, configurator, mtu=mtu, _netinfo=_netinfo) if vlan is not None: tag = _netinfo.vlans[vlan]['vlanid'] if vlan_id is None else vlan_id topNetDev = Vlan(topNetDev, tag, configurator, mtu=mtu, name=vlan) elif vlan_id is not None: topNetDev = Vlan(topNetDev, vlan_id, configurator, mtu=mtu) if bridge is not None: stp = None if 'stp' in opts: stp = opts.pop('stp') elif 'STP' in opts: stp = opts.pop('STP') try: stp = bridges.stp_booleanize(stp) except ValueError: raise ConfigNetworkError(ne.ERR_BAD_PARAMS, '"%s" is not a valid ' 'bridge STP value.' % stp) topNetDev = Bridge(bridge, configurator, port=topNetDev, mtu=mtu, stp=stp) # inherit DUID from the port's existing DHCP lease (BZ#1219429) if topNetDev.port and bootproto == 'dhcp': _inherit_dhcp_unique_identifier(topNetDev, _netinfo) if topNetDev is None: raise ConfigNetworkError(ne.ERR_BAD_PARAMS, 'Network defined without ' 'devices.') topNetDev.ipv4 = IPv4(ipaddr, netmask, gateway, defaultRoute, bootproto) topNetDev.ipv6 = IPv6(ipv6addr, ipv6gateway, defaultRoute, ipv6autoconf, dhcpv6) topNetDev.blockingdhcp = (configurator._inRollback or utils.tobool(blockingdhcp)) return topNetDev
def setupNetworks(networks, bondings, **options): """Add/Edit/Remove configuration for networks and bondings. Params: networks - dict of key=network, value=attributes where 'attributes' is a dict with the following optional items: vlan=<id> bonding="<name>" | nic="<name>" (bonding and nics are mutually exclusive) ipaddr="<ipv4>" netmask="<ipv4>" gateway="<ipv4>" bootproto="..." ipv6addr="<ipv6>[/<prefixlen>]" ipv6gateway="<ipv6>" ipv6autoconf="0|1" dhcpv6="0|1" defaultRoute=True|False (other options will be passed to the config file AS-IS) -- OR -- remove=True (other attributes can't be specified) bondings - dict of key=bonding, value=attributes where 'attributes' is a dict with the following optional items: nics=["<nic1>" , "<nic2>", ...] options="<bonding-options>" -- OR -- remove=True (other attributes can't be specified) options - dict of options, such as: connectivityCheck=0|1 connectivityTimeout=<int> _inRollback=True|False Notes: When you edit a network that is attached to a bonding, it's not necessary to re-specify the bonding (you need only to note the attachment in the network's attributes). Similarly, if you edit a bonding, it's not necessary to specify its networks. """ logger = logging.getLogger("setupNetworks") logger.debug("Setting up network according to configuration: " "networks:%r, bondings:%r, options:%r" % (networks, bondings, options)) _canonize_networks(networks) # TODO: Add _canonize_bondings(bondings) logging.debug("Validating configuration") _validateNetworkSetup(networks, bondings) bondings, networks, options = _apply_hook(bondings, networks, options) libvirt_nets = netinfo_networks() _netinfo = CachingNetInfo(_netinfo=netinfo_get( libvirtNets2vdsm(libvirt_nets))) connectivity_check_networks = set() logger.debug("Applying...") in_rollback = options.get('_inRollback', False) kernel_config = kernelconfig.KernelConfig(_netinfo) normalized_config = kernelconfig.normalize( netconfpersistence.BaseConfig(networks, bondings)) with ConfiguratorClass(in_rollback) as configurator: # Remove edited networks and networks with 'remove' attribute for network, attrs in networks.items(): if network in _netinfo.networks: logger.debug("Removing network %r", network) keep_bridge = _should_keep_bridge( network_attrs=normalized_config.networks[network], currently_bridged=_netinfo.networks[network]['bridged'], net_kernel_config=kernel_config.networks[network] ) _delNetwork(network, configurator=configurator, implicitBonding=False, _netinfo=_netinfo, keep_bridge=keep_bridge) _netinfo.del_network(network) _netinfo.updateDevices() elif network in libvirt_nets: # If the network was not in _netinfo but is in the networks # returned by libvirt, it means that we are dealing with # a broken network. logger.debug('Removing broken network %r', network) _delBrokenNetwork(network, libvirt_nets[network], configurator=configurator) _netinfo.updateDevices() elif 'remove' in attrs: raise ConfigNetworkError(ne.ERR_BAD_BRIDGE, "Cannot delete " "network %r: It doesn't exist in the " "system" % network) else: connectivity_check_networks.add(network) _handleBondings(bondings, configurator, in_rollback) _add_missing_networks(configurator, networks, bondings, logger, _netinfo) _check_connectivity(connectivity_check_networks, networks, bondings, options, logger) hooks.after_network_setup(_buildSetupHookDict(networks, bondings, options))
def _delNetwork(network, vlan=None, bonding=None, nics=None, bypassValidation=False, configurator=None, implicitBonding=True, _netinfo=None, keep_bridge=False, **options): if _netinfo is None: _netinfo = CachingNetInfo() if configurator is None: configurator = ConfiguratorClass() if network not in _netinfo.networks: logging.info("Network %r: doesn't exist in libvirt database", network) vlan = _vlanToInternalRepresentation(vlan) _delNonVdsmNetwork(network, vlan, bonding, nics, _netinfo, configurator) return nics, vlan, vlan_id, bonding = _netinfo.getNicsVlanAndBondingForNetwork( network) bridged = _netinfo.networks[network]['bridged'] logging.info("Removing network %s with vlan=%s, bonding=%s, nics=%s," "keep_bridge=%s options=%s", network, vlan, bonding, nics, keep_bridge, options) if not bypassValidation: _validateDelNetwork(network, vlan, bonding, nics, bridged and not keep_bridge, _netinfo) net_ent = _objectivizeNetwork(bridge=network if bridged else None, vlan=vlan, vlan_id=vlan_id, bonding=bonding, nics=nics, _netinfo=_netinfo, configurator=configurator, implicitBonding=implicitBonding) net_ent.ipv4.bootproto = ( 'dhcp' if _netinfo.networks[network]['dhcpv4'] else 'none') if bridged and keep_bridge: # we now leave the bridge intact but delete everything underneath it net_ent_to_remove = net_ent.port if net_ent_to_remove is not None: # the configurator will not allow us to remove a bridge interface # (be it vlan, bond or nic) unless it is not used anymore. Since # we are interested to leave the bridge here, we have to disconnect # it from the device so that the configurator will allow its # removal. _disconnect_bridge_port(net_ent_to_remove.name) else: net_ent_to_remove = net_ent # We must first remove the libvirt network and then the network entity. # Otherwise if we first remove the network entity while the libvirt # network is still up, the network entity (In some flows) thinks that # it still has users and thus does not allow its removal configurator.removeLibvirtNetwork(network) if net_ent_to_remove is not None: logging.info('Removing network entity %s', net_ent_to_remove) net_ent_to_remove.remove() # We must remove the QoS last so that no devices nor networks mark the # QoS as used backing_device = hierarchy_backing_device(net_ent) if (backing_device is not None and os.path.exists(NET_PATH + '/' + backing_device.name)): configurator.removeQoS(net_ent)
def listNetworks(): _netinfo = CachingNetInfo() print("Networks:", _netinfo.networks.keys()) print("Vlans:", _netinfo.vlans.keys()) print("Nics:", _netinfo.nics.keys()) print("Bondings:", _netinfo.bondings.keys())
def _addNetwork(network, vlan=None, bonding=None, nics=None, ipaddr=None, netmask=None, prefix=None, mtu=None, gateway=None, dhcpv6=None, ipv6addr=None, ipv6gateway=None, ipv6autoconf=None, configurator=None, bondingOptions=None, bridged=True, _netinfo=None, hostQos=None, defaultRoute=None, blockingdhcp=False, **options): nics = nics or () if _netinfo is None: _netinfo = CachingNetInfo() bridged = utils.tobool(bridged) if dhcpv6 is not None: dhcpv6 = utils.tobool(dhcpv6) if ipv6autoconf is not None: ipv6autoconf = utils.tobool(ipv6autoconf) vlan = _vlanToInternalRepresentation(vlan) if network == '': raise ConfigNetworkError(ne.ERR_BAD_BRIDGE, 'Empty network names are not valid') if prefix: if netmask: raise ConfigNetworkError(ne.ERR_BAD_PARAMS, 'Both PREFIX and NETMASK supplied') else: try: netmask = addresses.prefix2netmask(int(prefix)) except ValueError as ve: raise ConfigNetworkError(ne.ERR_BAD_ADDR, "Bad prefix: %s" % ve) logging.debug('validating network...') if network in _netinfo.networks: raise ConfigNetworkError( ne.ERR_USED_BRIDGE, 'Network already exists (%s)' % (network,)) if bonding: _validateInterNetworkCompatibility(_netinfo, vlan, bonding) else: for nic in nics: _validateInterNetworkCompatibility(_netinfo, vlan, nic) # defaultRoute is set either explicitly by the client, OR if we're adding # the management network. # REQUIRED_FOR: clusterLevel<=3.3 # remove reference to constants.LEGACY_MANAGEMENT_NETWORKS if defaultRoute is None: defaultRoute = network in constants.LEGACY_MANAGEMENT_NETWORKS logging.info("Adding network %s with vlan=%s, bonding=%s, nics=%s," " bondingOptions=%s, mtu=%s, bridged=%s, defaultRoute=%s," "options=%s", network, vlan, bonding, nics, bondingOptions, mtu, bridged, defaultRoute, options) if configurator is None: configurator = ConfiguratorClass() bootproto = options.pop('bootproto', None) net_ent = _objectivizeNetwork( bridge=network if bridged else None, vlan_id=vlan, bonding=bonding, bondingOptions=bondingOptions, nics=nics, mtu=mtu, ipaddr=ipaddr, netmask=netmask, gateway=gateway, bootproto=bootproto, dhcpv6=dhcpv6, blockingdhcp=blockingdhcp, ipv6addr=ipv6addr, ipv6gateway=ipv6gateway, ipv6autoconf=ipv6autoconf, defaultRoute=defaultRoute, _netinfo=_netinfo, configurator=configurator, opts=options) if bridged and network in _netinfo.bridges: # The bridge already exists, update the configured entity to one level # below and update the mtu of the bridge. # The mtu is updated in the bridge configuration and on all the tap # devices attached to it (for the VMs). # (expecting the bridge running mtu to be updated by the kernel when # the device attached under it has its mtu updated) logging.info("Bridge %s already exists.", network) net_ent_to_configure = net_ent.port _update_mtu_for_an_existing_bridge(network, configurator, mtu) else: net_ent_to_configure = net_ent if net_ent_to_configure is not None: logging.info("Configuring device %s", net_ent_to_configure) net_ent_to_configure.configure(**options) configurator.configureLibvirtNetwork(network, net_ent) if hostQos is not None: configurator.configureQoS(hostQos, net_ent)