def _get_network_info(northbound, bridge, southbound, ports, stp, addresses, routes): # OVS networks do not use the OVS bonds, they use external linux bonds. bond = Bond(southbound) if bond.exists(): bond_name = bond.master nics = list(bond.slaves) else: bond_name = '' nics = [southbound] tag = ports[northbound]['tag'] network_info = { 'iface': northbound, 'bridged': True, 'bond': bond_name, 'nics': nics, 'ports': _get_net_ports(bridge, northbound, southbound, tag, ports), 'stp': stp, 'switch': 'ovs' } if tag is not None: # TODO: We should always report vlan, even if it is None. Netinfo # should be canonicalized before passed to caps, so None will not be # exposed in API call result. network_info['vlanid'] = tag network_info.update(_get_iface_info(northbound, addresses, routes)) return network_info
def test_bond_create_failure_on_slave_add(self, bond_with_slaves): bond_name = random_iface_name('bond_', max_length=11) with pytest.raises(IOError): with Bond(bond_name) as broken_bond: broken_bond.create() broken_bond.add_slaves(bond_with_slaves.slaves) assert not Bond(bond_name).exists()
def info(link): bond = Bond(link.name) return { 'hwaddr': link.address, 'slaves': list(bond.slaves), 'active_slave': bond.active_slave(), 'opts': bond.options }
def bond_device(prefix="bond_", max_length=11): bond_name = random_iface_name(prefix, max_length) bond = Bond(bond_name) bond.create() try: yield bond finally: bond.destroy()
def test_bond_create_failure_on_slave_add(self): with dummy_devices(2) as (nic1, nic2): with bond_device() as base_bond: base_bond.add_slaves((nic1, nic2)) bond_name = random_iface_name('bond_', max_length=11) with self.assertRaises(IOError): with Bond(bond_name) as broken_bond: broken_bond.create() broken_bond.add_slaves((nic1, nic2)) self.assertFalse(Bond(bond_name).exists())
def test_add_net_on_existing_external_bond_preserving_mac( self, adapter, switch, nic0, nic1): if switch == 'ovs': pytest.xfail('Preserving bond mac is not supported on OVS switch.') HWADDRESS = 'ce:0c:46:59:c9:d1' with Bond(BOND_NAME, slaves=(nic0, nic1)) as bond: bond.create() iface(BOND_NAME).set_address(HWADDRESS) NETBASE = { NETWORK1_NAME: { 'bonding': BOND_NAME, 'bridged': False, 'switch': switch, } } with adapter.setupNetworks(NETBASE, {}, NOCHK): adapter.assertNetwork(NETWORK1_NAME, NETBASE[NETWORK1_NAME]) adapter.assertBond( BOND_NAME, { 'nics': [nic0, nic1], 'hwaddr': HWADDRESS, 'switch': switch, }, ) adapter.setupNetworks({}, {BOND_NAME: {'remove': True}}, NOCHK)
def test_bond_set_options(self, bond_with_slaves): OPTIONS = {'mode': '1', 'miimon': '300'} bond_with_slaves.set_options(OPTIONS) bond_with_slaves.up() bond = Bond(bond_with_slaves.master) assert bond.options == OPTIONS
def validate_network_setup(nets, bonds, net_info): kernel_nics = nics() kernel_bonds = Bond.bonds() for net, attrs in six.iteritems(nets): validate_net_configuration( net, attrs, bonds, kernel_bonds, kernel_nics) for bond, attrs in six.iteritems(bonds): validate_bond_configuration( bond, attrs, nets, net_info['networks'], kernel_nics)
def validate_network_setup(nets, bonds, net_info): kernel_nics = nics() kernel_bonds = Bond.bonds() for net, attrs in six.iteritems(nets): validate_net_configuration(net, attrs, bonds, kernel_bonds, kernel_nics) for bond, attrs in six.iteritems(bonds): validate_bond_configuration(bond, attrs, nets, net_info['networks'], kernel_nics)
def validate_network_setup(nets, bonds): ovs_networks = info.create_netinfo(info.OvsInfo())['networks'] kernel_nics = nics() kernel_bonds = Bond.bonds() for net, attrs in six.iteritems(nets): validator.validate_net_configuration(net, attrs, bonds, kernel_bonds, kernel_nics) for bond, attrs in six.iteritems(bonds): validator.validate_bond_configuration(bond, attrs, nets, ovs_networks, kernel_nics)
def permanent_address(): paddr = {} for b in Bond.bonds(): with open('/proc/net/bonding/' + b) as f: for line in f: if line.startswith('Slave Interface: '): slave = line[len('Slave Interface: '):-1] elif line.startswith('Permanent HW addr: ') and slave: paddr[slave] = line[len('Permanent HW addr: '):-1] return paddr
def test_bond_edit_options(self): OPTIONS_A = {"mode": "1", "miimon": "300"} OPTIONS_B = {"mode": "2"} OPTIONS_C = {"mode": "2", "miimon": "150"} with dummy_devices(2) as (nic1, nic2): with bond_device() as bond: bond.set_options(OPTIONS_A) bond.add_slaves((nic1, nic2)) _bond = Bond(bond.master) self.assertEqual(_bond.options, OPTIONS_A) bond.set_options(OPTIONS_B) _bond.refresh() self.assertEqual(_bond.options, OPTIONS_B) bond.set_options(OPTIONS_C) _bond.refresh() self.assertEqual(_bond.options, OPTIONS_C)
def test_bond_exists(self): with dummy_devices(2) as (nic1, nic2): with bond_device() as _bond: _bond.add_slaves((nic1, nic2)) _bond.up() bond = Bond(_bond.master) self.assertEqual(bond.slaves, set((nic1, nic2))) # TODO: Support options self.assertEqual(bond.options, None)
def validate_network_setup(nets, bonds): ovs_networks = info.create_netinfo(info.OvsInfo())['networks'] kernel_nics = nics() kernel_bonds = Bond.bonds() for net, attrs in six.iteritems(nets): validator.validate_net_configuration( net, attrs, bonds, kernel_bonds, kernel_nics) for bond, attrs in six.iteritems(bonds): validator.validate_bond_configuration( bond, attrs, nets, ovs_networks, kernel_nics)
def test_bond_exists(self): OPTIONS = {'mode': '1', 'miimon': '300'} with dummy_devices(2) as (nic1, nic2): with bond_device() as _bond: _bond.set_options(OPTIONS) _bond.add_slaves((nic1, nic2)) _bond.up() bond = Bond(_bond.master) self.assertEqual(bond.slaves, set((nic1, nic2))) self.assertEqual(bond.options, OPTIONS)
def test_bond_edit_options(self): OPTIONS_A = {'mode': '1', 'miimon': '300'} OPTIONS_B = {'mode': '2'} OPTIONS_C = {'mode': 'balance-rr', 'miimon': '150'} with dummy_devices(2) as (nic1, nic2): with bond_device() as bond: bond.set_options(OPTIONS_A) bond.add_slaves((nic1, nic2)) _bond = Bond(bond.master) self.assertEqual(_bond.options, OPTIONS_A) bond.set_options(OPTIONS_B) _bond.refresh() self.assertEqual(_bond.options, OPTIONS_B) bond.set_options(OPTIONS_C) _bond.refresh() OPTIONS_C['mode'] = '0' self.assertEqual(_bond.options, OPTIONS_C)
def _restore_default_bond_options(bond_name, desired_options): """ Restore the bond's options to defaults corresponding to the intended bonding mode. The logic of handling the defaults is embedded in the link.bond set options therefore, we use set options as a whole. This works around an initscripts design limitation: ifup only touches declared options and leaves other (possibly non-default) options as-is. """ Bond(bond_name).set_options(parse_bond_options(desired_options))
def test_bond_edit_failure_on_slave_add(self, bond0, bond1, nics): base_bond, edit_bond = bond0, bond1 base_bond.add_slaves((nics[0], )) edit_bond.add_slaves((nics[1], )) with pytest.raises(IOError): with Bond(edit_bond.master) as broken_bond: assert broken_bond.exists() broken_bond.add_slaves((nics[0], )) assert edit_bond.exists() assert edit_bond.slaves == {nics[1]}
def test_bond_edit_failure_on_slave_add(self): with dummy_devices(2) as (nic1, nic2): with bond_device() as base_bond, bond_device() as edit_bond: base_bond.add_slaves((nic1,)) edit_bond.add_slaves((nic2,)) with self.assertRaises(IOError): with Bond(edit_bond.master) as broken_bond: self.assertTrue(broken_bond.exists()) broken_bond.add_slaves((nic1,)) self.assertTrue(edit_bond.exists()) self.assertEqual(set((nic2,)), edit_bond.slaves)
def test_restore_bond(self, adapter, nic0, nic1): BONDCREATE = {BOND_NAME: {'nics': [nic0, nic1], 'switch': 'ovs'}} with adapter.reset_persistent_config(): with adapter.setupNetworks({}, BONDCREATE, NOCHK): adapter.setSafeNetworkConfig() Bond(BOND_NAME).destroy() netrestore.init_nets() adapter.update_netinfo() adapter.assertBond(BOND_NAME, BONDCREATE[BOND_NAME])
def bond_device(prefix='bond_', max_length=11): bond_name = random_iface_name(prefix, max_length) bond = Bond(bond_name) bond.create() try: yield bond finally: bond.destroy()
def init_nets(): persistence = config.get('vars', 'net_persistence') if persistence != 'unified': logging.info('Skipping: Unified persistence is not used.') return if _nets_already_restored(NETS_RESTORED_MARK): logging.info('Skipping: Networks were already restored.') return logging.info('Starting initial network setup.') persistent_config = PersistentConfig() nets = _persisted_ovs_entries(persistent_config.networks) logging.info('Restoring networks configuration: {}'.format(nets)) _set_blocking_dhcp(nets) bonds = _persisted_ovs_entries(persistent_config.bonds) logging.info('Restoring bondings configuration: {}'.format(bonds)) for net, attrs in six.iteritems(nets): with _try2execute('IPv6autoconf for {} failed.'.format(net)): netswitch.configurator.setup_ipv6autoconf({net: attrs}) for bond_name, attrs in six.iteritems(bonds): with _try2execute('Restoration of bond {} failed.'.format(bond_name)): requested_slaves = set(attrs['nics']) requested_options = ( setup.parse_bond_options(attrs['options']) if 'options' in attrs else None ) with Bond( bond_name, slaves=requested_slaves, options=requested_options ) as bond: bond.create() for bond, attrs in six.iteritems(bonds): with _try2execute('Setting links up for {} failed.'.format(bond)): netswitch.configurator.set_ovs_links_up({}, {bond: attrs}, {}) for net, attrs in six.iteritems(nets): with _try2execute('Setting links up for {} failed.'.format(net)): netswitch.configurator.set_ovs_links_up({net: attrs}, {}, {}) for net, attrs in six.iteritems(nets): with _try2execute('IP config for {} failed.'.format(net)): netswitch.configurator.setup_ovs_ip_config({net: attrs}, {}) logging.info('Initial network setup is done.')
def _split_switch_type_entries(entries, running_entries): legacy_entries = {} ovs_entries = {} def store_broken_entry(name, attrs): """ If a network/bond should be removed but its existing entry was not found in running config, we have to find out what switch type has to be used for removal on our own. All we do now is, that we pass orphan entry to legacy swich which is (unlike OVS switch) able to remove broken networks/bonds. TODO: Try to find out which switch type should be used for broken network/bonding removal. """ legacy_entries[name] = attrs def store_entry(name, attrs, switch_type): if switch_type is None: store_broken_entry(name, attrs) elif switch_type == legacy_switch.SWITCH_TYPE: legacy_entries[name] = attrs elif switch_type == ovs_switch.SWITCH_TYPE: ovs_entries[name] = attrs else: raise ne.ConfigNetworkError( ne.ERR_BAD_PARAMS, 'Invalid switch type %s' % attrs['switch']) for name, attrs in six.iteritems(entries): if 'remove' in attrs: running_attrs = running_entries.get(name, {}) switch_type = running_attrs.get('switch') # When removing a network/bond, we try to determine its switch # type from the netinfo report. # This is not always possible, specifically with bonds owned by ovs # but not successfully deployed (not saved in running config). if (switch_type == legacy_switch.SWITCH_TYPE and Bond(name).exists() and not legacy_switch.ConfiguratorClass.owned_device(name)): # If not owned by Legacy, assume OVS and let it be removed in # the OVS way. switch_type = ovs_switch.SWITCH_TYPE else: switch_type = attrs['switch'] store_entry(name, attrs, switch_type) return legacy_entries, ovs_entries
def test_restore_bond(self): with dummy_devices(2) as (nic1, nic2): BONDCREATE = {BOND_NAME: {'nics': [nic1, nic2], 'switch': 'ovs'}} with self.reset_persistent_config(): with self.setupNetworks({}, BONDCREATE, NOCHK): self.vdsm_proxy.setSafeNetworkConfig() Bond(BOND_NAME).destroy() netrestore.init_nets() self.update_netinfo() self.assertBond(BOND_NAME, BONDCREATE[BOND_NAME])
def test_add_net_on_existing_external_vlanned_bond(self): ADDRESS1 = '192.168.99.1' ADDRESS2 = '192.168.99.254' PREFIX = '29' with dummy_devices(2) as (nic1, nic2): with Bond(BOND_NAME, slaves=(nic1, nic2)) as bond: bond.create() bond.up() with vlan_device(bond.master) as vlan: # Make slaves dirty intentionally and check if they recover self._set_ip_address('1.1.1.1/29', nic1) self._set_ip_address('1.1.1.2/29', nic2) self._set_ip_address(ADDRESS1 + '/' + PREFIX, bond.master) self._set_ip_address(ADDRESS2 + '/' + PREFIX, vlan.devName) NETBASE = { NETWORK1_NAME: { 'bonding': BOND_NAME, 'bridged': True, 'ipaddr': ADDRESS1, 'prefix': PREFIX, 'switch': 'legacy', } } with adapter.setupNetworks(NETBASE, {}, NOCHK): adapter.assertNetwork(NETWORK1_NAME, NETBASE[NETWORK1_NAME]) adapter.assertBond( BOND_NAME, { 'nics': [nic1, nic2], 'switch': 'legacy' }, ) nic1_info = adapter.netinfo.nics[nic1] nic2_info = adapter.netinfo.nics[nic2] vlan_info = adapter.netinfo.vlans[vlan.devName] assert nic1_info['ipv4addrs'] == [] assert nic2_info['ipv4addrs'] == [] assert vlan_info['ipv4addrs'] == [ ADDRESS2 + '/' + PREFIX ] adapter.setupNetworks({}, {BOND_NAME: {'remove': True}}, NOCHK)
def test_add_vlan_network_on_existing_external_bond_with_used_slave(self): with dummy_devices(2) as (nic1, nic2): with Bond(BOND_NAME, slaves=(nic1, nic2)) as bond: bond.create() bond.up() with vlan_device(nic1): NETBASE = {NETWORK1_NAME: {'bonding': BOND_NAME, 'bridged': True, 'switch': 'legacy', 'vlan': 17}} with pytest.raises(SetupNetworksError) as err: with adapter.setupNetworks(NETBASE, {}, NOCHK): pass assert err.value.status == ne.ERR_USED_NIC assert 'already used by' in err.value.msg bond.destroy()
def test_bond_edit_options(self, bond_with_slaves): OPTIONS_A = {'mode': '1', 'miimon': '300'} OPTIONS_B = {'mode': '2'} OPTIONS_C = {'mode': 'balance-rr', 'miimon': '150'} bond_with_slaves.set_options(OPTIONS_A) bond = Bond(bond_with_slaves.master) assert bond.options == OPTIONS_A bond_with_slaves.set_options(OPTIONS_B) bond.refresh() assert bond.options == OPTIONS_B bond_with_slaves.set_options(OPTIONS_C) bond.refresh() OPTIONS_C['mode'] = '0' assert bond.options == OPTIONS_C
def validate_network_setup(nets, bonds, net_info): kernel_nics = nics() kernel_bonds = Bond.bonds() current_nets = net_info['networks'] for net, attrs in six.iteritems(nets): validate_net_configuration( net, attrs, bonds, kernel_bonds, kernel_nics, current_nets, RunningConfig().networks, ) for bond, attrs in six.iteritems(bonds): validate_bond_configuration(bond, attrs, nets, current_nets, kernel_nics) validate_nic_usage( nets, bonds, _get_kernel_nets_nics(current_nets, kernel_bonds, nets), _get_kernel_bonds_slaves(kernel_bonds, bonds), )
def test_bond_edit_options(self): OPTIONS_A = {'mode': '1', 'miimon': '300'} OPTIONS_B = {'mode': '2'} OPTIONS_C = {'mode': '2', 'miimon': '150'} with dummy_devices(2) as (nic1, nic2): with bond_device() as bond: bond.set_options(OPTIONS_A) bond.add_slaves((nic1, nic2)) _bond = Bond(bond.master) self.assertEqual(_bond.options, OPTIONS_A) bond.set_options(OPTIONS_B) _bond.refresh() self.assertEqual(_bond.options, OPTIONS_B) bond.set_options(OPTIONS_C) _bond.refresh() self.assertEqual(_bond.options, OPTIONS_C)
def test_bond_list(self): with bond_device() as b1, bond_device() as b2, bond_device() as b3: actual_bond_set = set(Bond.bonds()) expected_bond_set = set([b1.master, b2.master, b3.master]) self.assertLessEqual(expected_bond_set, actual_bond_set)
def test_bond_list(self, bond0, bond1, bond2): actual_bond_set = set(Bond.bonds()) expected_bond_set = {b.master for b in (bond0, bond1, bond2)} assert expected_bond_set <= actual_bond_set
def info(link): bond = Bond(link.name) return {'hwaddr': link.address, 'slaves': list(bond.slaves), 'active_slave': bond.active_slave(), 'opts': bond.options}
def _net_nics(attrs): if 'bonding' in attrs: return Bond(attrs['bonding']).slaves else: return [attrs.pop('nic')] if 'nic' in attrs else ()
def _bond_opts_without_mode(bond_name): opts = Bond(bond_name).options opts.pop('mode') return opts
def _get_kernel_bonds_slaves(): kernel_bonds_slaves = set() for bond_name in Bond.bonds(): kernel_bonds_slaves |= Bond(bond_name).slaves return kernel_bonds_slaves
def setBondingMtu(self, bonding, newmtu): self.setIfaceMtu(bonding, newmtu) slaves = Bond(bonding).slaves for slave in slaves: self.setIfaceMtu(slave, newmtu)