Ondřej Svoboda has uploaded a new change for review.
Change subject: tests: let VDSM consume a bond created by nmcli (WIP)
......................................................................
tests: let VDSM consume a bond created by nmcli (WIP)
This test should serve at this point to automate the process
of VDSM adopting networks managed by NetworkManager, to explore
and debug their competition over the network parts.
TODO: - figure out why NM won't run dhclient automatically,
what is it waiting for? (VDSM connects alright)
- actually, it seems to run its dhclient when VDSM
ifup's the bridge
- wait for NM's dhclient obtaining an address with
netlink.Monitor
Change-Id: I7047ce59a515d0b8ed2c4c5307b4c0d47d4aa92b
Bug-Url: https://bugzilla.redhat.com/1304509
Signed-off-by: Ondřej Svoboda <[email protected]>
---
M tests/functional/networkTests.py
M tests/network/dhcp.py
M tests/network/nettestlib.py
3 files changed, 126 insertions(+), 6 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/59/56059/1
diff --git a/tests/functional/networkTests.py b/tests/functional/networkTests.py
index 52130c1..e07d586 100644
--- a/tests/functional/networkTests.py
+++ b/tests/functional/networkTests.py
@@ -19,6 +19,7 @@
from contextlib import contextmanager
from functools import wraps
import json
+import logging
import re
import os.path
import six
@@ -2955,3 +2956,120 @@
self.assertNotIn('ad_partner_mac', bond_caps[bond])
for nic in nics:
self.assertNotIn('ad_aggregator_id', nic_caps[nic])
+
+ @contextmanager
+ def nm_connection(self, device_name, conn_type, slaves=None, dhcpv4=False):
+ if conn_type != 'bond':
+ raise NotImplementedError('Only bonded NM connections supported.')
+
+ NMCLI = CommandPath('nmcli', '/usr/bin/nmcli')
+
+ conn_name = '{}-{}-{}'.format(conn_type, device_name, 123)
+ rc, out, err = execCmd([NMCLI.cmd,
+ 'connection', 'add',
+ 'type', conn_type,
+ 'ifname', device_name, # e.g. the bond name
+ 'con-name', conn_name,
+ 'autoconnect', 'yes', # already a default
+ 'save', 'no',
+ 'mode', '802.3ad',
+ ])
+ logging.debug('nmcli conn add type bond: %s (%s, %s)', rc,
+ ''.join(out), ''.join(err))
+ if rc == 8:
+ raise SkipTest('NetworkManager is not running.')
+
+ if dhcpv4:
+ rc, out, err = execCmd([NMCLI.cmd,
+ 'connection', 'modify', '--temporary',
+ conn_name,
+ 'ipv4.method', 'auto',
+ ])
+ logging.debug('nmcli conn modify: %s (%s, %s)', rc,
+ ''.join(out), ''.join(err))
+
+ if conn_type == 'bond' and slaves:
+ for slave in slaves:
+ rc, out, err = execCmd([NMCLI.cmd,
+ 'connection', 'add',
+ 'type', 'bond-slave',
+ 'ifname', slave,
+ 'save', 'no',
+ 'master', device_name,
+ ])
+ logging.debug('nmcli conn add type bond-slave: %s (%s, %s)',
+ rc, ''.join(out), ''.join(err))
+ linkSet(slave, ['up'])
+
+ rc, out, err = execCmd([NMCLI.cmd, 'connection', 'up', conn_name])
+ logging.debug('nmcli conn up bond: %s (%s, %s)', rc,
+ ''.join(out), ''.join(err))
+
+ try:
+ yield
+ finally:
+ if slaves:
+ for slave in slaves:
+ rc, out, err = execCmd([NMCLI.cmd,
+ 'connection', 'delete',
+ 'bond-slave-' + slave])
+ logging.debug('nmcli conn delete bond-slave: %s (%s, %s)',
+ rc, ''.join(out), ''.join(err))
+
+ rc, out, err = execCmd([NMCLI.cmd,
+ 'connection', 'delete', conn_name])
+ logging.debug('nmcli conn delete bond: %s (%s, %s)', rc,
+ ''.join(out), ''.join(err))
+
+ @contextmanager
+ def nm_controlled_bond(self):
+ with veth_pair() as (n1, n2), veth_pair() as (n3, n4):
+ addrAdd(n1, IP_ADDRESS, IP_CIDR)
+ linkSet(n1, ['up'])
+ addrAdd(n3, IP_ADDRESS, IP_CIDR)
+ linkSet(n3, ['up'])
+
+ # The address pool is limited so the bond would always get the same
+ # address, until a way is found to use the same MAC (or DUID) under
+ # NM, which refuses the active_slave option (in mode 1).
+
+ # TODO: Or will dnsmasq refuse to give it to us because it has
+ # leased someone else already?
+ with dnsmasq_run([n1, n3], DHCP_RANGE_FROM, DHCP_RANGE_FROM,
+ IP_GATEWAY):
+ bond_name = BONDING_NAME
+ slaves = [n2, n4]
+
+ # TODO: NM should run dhclient on the bond, now it just waits
+ with self.nm_connection(device_name=bond_name,
+ conn_type='bond', slaves=slaves,
+ dhcpv4=True):
+ # TODO: use netlink.Monitor to wait for an address,
+ # but wrap the outer context manager of course
+
+ # status, msg, info = self.vdsm_net.getVdsCapabilities()
+ # bond_caps, nic_caps = info['bondings'], info['nics']
+ yield bond_name, slaves
+
+ @cleanupNet
+ @ValidateRunningAsRoot
+ def test_consume_nm_bond(self):
+ with self.nm_controlled_bond() as (bond_name, slaves):
+ status, msg = self.setupNetworks(
+ {NETWORK_NAME:
+ {'bonding': bond_name, 'bridged': True, 'bootproto': 'dhcp',
+ 'blockingdhcp': True}},
+ {bond_name: {'nics': slaves, 'options': 'mode=4 miimon=100'}},
+ NOCHK)
+ # TODO: check for DHCP and that the same address was obtained
+ self.assertEqual(status, SUCCESS, msg)
+ self.assertNetworkExists(NETWORK_NAME)
+ self.assertBondExists(bond_name, slaves)
+
+ status, msg = self.setupNetworks(
+ {NETWORK_NAME: {'remove': True}},
+ {bond_name: {'remove': True}},
+ NOCHK)
+ self.assertEqual(status, SUCCESS, msg)
+ self.assertNetworkDoesntExist(NETWORK_NAME)
+ self.assertBondDoesntExist(bond_name, slaves)
diff --git a/tests/network/dhcp.py b/tests/network/dhcp.py
index 5bbfe98..8a1d601 100644
--- a/tests/network/dhcp.py
+++ b/tests/network/dhcp.py
@@ -45,13 +45,14 @@
def __init__(self):
self.proc = None
- def start(self, interface, dhcp_range_from=None, dhcp_range_to=None,
+ def start(self, interfaces, dhcp_range_from=None, dhcp_range_to=None,
dhcpv6_range_from=None, dhcpv6_range_to=None, router=None,
ipv6_slaac_prefix=None):
# --dhcp-authoritative The only DHCP server on network
# -p 0 don't act as a DNS server
# --dhcp-option=3,<router> advertise a specific gateway (or None)
# --dhcp-option=6 don't reply with any DNS servers
+ # --interface= (accepts a comma-separated list)
# -d don't daemonize and log to stderr
# --bind-dynamic bind only the testing veth iface
command = [
@@ -59,7 +60,7 @@
'-p', '0',
'--dhcp-option=3' + (',{0}'.format(router) if router else ''),
'--dhcp-option=6',
- '-i', interface, '-I', 'lo', '-d', '--bind-dynamic',
+ '-i', ','.join(interfaces), '-I', 'lo', '-d', '--bind-dynamic',
]
if dhcp_range_from and dhcp_range_to:
diff --git a/tests/network/nettestlib.py b/tests/network/nettestlib.py
index 2839dfe..c00ba96 100644
--- a/tests/network/nettestlib.py
+++ b/tests/network/nettestlib.py
@@ -26,7 +26,7 @@
import signal
import struct
import time
-from contextlib import contextmanager
+from contextlib import contextmanager, nested
from multiprocessing import Process
from nose.plugins.skip import SkipTest
@@ -389,16 +389,17 @@
@contextmanager
-def dnsmasq_run(interface, dhcp_range_from=None, dhcp_range_to=None,
+def dnsmasq_run(interfaces, dhcp_range_from=None, dhcp_range_to=None,
dhcpv6_range_from=None, dhcpv6_range_to=None, router=None,
ipv6_slaac_prefix=None):
"""Manages the life cycle of dnsmasq as a DHCP/RA server."""
server = dhcp.Dnsmasq()
- server.start(interface, dhcp_range_from, dhcp_range_to,
+ server.start(interfaces, dhcp_range_from, dhcp_range_to,
dhcpv6_range_from, dhcpv6_range_to, router,
ipv6_slaac_prefix)
- with firewall.allow_dhcp(interface):
+ # TODO: replace with contextlib.ExitStack after migrating to Python 3.3+
+ with nested(*(firewall.allow_dhcp(interface) for interface in interfaces)):
try:
yield
finally:
--
To view, visit https://gerrit.ovirt.org/56059
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I7047ce59a515d0b8ed2c4c5307b4c0d47d4aa92b
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Ondřej Svoboda <[email protected]>
_______________________________________________
vdsm-patches mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches