Petr Horáček has uploaded a new change for review. Change subject: net: introduce acquire module ......................................................................
net: introduce acquire module This module will be used to acquire external devices, both persisted by ifcfg and not persisted. It is needed for OVS switch (and eventually also for iproute2 and pyroute2). This patch also introduces PersistingStringIO which is needed for files mocking. Change-Id: I180cfd7d69c0ae0a24188bc3d909b9d3d7c12145 Bug-Url: https://bugzilla.redhat.com/1195208 Signed-off-by: Petr Horáček <[email protected]> --- M lib/vdsm/network/Makefile.am A lib/vdsm/network/acquire.py A tests/network/acquire_ifaces_test.py M tests/testlib.py M vdsm.spec.in 5 files changed, 206 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/74/60974/1 diff --git a/lib/vdsm/network/Makefile.am b/lib/vdsm/network/Makefile.am index 3889776..52a5719 100644 --- a/lib/vdsm/network/Makefile.am +++ b/lib/vdsm/network/Makefile.am @@ -24,6 +24,7 @@ vdsmnetworkdir = $(vdsmpylibdir)/network dist_vdsmnetwork_PYTHON = \ __init__.py \ + acquire.py \ api.py \ errors.py \ canonicalize.py \ diff --git a/lib/vdsm/network/acquire.py b/lib/vdsm/network/acquire.py new file mode 100644 index 0000000..3bd4cda --- /dev/null +++ b/lib/vdsm/network/acquire.py @@ -0,0 +1,78 @@ +# Copyright 2016 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Refer to the README and COPYING files for full details of the license +# +from __future__ import absolute_import + +import itertools +import os + +import six + +from .configurators import ifcfg +from .ip import address +from .ip import dhclient + + +ACQUIRED_IFCFG_SUFFIX = ( + u'\n' + '# This device is now owned by VDSM\n' + 'ONBOOT=no\n' + 'NM_CONTROLLED=no\n' + '# end of VDSM suffix.' +) + + +def acquire_unowned_ifaces(netinfo_nets, external_ifaces): + """Acquire external ifaces which are not owned by us.""" + used_ports = frozenset(itertools.chain.from_iterable( + [attrs['ports'] for attrs in six.itervalues(netinfo_nets)])) + for iface in external_ifaces: + if iface not in used_ports: + _acquire_external_iface(iface) + + +def _acquire_external_iface(iface): + is_ifcfg_controlled = os.path.isfile(ifcfg.NET_CONF_PREF + iface) + if is_ifcfg_controlled: + _acquire_external_ifcfg_iface(iface) + else: + _acquire_external_non_persistent_iface(iface) + + +def _acquire_external_ifcfg_iface(iface): + # TODO: Test that NM does not turn iface UP again. + with open(ifcfg.NET_CONF_PREF + iface, 'r+') as f: + lines = f.readlines() + _comment_out_parameters(('ONBOOT', 'NM_CONTROLLED'), lines) + f.seek(0) + f.writelines(lines) + f.write(ACQUIRED_IFCFG_SUFFIX) + ifcfg.ifdown(iface) + + +def _comment_out_parameters(parameters, lines): + for i, line in enumerate(lines): + if any([line.startswith(parameter) for parameter in parameters]): + lines[i] = '#' + line + + +def _acquire_external_non_persistent_iface(iface): + # TODO: Tell NM to ignore this iface if it is needed. + dhclient.kill(iface, family=4) + dhclient.kill(iface, family=6) + address.flush(iface) diff --git a/tests/network/acquire_ifaces_test.py b/tests/network/acquire_ifaces_test.py new file mode 100644 index 0000000..815adb2 --- /dev/null +++ b/tests/network/acquire_ifaces_test.py @@ -0,0 +1,104 @@ +# Copyright 2016 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA +# +# Refer to the README and COPYING files for full details of the license +# +from __future__ import absolute_import + +from io import StringIO + +from nose.plugins.attrib import attr + +from vdsm.network import acquire + +from testlib import VdsmTestCase as TestCaseBase, PersistingStringIO, mock + + +NIC_NAME = 'eth3' + +NIC_IFCFG = ( + u'DEVICE={}\n' + 'BOOTPROTO=dhcp\n' + 'ONBOOT=yes\n' + 'MTU=1500\n' + 'NM_CONTROLLED=yes') + +ACQUIRED_NIC_IFCFG = ( + u'DEVICE={}\n' + 'BOOTPROTO=dhcp\n' + '#ONBOOT=yes\n' + 'MTU=1500\n' + '#NM_CONTROLLED=yes') + acquire.ACQUIRED_IFCFG_SUFFIX + + +NETINFO_NETS = { + 'net1': { + 'ports': [NIC_NAME, 'vnet0'] + } +} + + +@attr(type='unit') +class AcquireNicTest(TestCaseBase): + + @mock.patch('vdsm.network.acquire.address') + @mock.patch('vdsm.network.acquire.dhclient') + @mock.patch('vdsm.network.acquire.ifcfg') + @mock.patch('vdsm.network.acquire.open', create=True) + @mock.patch('vdsm.network.acquire.os') + def test_do_not_acquire_acquired_nic(self, mock_os, mock_open, mock_ifcfg, + mock_dhclient, mock_address): + acquire.acquire_unowned_ifaces( + netinfo_nets=NETINFO_NETS, external_ifaces=[NIC_NAME]) + + mock_os.path.isfile.assert_not_called() + mock_open.assert_not_called() + mock_ifcfg.ifdown.assert_not_called() + mock_dhclient.kill.assert_not_called() + mock_address.flush.assert_not_called() + + @mock.patch('vdsm.network.acquire.address') + @mock.patch('vdsm.network.acquire.dhclient') + @mock.patch('vdsm.network.acquire.os') + def test_acquire_non_persisted_nic(self, mock_os, mock_dhclient, + mock_address): + mock_os.path.isfile.return_value = False + mock_dhclient.kill.return_value = None + mock_address.flush.return_value = None + + acquire.acquire_unowned_ifaces( + netinfo_nets={}, external_ifaces=[NIC_NAME]) + + mock_dhclient.kill.assert_any_call(NIC_NAME, family=4) + mock_dhclient.kill.assert_any_call(NIC_NAME, family=6) + mock_address.flush.assert_called_with(NIC_NAME) + + @mock.patch('vdsm.network.acquire.ifcfg') + @mock.patch('vdsm.network.acquire.os') + def test_acquire_ifcfg_persisted_nic(self, mock_os, mock_ifcfg): + mock_os.path.isfile.return_value = True + mock_ifcfg.ifdown.return_value = None + + mock_file = PersistingStringIO(NIC_IFCFG) + with mock.patch('vdsm.network.acquire.open', return_value=mock_file, + create=True): + + acquire.acquire_unowned_ifaces( + netinfo_nets={}, external_ifaces=[NIC_NAME]) + + mock_ifcfg.ifdown.assert_called_with(NIC_NAME) + self.assertEqual(mock_file.read_closed(), ACQUIRED_NIC_IFCFG) diff --git a/tests/testlib.py b/tests/testlib.py index 132f5e2..14960a9 100644 --- a/tests/testlib.py +++ b/tests/testlib.py @@ -564,3 +564,25 @@ return meth(self, *args, **kwargs) raise exception return wrapper + + +class PersistingStringIO(io.StringIO): + """Keeps its contents even after it's closed. Can be used to mock files.""" + + def __init__(self, *args, **kwargs): + self._pre_close_content = None + super(PersistingStringIO, self).__init__(*args, **kwargs) + + def close(self, *args, **kwargs): + self._pre_close_content = self._read_content() + super(PersistingStringIO, self).__init__(*args, **kwargs) + + def _read_content(self): + position = self.tell() + self.seek(0) + content = self.read() + self.seek(position) + return content + + def read_closed(self): + return self._pre_close_content diff --git a/vdsm.spec.in b/vdsm.spec.in index 020d74e..5e52762 100644 --- a/vdsm.spec.in +++ b/vdsm.spec.in @@ -1179,6 +1179,7 @@ %{python_sitelib}/%{vdsm_name}/moduleloader.py* %{python_sitelib}/%{vdsm_name}/momIF.py* %{python_sitelib}/%{vdsm_name}/network/__init__.py* +%{python_sitelib}/%{vdsm_name}/network/acquire.py* %{python_sitelib}/%{vdsm_name}/network/api.py* %{python_sitelib}/%{vdsm_name}/network/configurators/__init__.py* %{python_sitelib}/%{vdsm_name}/network/configurators/ifcfg.py* -- To view, visit https://gerrit.ovirt.org/60974 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I180cfd7d69c0ae0a24188bc3d909b9d3d7c12145 Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: master Gerrit-Owner: Petr Horáček <[email protected]> _______________________________________________ vdsm-patches mailing list [email protected] https://lists.fedorahosted.org/admin/lists/[email protected]
