[node-patches] Change in ovirt-node[master]: [DRAFT] network: Add bonding support
fabiand at fedoraproject.org
fabiand at fedoraproject.org
Fri Jun 14 10:35:51 UTC 2013
Fabian Deutsch has uploaded a new change for review.
Change subject: [DRAFT] network: Add bonding support
......................................................................
[DRAFT] network: Add bonding support
This patch adds support for nic bonding.
OVIRT_BOND_NAME, OVIRT_BOND_SLAVES and OVIRT_BOND_OPTIONS are new
keywords which can be used to create a bond device.
Bug-Url: https://bugzilla.redhat.com/show_bug.cgi?id=831318
Change-Id: I9ec23904942649baa031a61f194ee782b63019b0
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M src/ovirt/node/config/defaults.py
M src/ovirt/node/config/network.py
M tests/nose/network_config.py
3 files changed, 206 insertions(+), 20 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/14/15714/1
diff --git a/src/ovirt/node/config/defaults.py b/src/ovirt/node/config/defaults.py
index bdc0dd6..4283c6d 100644
--- a/src/ovirt/node/config/defaults.py
+++ b/src/ovirt/node/config/defaults.py
@@ -310,7 +310,12 @@
def commit(self):
m = Network().retrieve()
aug = AugeasWrapper()
- topology = NetworkLayout().retrieve()["topology"]
+
+ bond = NicBonding().retrieve()
+ if bond["slaves"]:
+ NicBonding().transaction().commit()
+
+ topology = NetworkLayout().retrieve()["layout"]
if topology == "bridged":
self.__write_bridged_config()
else:
@@ -373,6 +378,8 @@
slave_cfg.vlan = "yes" if m["vlanid"] else None
slave_cfg.onboot = "yes"
+ self.__assign_bond_master(slave_cfg)
+
# save()includes persisting
bridge_cfg.save()
slave_cfg.save()
@@ -389,7 +396,17 @@
nic_cfg.device = nic_ifname
nic_cfg.hwaddr = NIC(m["iface"]).hwaddr
+ self.__assign_bond_master(nic_cfg)
+
nic_cfg.save()
+
+ def __assign_bond_master(self, cfg):
+ m = Network().retrieve()
+ m_bond = NicBonding().retrieve()
+ if m_bond["options"]:
+ if m["iface"] != m_bond["name"]:
+ raise RuntimeError("Bond configured but not used")
+ cfg.bonding_opts = m_bond["options"]
class PersistMacNicMapping(utils.Transaction.Element):
title = "Persist MAC-NIC Mappings"
@@ -420,6 +437,63 @@
return tx
+class NicBonding(NodeConfigFileSection):
+ """Create a bonding device
+ - OVIRT_BOND
+
+ >>> from ovirt.node.utils import fs
+ >>> n = NicBonding(fs.FakeFs.File("dst"))
+ >>> n.update("bond0", ["ens1", "ens2", "ens3"], "mode=4")
+ >>> data = sorted(n.retrieve().items())
+ >>> data[:2]
+ [('name', 'bond0'), ('options', 'mode=4')]
+ >>> data [2:]
+ [('slaves', ['ens1', 'ens2', 'ens3'])]
+ """
+ keys = ("OVIRT_BOND_NAME",
+ "OVIRT_BOND_SLAVES",
+ "OVIRT_BOND_OPTIONS")
+
+ @NodeConfigFileSection.map_and_update_defaults_decorator
+ def update(self, name, slaves, options):
+ if not name.startswith("bond"):
+ raise RuntimeError("Bond ifname must start with 'bond'")
+ assert type(slaves) is list
+ return {"OVIRT_BOND_SLAVES": ",".join(slaves) if slaves else None}
+
+ def retrieve(self):
+ cfg = super(NicBonding, self).retrieve()
+ cfg.update({"slaves": (cfg["slaves"].split(",") if cfg["slaves"]
+ else None)})
+ return cfg
+
+ def configure_8023ad(self, name, slaves):
+ return self.update(name, slaves, "mode=4")
+
+ def transaction(self):
+ bond = NicBonding().retrieve()
+
+ class WriteSlaveConfigs(utils.Transaction.Element):
+ title = "Writing bond slaves configuration"
+
+ def commit(self):
+ m = Network().retrieve()
+ if m["iface"] in bond["slaves"]:
+ raise RuntimeError("Bond slave can not be used as " +
+ "primary device")
+
+ for slave in bond["slaves"]:
+ slave_cfg = NicConfig(slave)
+ slave_cfg.device = slave
+ slave_cfg.slave = "yes"
+ slave_cfg.master = bond["name"]
+ slave_cfg.onboot = "yes"
+ slave_cfg.save()
+
+ tx = utils.Transaction("Writing bond configuration")
+ tx.append(WriteSlaveConfigs())
+ return tx
+
class NetworkLayout(NodeConfigFileSection):
"""Sets the network topology
- OVIRT_NETWORK_TOPOLOGY
@@ -428,19 +502,19 @@
>>> n = NetworkLayout(fs.FakeFs.File("dst"))
>>> n.update("bridged")
>>> sorted(n.retrieve().items())
- [('topology', 'bridged')]
+ [('layout', 'bridged')]
"""
keys = ("OVIRT_NETWORK_LAYOUT",)
- known_topologies = ["bridged",
- # bridged way, a bridge is created for BOOTIF
+ known_layouts = ["bridged",
+ # bridged way, a bridge is created for BOOTIF
- "direct"
- # The BOOTIF NIC is configured directly
- ]
+ "direct"
+ # The BOOTIF NIC is configured directly
+ ]
@NodeConfigFileSection.map_and_update_defaults_decorator
- def update(self, topology):
- assert topology in self.known_topologies
+ def update(self, layout):
+ assert layout in self.known_layouts
def configure_bridged(self):
return self.update("bridged")
diff --git a/src/ovirt/node/config/network.py b/src/ovirt/node/config/network.py
index dc1c4a1..b694955 100644
--- a/src/ovirt/node/config/network.py
+++ b/src/ovirt/node/config/network.py
@@ -60,6 +60,10 @@
peerntp = None
peerdns = None
+ master = None
+ slave = None
+ bonding_opts = None
+
vlan_parent = None
_backend = None
@@ -67,7 +71,8 @@
"gateway", "vlan", "device", "onboot", "hwaddr",
"ipv6init", "ipv6forwarding", "ipv6_autoconf",
"dhcpv6c", "ipv6addr", "ipv6_defaultgw", "delay",
- "peerntp", "peerdns"]
+ "peerntp", "peerdns",
+ "master", "slave", "bonding_opts"]
def __init__(self, ifname):
super(NicConfig, self).__init__()
diff --git a/tests/nose/network_config.py b/tests/nose/network_config.py
index faba753..eeecd29 100644
--- a/tests/nose/network_config.py
+++ b/tests/nose/network_config.py
@@ -36,8 +36,6 @@
FakeFs.erase()
def test_basic(self):
- """Ensure that FakeFs is working
- """
FakeFs.erase()
with patch("ovirt.node.utils.fs.File", FakeFs.File):
f = fs.File("new-file")
@@ -64,8 +62,6 @@
FakeFs.erase()
def test_dhcp(self, *args, **kwargs):
- """Test BridgedNIC with DHCP configuration file creation
- """
m = defaults.Network()
mt = defaults.NetworkLayout()
@@ -84,8 +80,6 @@
('PEERNTP', 'yes'), ('TYPE', 'Bridge')])
def test_static(self, *args, **kwargs):
- """Test BridgedNIC with static IP configuration file creation
- """
m = defaults.Network()
mt = defaults.NetworkLayout()
@@ -128,10 +122,9 @@
"""Test bridgeless with DHCP configuration file creation
"""
mt = defaults.NetworkLayout()
- mt.configure_direct()
-
m = defaults.Network()
+ mt.configure_direct()
m.configure_dhcp("eth0")
run_tx_by_name(m.transaction(), "WriteConfiguration")
@@ -147,10 +140,9 @@
"""Test bridgeless with static IP configuration file creation
"""
mt = defaults.NetworkLayout()
- mt.configure_direct()
-
m = defaults.Network()
+ mt.configure_direct()
m.configure_static("ens1", "192.168.122.42", "255.255.255.0",
"192.168.122.1", None)
@@ -168,6 +160,121 @@
assert "brens1" not in FakeFs.filemap
+ at patch("ovirt.node.utils.fs.File", FakeFs.File)
+ at patch.object(UdevNICInfo, "vendor")
+ at patch.object(UdevNICInfo, "devtype")
+ at patch.object(SysfsNICInfo, "hwaddr", "th:em:ac:ad:dr")
+class TestBond():
+ """Test bonding configuration
+ """
+ def setUp(self):
+ FakeFs.erase()
+ FakeFs.File("/etc/default/ovirt").touch()
+
+ def tearDown(self):
+ FakeFs.erase()
+
+ def test_direct_dhcp(self, *args, **kwargs):
+ mb = defaults.NicBonding()
+ mt = defaults.NetworkLayout()
+ m = defaults.Network()
+
+ mb.configure_8023ad("bond0", ["ens1", "ens2", "ens3"])
+ m.configure_dhcp("bond0")
+ mt.configure_direct()
+
+ run_tx_by_name(m.transaction(), "WriteConfiguration")
+
+ assert_ifcfg_has_items("bond0",
+ [('BONDING_OPTS', 'mode=4'),
+ ('BOOTPROTO', 'dhcp'),
+ ('DEVICE', 'bond0'),
+ ('HWADDR', 'th:em:ac:ad:dr'),
+ ('ONBOOT', 'yes'), ('PEERNTP', 'yes')])
+
+ assert_ifcfg_has_items("ens1",
+ [('DEVICE', 'ens1'), ('MASTER', 'bond0'),
+ ('ONBOOT', 'yes'), ('SLAVE', 'yes')])
+
+ assert_ifcfg_has_items("ens2",
+ [('DEVICE', 'ens2'), ('MASTER', 'bond0'),
+ ('ONBOOT', 'yes'), ('SLAVE', 'yes')])
+
+ assert_ifcfg_has_items("ens3",
+ [('DEVICE', 'ens3'), ('MASTER', 'bond0'),
+ ('ONBOOT', 'yes'), ('SLAVE', 'yes')])
+
+ assert len(FakeFs.filemap) == (1 + 1 + 3)
+
+ def test_bridged_dhcp(self, *args, **kwargs):
+ mb = defaults.NicBonding()
+ mt = defaults.NetworkLayout()
+ m = defaults.Network()
+
+ mb.configure_8023ad("bond0", ["ens1", "ens2", "ens3"])
+ m.configure_dhcp("bond0")
+ mt.configure_bridged()
+
+ run_tx_by_name(m.transaction(), "WriteConfiguration")
+
+ assert_ifcfg_has_items("bond0",
+ [('BONDING_OPTS', 'mode=4'),
+ ('BRIDGE', 'brbond0'),
+ ('DEVICE', 'bond0'),
+ ('HWADDR', 'th:em:ac:ad:dr'),
+ ('ONBOOT', 'yes')])
+
+ assert_ifcfg_has_items("ens1",
+ [('DEVICE', 'ens1'), ('MASTER', 'bond0'),
+ ('ONBOOT', 'yes'), ('SLAVE', 'yes')])
+
+ assert_ifcfg_has_items("ens2",
+ [('DEVICE', 'ens2'), ('MASTER', 'bond0'),
+ ('ONBOOT', 'yes'), ('SLAVE', 'yes')])
+
+ assert_ifcfg_has_items("ens3",
+ [('DEVICE', 'ens3'), ('MASTER', 'bond0'),
+ ('ONBOOT', 'yes'), ('SLAVE', 'yes')])
+
+ assert_ifcfg_has_items("brbond0",
+ [('BOOTPROTO', 'dhcp'),
+ ('DELAY', '0'),
+ ('DEVICE', 'brbond0'),
+ ('ONBOOT', 'yes'),
+ ('PEERNTP', 'yes'),
+ ('TYPE', 'Bridge')])
+
+ assert len(FakeFs.filemap) == (1 + 1 + 3 + 1)
+
+ def test_bond_slave_as_primary(self, *args, **kwargs):
+ mb = defaults.NicBonding()
+ m = defaults.Network()
+
+ # ens1 is used as a slave, but then also as a primary device
+ # this doesn't work
+ mb.configure_8023ad("bond0", ["ens1", "ens2", "ens3"])
+ m.configure_dhcp("ens1")
+
+ try:
+ run_tx_by_name(m.transaction(), "WriteConfiguration")
+ except RuntimeError as e:
+ assert e.message == ("Bond slave can not be used as " +
+ "primary device")
+
+ def test_unused_bond(self, *args, **kwargs):
+ mb = defaults.NicBonding()
+ m = defaults.Network()
+
+ # bond0 is created, but ens42 is used
+ mb.configure_8023ad("bond0", ["ens1", "ens2", "ens3"])
+ m.configure_dhcp("ens42")
+
+ try:
+ run_tx_by_name(m.transaction(), "WriteConfiguration")
+ except RuntimeError as e:
+ assert e.message == "Bond configured but not used"
+
+
def run_tx_by_name(txs, name):
tx = None
for _tx in txs:
--
To view, visit http://gerrit.ovirt.org/15714
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9ec23904942649baa031a61f194ee782b63019b0
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-node
Gerrit-Branch: master
Gerrit-Owner: Fabian Deutsch <fabiand at fedoraproject.org>
More information about the node-patches
mailing list