Example #1
0
File: api.py Project: fancyKai/vdsm
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)
Example #2
0
    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)
Example #3
0
    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))
Example #4
0
    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)
Example #5
0
File: api.py Project: fancyKai/vdsm
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)
Example #6
0
File: api.py Project: fancyKai/vdsm
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
Example #7
0
File: api.py Project: fancyKai/vdsm
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))
Example #8
0
 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
Example #10
0
File: api.py Project: fancyKai/vdsm
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()
Example #11
0
File: api.py Project: fancyKai/vdsm
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
Example #12
0
File: api.py Project: fancyKai/vdsm
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))
Example #13
0
File: api.py Project: fancyKai/vdsm
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)
Example #14
0
File: api.py Project: fancyKai/vdsm
def listNetworks():
    _netinfo = CachingNetInfo()
    print("Networks:", _netinfo.networks.keys())
    print("Vlans:", _netinfo.vlans.keys())
    print("Nics:", _netinfo.nics.keys())
    print("Bondings:", _netinfo.bondings.keys())
Example #15
0
File: api.py Project: fancyKai/vdsm
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)