Esempio n. 1
0
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="<ip>"
                        netmask="<ip>"
                        gateway="<ip>"
                        bootproto="..."
                        delay="..."
                        onboot="yes"|"no"
                        (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:
                        force=0|1
                        connectivityCheck=0|1
                        connectivityTimeout=<int>
                        explicitBonding=0|1


    Notes:
        Bondings are removed when they change state from 'used' to 'unused'.

        By default, if 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 attachement
        in the network's attributes). Similarly, if you edit a bonding, it's not
        necessary to specify its networks.
        However, if you specify the 'explicitBonding' option as true, the function
        will expect you to specify all networks that are attached to a specified
        bonding, and vice-versa, the bonding attached to a specified network.

    """
    logger = logging.getLogger("setupNetworks")

    try:
        _netinfo = NetInfo()
        configWriter = ConfigWriter()
        networksAdded = []
        #bondingNetworks = {}   # Reminder TODO

        logger.info("Setting up network")
        logger.debug("Setting up network according to configuration: networks:%r, bondings:%r, options:%r" % (networks, bondings, options))

        force = options.get('force', False)
        if not utils.tobool(force):
            logging.debug("Validating configuration")
            _validateNetworkSetup(dict(networks), dict(bondings), explicitBonding=options.get('explicitBonding', False))

        logger.debug("Applying...")
        try:
            delnetworks = {}
            for network, networkAttrs in networks.iteritems():
                if 'remove' in networkAttrs:
                    delnetworks[network] = networkAttrs

            for network, networkAttrs in delnetworks.iteritems():
                if networkAttrs.pop('remove', False):
                    assert not networkAttrs

                    logger.debug('Removing network %r'%network)
                    delNetwork(network, force=force)
                    del networks[network]

            for network, networkAttrs in networks.items():
                if network in _netinfo.networks:
                    delNetwork(network, force=force)
                else:
                    networksAdded.append(network)
                d = dict(networkAttrs)
                if 'bonding' in d:
                    d['nics'] = bondings[d['bonding']]['nics']
                    d['bondingOptions'] = bondings[d['bonding']].get('options', None)
                else:
                    d['nics'] = [d.pop('nic')]
                d['force'] = force

                logger.debug('Adding network %r'%network)
                addNetwork(network, **d)

        except:
            configWriter.restoreAtomicBackup()
            raise
        if utils.tobool(options.get('connectivityCheck', True)):
            logger.debug('Checking connectivity...')
            if not clientSeen(int(options.get('connectivityTimeout', CONNECTIVITY_TIMEOUT_DEFAULT))):
                logger.info('Connectivity check failed, rolling back')
                for bridge in networksAdded:
                    delNetwork(bridge, force=True)
                configWriter.restoreAtomicBackup()
                raise ConfigNetworkError(ne.ERR_LOST_CONNECTION, 'connectivity check failed')

    except Exception, e:
        # SuperVdsm eats the error, so let's print it ourselves
        logger.error(e, exc_info=True)
        raise
Esempio n. 2
0
def _validateNetworkSetup(networks={}, bondings={}, explicitBonding=False):
    _netinfo = NetInfo()

    # Step 1: Initial validation (validate names, existence of params, etc.)
    for network, networkAttrs in networks.iteritems():
        validateBridgeName(network)

        if networkAttrs.get('remove', False):
            if set(networkAttrs) - set(['remove']):
                raise ConfigNetworkError(ne.ERR_BAD_PARAMS, "Cannot specify any attribute when removing")
            if network not in _netinfo.networks:
                raise ConfigNetworkError(ne.ERR_BAD_BRIDGE, 'Cannot remove bridge %s: Doesn\'t exist' % network)
            continue

        vlan = networkAttrs.get('vlan', None)
        ipaddr = networkAttrs.get('ipaddr', None)
        netmask = networkAttrs.get('netmask', None)
        gateway = networkAttrs.get('gateway', None)
        if vlan:
            validateVlanId(vlan)

        # Check ip, netmask, gateway
        if ipaddr:
            if not netmask:
                raise ConfigNetworkError(ne.ERR_BAD_ADDR, "Must specify netmask to configure ip for bridge")
            validateIpAddress(ipaddr)
            validateNetmask(netmask)
            if gateway:
                validateGateway(gateway)
        else:
            if netmask or gateway:
                raise ConfigNetworkError(ne.ERR_BAD_ADDR, "Specified netmask or gateway but not ip")

        # check nic or bonding
        nic = networkAttrs.get('nic', None)
        bonding = networkAttrs.get('bonding', None)

        if nic and bonding:
            raise ConfigNetworkError(ne.ERR_BAD_PARAMS, "Don't specify both nic and bonding")
        if not nic and not bonding:
            raise ConfigNetworkError(ne.ERR_BAD_PARAMS, "Must specify either nic or bonding")

        if nic and nic not in _netinfo.nics:
            raise ConfigNetworkError(ne.ERR_BAD_NIC, "unknown nic: %r"%nic)

    for bonding, bondingAttrs in bondings.iteritems():
        validateBondingName(bonding)
        if 'options' in bondingAttrs:
            validateBondingOptions(bonding, bondingAttrs['options'])

        if bondingAttrs.get('remove', False):
            if bonding not in _netinfo.bondings:
                raise ConfigNetworkError(ne.ERR_BAD_BONDING, 'Cannot remove bonding %s: Doesn\'t exist' % bonding)
            continue

        nics = bondingAttrs.get('nics', None)
        if not nics:
            raise ConfigNetworkError(ne.ERR_BAD_PARAMS, "Must specify nics for bonding")
        if not set(nics).issubset(set(_netinfo.nics)):
            raise ConfigNetworkError(ne.ERR_BAD_NIC, "Unknown nics in: %r"%list(nics))


    # Step 2: Make sure we have complete information about the Setup, more validation
    # (if explicitBonding==False we complete the missing information ourselves, else we raise an exception)
    nics = defaultdict(lambda: {'networks':[], 'bonding':None})
    for network, networkAttrs in networks.iteritems():
        if networkAttrs.get('remove', False):
            continue

        if 'bonding' in networkAttrs:
            assert 'nic' not in networkAttrs

            bonding = networkAttrs['bonding']
            if bonding not in bondings:
                if explicitBonding:
                    raise ConfigNetworkError(ne.ERR_BAD_PARAMS, "Network %s requires unspecified bonding %s"%(
                                             network, bonding))

                # fill in bonding info
                bondings[bonding] =  {'nics':_netinfo.bondings[bonding]['slaves']}

            if '_networks' not in bondings[bonding]:
                bondings[bonding]['_networks'] = []
            bondings[bonding]['_networks'].append( network )
        else:
            assert 'nic' in networkAttrs

            nics[networkAttrs['nic']]['networks'].append( network )

    for bonding, bondingAttrs in bondings.iteritems():
        if bondingAttrs.get('remove', False):
            continue
        connectedNetworks = _netinfo.getNetworksForNic(bonding)

        for network in connectedNetworks:
            if network not in networks:
                if explicitBonding:
                    raise ConfigNetworkError(ne.ERR_BAD_PARAMS, "Bonding %s is associated with unspecified network %s"%(
                                             bonding, network))
                # fill in network info
                _, vlan, bonding2 = _netinfo.getNicsVlanAndBondingForNetwork(network)
                assert bonding == bonding2
                networks[network] = {'bonding': bonding, 'vlan':vlan}

        for nic in bondingAttrs['nics']:
            if nics[nic]['bonding']:
                raise ConfigNetworkError(ne.ERR_BAD_BONDING, "Nic %s is attached to two different bondings in setup: %s, %s"%(
                                         nic, bonding, nics[nic]['bonding']))
            nics[nic]['bonding'] = bonding

    # At this point the state may be contradictory.

    # Step 3: Apply removals (We're not iterating because we change the dictionary size)
    queue = []
    for network, networkAttrs in networks.items():
        if networkAttrs.get('remove', False):
            del networks[network]
        else:
            queue.append(('network', network, networkAttrs))
    for bonding, bondingAttrs in bondings.items():
        if bondingAttrs.get('remove', False):
            del bondings[bonding]
        else:
            queue.append(('bonding', bonding, bondingAttrs))

    # Step 4: Verify Setup
    for nic, nicAttrs in nics.iteritems():
        networks = nicAttrs['networks']
        if networks and nicAttrs['bonding']:
            raise ConfigNetworkError(ne.ERR_USED_NIC, "Setup attached both network and bonding to nic %s"%(nic))
        if len(networks) > 1:
            for network, networkAttrs in networks.iteritems():
                if not networkAttrs.get('vlan', None):
                    raise ConfigNetworkError(ne.ERR_USED_NIC,
                            "Setup attached more than one network to nic %s, some of which aren't vlans"%(nic))

    for bonding, bondingAttrs in bondings.iteritems():
        networks = bondingAttrs['_networks']
        if len(networks) > 1:
            for network, networkAttrs in networks.iteritems():
                if not networkAttrs.get('vlan', None):
                    raise ConfigNetworkError(ne.ERR_BAD_BONDING,
                            "Setup attached more than one network to bonding %s, some of which aren't vlans"%(bonding))