diff --git a/lower-constraints.txt b/lower-constraints.txt index 7b0e4c16..f8ed37c8 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -62,7 +62,7 @@ pyflakes==0.8.1 Pygments==2.2.0 pyinotify==0.9.6 pyparsing==2.2.0 -pyroute2==0.4.21 +pyroute2==0.5.2 python-dateutil==2.7.0 python-mimeparse==1.6.0 python-subunit==1.0.0 diff --git a/os_vif/internal/command/ip/api.py b/os_vif/internal/command/ip/api.py index a7f2792f..537307ad 100644 --- a/os_vif/internal/command/ip/api.py +++ b/os_vif/internal/command/ip/api.py @@ -10,80 +10,19 @@ # License for the specific language governing permissions and limitations # under the License. -import abc import os -import six from oslo_log import log as logging -from oslo_utils import importutils +from os_vif.internal.command.ip.linux import impl_pyroute2 as linux_ip_lib from os_vif.internal.command.ip.windows import impl_netifaces as win_ip_lib LOG = logging.getLogger(__name__) -impl_map = { - 'pyroute2': 'os_vif.internal.command.ip.impl_pyroute2.PyRoute2', - 'IPTools': 'os_vif.internal.command.ip.impl_shell.IPTools', -} - - def _get_impl(): if os.name == 'nt': return win_ip_lib else: - # NOTE(sean-k-mooney): currently pyroute2 has a file handle leak. An - # iptools driver has been added as a workaround but No config options - # are # exposed to the user. The iptools driver is considered - # deprecated and # will be removed when a new release of pyroute2 is - # available. - driver = 'IPTools' - return importutils.import_object(impl_map[driver]) - - -@six.add_metaclass(abc.ABCMeta) -class IpCommand(object): - - TYPE_VETH = 'veth' - TYPE_VLAN = 'vlan' - - @abc.abstractmethod - def set(self, device, check_exit_code=None, state=None, mtu=None, - address=None, promisc=None): - """Method to set a parameter in an interface. - - :param device: A network device (string) - :param check_exit_code: List of integers of allowed execution exit - codes - :param state: String network device state - :param mtu: Integer MTU value - :param address: String MAC address - :param promisc: Boolean promiscuous mode - :return: status of the command execution - """ - - @abc.abstractmethod - def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, - vlan_id=None): - """Method to add an interface. - - :param device: A network device (string) - :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) - :param check_exit_code: List of integers of allowed execution exit - codes - :param peer: String peer name, for veth interfaces - :param link: String root network interface name, 'device' will be a - VLAN tagged virtual interface - :param vlan_id: Integer VLAN ID for VLAN devices - :return: status of the command execution - """ - - @abc.abstractmethod - def delete(self, device, check_exit_code=None): - """Method to delete an interface. - - :param device: A network device (string) - :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) - :return: status of the command execution - """ + return linux_ip_lib diff --git a/os_vif/internal/command/ip/impl_shell.py b/os_vif/internal/command/ip/impl_shell.py deleted file mode 100644 index 503e22ec..00000000 --- a/os_vif/internal/command/ip/impl_shell.py +++ /dev/null @@ -1,138 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import re - -from oslo_concurrency import processutils -from oslo_utils import excutils - -from os_vif.internal.command.ip import api - - -def _execute_command(*args): - return processutils.execute(*args) - - -class ShellIpCommands(object): - - def __init__(self, cmd=_execute_command): - self._execute_command = cmd - - def add_device(self, device, dev_type, peer=None, link=None, - vlan_id=None): - ret = None - if 'vlan' == dev_type: - ret = self._execute_command('ip', 'link', 'add', 'link', link, - 'name', device, 'type', dev_type, - 'id', vlan_id) - elif 'veth' == dev_type: - ret = self._execute_command('ip', 'link', 'add', device, 'type', - dev_type, 'peer', 'name', peer) - elif 'dummy' == dev_type: - ret = self._execute_command('ip', 'link', 'add', device, - 'type', dev_type) - return ret - - def del_device(self, device): - ret = None - if self.exist_device(device): - ret = self._execute_command('ip', 'link', 'del', device) - return ret - - def set(self, device, status=None, **kwargs): - args = ['ip', 'link', 'set', device] - if status is not None: - args.append(status) - temp = [x for x in kwargs.items()] - for x in temp: - args += x - self._execute_command(*args) - - def set_status_up(self, device): - self.set(device, status='up') - - def set_status_down(self, device): - self.set(device, status='down') - - def set_device_mtu(self, device, mtu): - args = {'mtu': mtu} - self.set(device, args) - - def show_device(self, device): - val, err = _execute_command('ip', 'link', 'show', device) - return val.splitlines() - - def exist_device(self, device): - try: - self._execute_command('ip', 'link', 'show', device) - return True - except processutils.ProcessExecutionError as e: - with excutils.save_and_reraise_exception() as saved_exception: - if e.exit_code == 1: - saved_exception.reraise = False - return False - - def show_state(self, device): - regex = re.compile(r".*state (?P\w+)") - match = regex.match(self.show_device(device)[0]) - if match is None: - return - return match.group('state') - - def show_promisc(self, device): - regex = re.compile(r".*(PROMISC)") - match = regex.match(self.show_device(device)[0]) - return True if match else False - - def show_mac(self, device): - exp = r".*link/ether (?P([0-9A-Fa-f]{2}[:]){5}[0-9A-Fa-f]{2})" - regex = re.compile(exp) - match = regex.match(self.show_device(device)[1]) - if match is None: - return - return match.group('mac') - - def show_mtu(self, device): - regex = re.compile(r".*mtu (?P\d+)") - match = regex.match(self.show_device(device)[0]) - if match is None: - return - return int(match.group('mtu')) - - -class IPTools(api.IpCommand): - - def __init__(self, cmd=_execute_command): - self.ip = ShellIpCommands(cmd=cmd) - - def set(self, device, check_exit_code=None, state=None, mtu=None, - address=None, promisc=None): - args = {} - if mtu is not None: - args['mtu'] = mtu - if address is not None: - args['address'] = address - if promisc is not None: - args['promisc'] = 'on' if promisc else 'off' - - if isinstance(check_exit_code, int): - check_exit_code = [check_exit_code] - return self.ip.set(device, status=state, **args) - - def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, - vlan_id=None): - - return self.ip.add_device(device, dev_type, peer=peer, - link=link, vlan_id=vlan_id) - - def delete(self, device, check_exit_code=None): - return self.ip.del_device(device) diff --git a/os_vif/internal/command/ip/ip_command.py b/os_vif/internal/command/ip/ip_command.py new file mode 100644 index 00000000..a5c8c34a --- /dev/null +++ b/os_vif/internal/command/ip/ip_command.py @@ -0,0 +1,61 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import abc +import six + + +@six.add_metaclass(abc.ABCMeta) +class IpCommand(object): + + TYPE_VETH = 'veth' + TYPE_VLAN = 'vlan' + + @abc.abstractmethod + def set(self, device, check_exit_code=None, state=None, mtu=None, + address=None, promisc=None): + """Method to set a parameter in an interface. + + :param device: A network device (string) + :param check_exit_code: List of integers of allowed execution exit + codes + :param state: String network device state + :param mtu: Integer MTU value + :param address: String MAC address + :param promisc: Boolean promiscuous mode + :return: status of the command execution + """ + + @abc.abstractmethod + def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, + vlan_id=None): + """Method to add an interface. + + :param device: A network device (string) + :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) + :param check_exit_code: List of integers of allowed execution exit + codes + :param peer: String peer name, for veth interfaces + :param link: String root network interface name, 'device' will be a + VLAN tagged virtual interface + :param vlan_id: Integer VLAN ID for VLAN devices + :return: status of the command execution + """ + + @abc.abstractmethod + def delete(self, device, check_exit_code=None): + """Method to delete an interface. + + :param device: A network device (string) + :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) + :return: status of the command execution + """ diff --git a/os_vif/internal/command/ip/linux/impl_pyroute2.py b/os_vif/internal/command/ip/linux/impl_pyroute2.py index d1e446a5..5c9e46d5 100644 --- a/os_vif/internal/command/ip/linux/impl_pyroute2.py +++ b/os_vif/internal/command/ip/linux/impl_pyroute2.py @@ -17,13 +17,13 @@ from pyroute2.netlink import exceptions as ipexc from pyroute2.netlink.rtnl import ifinfmsg from os_vif import exception -from os_vif.internal.command.ip import api +from os_vif.internal.command.ip import ip_command from os_vif import utils LOG = logging.getLogger(__name__) -class PyRoute2(api.IpCommand): +class PyRoute2(ip_command.IpCommand): def _ip_link(self, ip, command, check_exit_code, **kwargs): try: diff --git a/os_vif/tests/functional/internal/command/ip/test_impl_iptools.py b/os_vif/tests/functional/internal/command/ip/test_impl_iptools.py deleted file mode 100644 index 52f7ae11..00000000 --- a/os_vif/tests/functional/internal/command/ip/test_impl_iptools.py +++ /dev/null @@ -1,180 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import re - -from oslo_concurrency import processutils -from oslo_utils import excutils - -from os_vif.internal.command.ip import impl_shell -from os_vif.tests.functional import base -from os_vif.tests.functional import privsep - - -@privsep.os_vif_pctxt.entrypoint -def _execute_command(*args): - return processutils.execute(*args) - - -class ShellIpCommands(object): - - def add_device(self, device, dev_type, peer=None, link=None, - vlan_id=None): - if 'vlan' == dev_type: - _execute_command('ip', 'link', 'add', 'link', link, - 'name', device, 'type', dev_type, 'vlan', 'id', - vlan_id) - elif 'veth' == dev_type: - _execute_command('ip', 'link', 'add', device, 'type', dev_type, - 'peer', 'name', peer) - elif 'dummy' == dev_type: - _execute_command('ip', 'link', 'add', device, 'type', dev_type) - - def del_device(self, device): - if self.exist_device(device): - _execute_command('ip', 'link', 'del', device) - - def set_status_up(self, device): - _execute_command('ip', 'link', 'set', device, 'up') - - def set_status_down(self, device): - _execute_command('ip', 'link', 'set', device, 'down') - - def set_device_mtu(self, device, mtu): - _execute_command('ip', 'link', 'set', device, 'mtu', mtu) - - def show_device(self, device): - val, err = _execute_command('ip', 'link', 'show', device) - return val.splitlines() - - def exist_device(self, device): - try: - _execute_command('ip', 'link', 'show', device) - return True - except processutils.ProcessExecutionError as e: - with excutils.save_and_reraise_exception() as saved_exception: - if e.exit_code == 1: - saved_exception.reraise = False - return False - - def show_state(self, device): - regex = re.compile(r".*state (?P\w+)") - match = regex.match(self.show_device(device)[0]) - if match is None: - return - return match.group('state') - - def show_promisc(self, device): - regex = re.compile(r".*(PROMISC)") - match = regex.match(self.show_device(device)[0]) - return True if match else False - - def show_mac(self, device): - exp = r".*link/ether (?P([0-9A-Fa-f]{2}[:]){5}[0-9A-Fa-f]{2})" - regex = re.compile(exp) - match = regex.match(self.show_device(device)[1]) - if match is None: - return - return match.group('mac') - - def show_mtu(self, device): - regex = re.compile(r".*mtu (?P\d+)") - match = regex.match(self.show_device(device)[0]) - if match is None: - return - return int(match.group('mtu')) - - -def _ip_cmd_set(*args, **kwargs): - impl_shell.IPTools(cmd=_execute_command).set(*args, **kwargs) - - -def _ip_cmd_add(*args, **kwargs): - impl_shell.IPTools(cmd=_execute_command).add(*args, **kwargs) - - -def _ip_cmd_delete(*args, **kwargs): - impl_shell.IPTools(cmd=_execute_command).delete(*args, **kwargs) - - -class TestIpCommand(ShellIpCommands, base.BaseFunctionalTestCase): - - def setUp(self): - super(TestIpCommand, self).setUp() - - def test_set_state(self): - device1 = "iptools_dev_1" - device2 = "iptools_dev_2" - self.addCleanup(self.del_device, device1) - self.add_device(device1, 'veth', peer=device2) - _ip_cmd_set(device1, state='up') - _ip_cmd_set(device2, state='up') - self.assertEqual('UP', self.show_state(device1)) - self.assertEqual('UP', self.show_state(device2)) - _ip_cmd_set(device1, state='down') - _ip_cmd_set(device2, state='down') - self.assertEqual('DOWN', self.show_state(device1)) - self.assertEqual('DOWN', self.show_state(device2)) - - def test_set_mtu(self): - device = "iptools_dev_3" - self.addCleanup(self.del_device, device) - self.add_device(device, 'dummy') - _ip_cmd_set(device, mtu=1200) - self.assertEqual(1200, self.show_mtu(device)) - _ip_cmd_set(device, mtu=900) - self.assertEqual(900, self.show_mtu(device)) - - def test_set_address(self): - device = "iptools_dev_4" - address1 = "36:a7:e4:f9:01:01" - address2 = "36:a7:e4:f9:01:01" - self.addCleanup(self.del_device, device) - self.add_device(device, 'dummy') - _ip_cmd_set(device, address=address1) - self.assertEqual(address1, self.show_mac(device)) - _ip_cmd_set(device, address=address2) - self.assertEqual(address2, self.show_mac(device)) - - def test_set_promisc(self): - device = "iptools_dev_5" - self.addCleanup(self.del_device, device) - self.add_device(device, 'dummy') - _ip_cmd_set(device, promisc=True) - self.assertTrue(self.show_promisc(device)) - _ip_cmd_set(device, promisc=False) - self.assertFalse(self.show_promisc(device)) - - def test_add_vlan(self): - device = "iptools_dev_6" - link = "iptools_devlink" - self.addCleanup(self.del_device, device) - self.addCleanup(self.del_device, link) - self.add_device(link, 'dummy') - _ip_cmd_add(device, 'vlan', link=link, vlan_id=100) - self.assertTrue(self.exist_device(device)) - - def test_add_veth(self): - device = "iptools_dev_7" - peer = "iptools_devpeer" - self.addCleanup(self.del_device, device) - _ip_cmd_add(device, 'veth', peer=peer) - self.assertTrue(self.exist_device(device)) - self.assertTrue(self.exist_device(peer)) - - def test_delete(self): - device = "iptools_dev_8" - self.addCleanup(self.del_device, device) - self.add_device(device, 'dummy') - self.assertTrue(self.exist_device(device)) - _ip_cmd_delete(device) - self.assertFalse(self.exist_device(device)) diff --git a/releasenotes/notes/remove_iptools_implementation-2eb866573a680e61.yaml b/releasenotes/notes/remove_iptools_implementation-2eb866573a680e61.yaml new file mode 100644 index 00000000..7e58e9ea --- /dev/null +++ b/releasenotes/notes/remove_iptools_implementation-2eb866573a680e61.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + Removed IPTools implementation. IPTools driver was implemented to avoid a + bug in pyroute2 library, currently solved. This implementation was marked + as "deprecated" two releases ago. IP Linux commands now use `Pyroute2`_. + + .. _Pyroute2: https://docs.pyroute2.org/ diff --git a/requirements.txt b/requirements.txt index b3dfaff3..0111051c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,6 @@ oslo.i18n>=3.15.3 # Apache-2.0 oslo.privsep>=1.23.0 # Apache-2.0 oslo.versionedobjects>=1.28.0 # Apache-2.0 ovsdbapp>=0.12.1 # Apache-2.0 -pyroute2>=0.4.21;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) +pyroute2>=0.5.2;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) six>=1.10.0 # MIT stevedore>=1.20.0 # Apache-2.0