diff --git a/neutron/agent/common/ip_lib.py b/neutron/agent/common/ip_lib.py new file mode 100644 index 00000000000..ee129f40a0b --- /dev/null +++ b/neutron/agent/common/ip_lib.py @@ -0,0 +1,27 @@ +# Copyright 2016 Cloudbase Solutions. +# All Rights Reserved. +# +# 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 os + +if os.name == 'nt': + from neutron.agent.windows import ip_lib +else: + from neutron.agent.linux import ip_lib + + +OPTS = ip_lib.OPTS + +IPWrapper = ip_lib.IPWrapper +IPDevice = ip_lib.IPDevice diff --git a/neutron/agent/windows/ip_lib.py b/neutron/agent/windows/ip_lib.py new file mode 100644 index 00000000000..fea99674794 --- /dev/null +++ b/neutron/agent/windows/ip_lib.py @@ -0,0 +1,59 @@ +# Copyright 2016 Cloudbase Solutions. +# All Rights Reserved. +# +# 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 netifaces + +from oslo_log import log as logging + +from neutron._i18n import _LE + +LOG = logging.getLogger(__name__) + +OPTS = [] + + +class IPWrapper(object): + + def get_device_by_ip(self, ip): + if not ip: + return + + for device in self.get_devices(): + if device.device_has_ip(ip): + return device + + def get_devices(self): + try: + return [IPDevice(iface) for iface in netifaces.interfaces()] + except (OSError, MemoryError): + LOG.error(_LE("Failed to get network interfaces.")) + return [] + + +class IPDevice(object): + + def __init__(self, name): + self.device_name = name + + def device_has_ip(self, ip): + try: + addresses = [ip_addr['addr'] for ip_addr in + netifaces.ifaddresses(self.device_name).get( + netifaces.AF_INET, [])] + return ip in addresses + except OSError: + LOG.error(_LE("Failed to get ip addresses for interface: %s."), + self.device_name) + return False diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py index 5b10794f803..a08d3d3ed1e 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py @@ -29,11 +29,11 @@ import six from six import moves from neutron._i18n import _, _LE, _LI, _LW +from neutron.agent.common import ip_lib from neutron.agent.common import ovs_lib from neutron.agent.common import polling from neutron.agent.common import utils from neutron.agent.l2.extensions import manager as ext_manager -from neutron.agent.linux import ip_lib from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as sg_rpc from neutron.api.rpc.callbacks import resources diff --git a/neutron/tests/functional/agent/windows/__init__.py b/neutron/tests/functional/agent/windows/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/tests/functional/agent/windows/test_ip_lib.py b/neutron/tests/functional/agent/windows/test_ip_lib.py new file mode 100644 index 00000000000..ba8dd842614 --- /dev/null +++ b/neutron/tests/functional/agent/windows/test_ip_lib.py @@ -0,0 +1,30 @@ +# Copyright 2016 Cloudbase Solutions. +# All Rights Reserved. +# +# 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. + +from neutron.agent.windows import ip_lib +from neutron.tests import base + +WRONG_IP = '0.0.0.0' +TEST_IP = '127.0.0.1' + + +class IpLibTestCase(base.BaseTestCase): + + def test_ipwrapper_get_device_by_ip_None(self): + self.assertIsNone(ip_lib.IPWrapper().get_device_by_ip(WRONG_IP)) + + def test_ipwrapper_get_device_by_ip(self): + ip_dev = ip_lib.IPWrapper().get_device_by_ip(TEST_IP) + self.assertEqual('lo', ip_dev.device_name) diff --git a/neutron/tests/unit/agent/windows/__init__.py b/neutron/tests/unit/agent/windows/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/tests/unit/agent/windows/test_ip_lib.py b/neutron/tests/unit/agent/windows/test_ip_lib.py new file mode 100644 index 00000000000..1e29cb3992f --- /dev/null +++ b/neutron/tests/unit/agent/windows/test_ip_lib.py @@ -0,0 +1,92 @@ +# Copyright 2016 Cloudbase Solutions. +# All Rights Reserved. +# +# 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 mock +import netifaces + +from neutron.agent.windows import ip_lib +from neutron.tests import base + + +class TestIpWrapper(base.BaseTestCase): + + def test_get_device_by_ip_no_ip(self): + ret = ip_lib.IPWrapper().get_device_by_ip(None) + self.assertIsNone(ret) + + @mock.patch.object(ip_lib.IPWrapper, 'get_devices') + def test_get_device_by_ip(self, mock_get_devices): + mock_dev1 = mock.MagicMock() + mock_dev2 = mock.MagicMock() + mock_dev1.device_has_ip.return_value = False + mock_dev2.device_has_ip.return_value = True + mock_get_devices.return_value = [mock_dev1, mock_dev2] + ret = ip_lib.IPWrapper().get_device_by_ip('fake_ip') + + self.assertEqual(mock_dev2, ret) + + @mock.patch.object(ip_lib.IPWrapper, 'get_devices') + def test_get_device_by_ip_exception(self, mock_get_devices): + mock_get_devices.side_effects = OSError + ret = ip_lib.IPWrapper().get_device_by_ip(mock.sentinel.fake_ip) + + self.assertIsNone(ret) + + @mock.patch('netifaces.interfaces') + def test_get_devices(self, mock_interfaces): + mock_interfaces.return_value = [mock.sentinel.dev1, + mock.sentinel.dev2] + + ret = ip_lib.IPWrapper().get_devices() + self.assertEqual(mock.sentinel.dev1, ret[0].device_name) + self.assertEqual(mock.sentinel.dev2, ret[1].device_name) + + @mock.patch('netifaces.interfaces') + def test_get_devices_error(self, mock_interfaces): + mock_interfaces.side_effects = OSError + ret = ip_lib.IPWrapper().get_devices() + + self.assertEqual([], ret) + + +class TestIpDevice(base.BaseTestCase): + + @mock.patch('netifaces.ifaddresses') + def test_device_has_ip(self, mock_netifaces): + mock_address = {'addr': mock.sentinel.fake_addr} + mock_netifaces.return_value = {netifaces.AF_INET: [mock_address]} + + ret = ip_lib.IPDevice("fake_dev").device_has_ip( + mock.sentinel.fake_addr) + + self.assertTrue(ret) + + @mock.patch('netifaces.ifaddresses') + def test_device_has_ip_false(self, mock_netifaces): + mock_netifaces.return_value = {} + + ret = ip_lib.IPDevice("fake_dev").device_has_ip( + mock.sentinel.fake_addr) + + self.assertFalse(ret) + + @mock.patch('netifaces.ifaddresses') + def test_device_has_ip_error(self, mock_netifaces): + mock_netifaces.side_effects = OSError + + ret = ip_lib.IPDevice("fake_dev").device_has_ip( + mock.sentinel.fake_addr) + + self.assertFalse(ret) diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py index 1c344020b9c..c50f6778fb6 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py @@ -21,8 +21,8 @@ from oslo_config import cfg from oslo_log import log import six +from neutron.agent.common import ip_lib from neutron.agent.common import ovs_lib -from neutron.agent.linux import ip_lib from neutron.common import constants as n_const from neutron.plugins.common import constants as p_const from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants diff --git a/requirements.txt b/requirements.txt index 09a4801c604..2fefd7d091c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,6 +16,7 @@ requests!=2.9.0,>=2.8.1 # Apache-2.0 Jinja2>=2.8 # BSD License (3 clause) keystonemiddleware!=4.1.0,>=4.0.0 # Apache-2.0 netaddr!=0.7.16,>=0.7.12 # BSD +netifaces>=0.10.4 # MIT neutron-lib>=0.0.1 # Apache-2.0 python-neutronclient!=4.1.0,>=2.6.0 # Apache-2.0 retrying!=1.3.0,>=1.2.3 # Apache-2.0