If method ``set_netns`` fails, restore previous device namespace

If the ``IpLinkCommand.set_netns`` fails, the method restores the
previous device namespace before raising the exception.

Closes-Bug: #2049590
Change-Id: I73b36ef161441b52922d888c11a144eafe8a7ed0
This commit is contained in:
Rodolfo Alonso Hernandez 2024-01-16 02:36:27 +00:00
parent 5ce17647c6
commit e234a7aeab
3 changed files with 20 additions and 17 deletions

View File

@ -374,11 +374,6 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
LOG.warning("Failed to set interface %s into namespace %s. " LOG.warning("Failed to set interface %s into namespace %s. "
"Interface not found, attempt: %s, retrying.", "Interface not found, attempt: %s, retrying.",
device, namespace, i + 1) device, namespace, i + 1)
# NOTE(slaweq) In such case it's required to reset device's
# namespace as it was already set to the "namespace"
# and after retry neutron will look for it in that namespace
# which is wrong
device.namespace = None
time.sleep(1) time.sleep(1)
except utils.WaitTimeout: except utils.WaitTimeout:
# NOTE(slaweq): if the exception was WaitTimeout then it means # NOTE(slaweq): if the exception was WaitTimeout then it means

View File

@ -25,6 +25,7 @@ from neutron_lib import constants
from neutron_lib import exceptions from neutron_lib import exceptions
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import netutils from oslo_utils import netutils
from pyroute2.netlink import exceptions as netlink_exceptions from pyroute2.netlink import exceptions as netlink_exceptions
from pyroute2.netlink import rtnl from pyroute2.netlink import rtnl
@ -477,16 +478,16 @@ class IpLinkCommand(IpDeviceCommandBase):
self.name, self._parent.namespace, state='down') self.name, self._parent.namespace, state='down')
def set_netns(self, namespace, is_ovs_port=False): def set_netns(self, namespace, is_ovs_port=False):
privileged.set_link_attribute( old_namespace = self._parent.namespace
self.name, self._parent.namespace, net_ns_fd=namespace) try:
self._parent.namespace = namespace privileged.set_link_attribute(
if is_ovs_port: self.name, self._parent.namespace, net_ns_fd=namespace)
# NOTE(slaweq): because of the "shy port" which may dissapear for self._parent.namespace = namespace
# short time after it's moved to the namespace we need to wait common_utils.wait_until_true(lambda: self.exists, timeout=3,
# a bit before checking if port really exists in the namespace sleep=0.5)
time.sleep(1) except common_utils.WaitTimeout:
common_utils.wait_until_true(lambda: self.exists, timeout=5, with excutils.save_and_reraise_exception():
sleep=0.5) self._parent.namespace = old_namespace
def set_name(self, name): def set_name(self, name):
privileged.set_link_attribute( privileged.set_link_attribute(

View File

@ -13,11 +13,13 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import time
from unittest import mock from unittest import mock
from neutron_lib import constants from neutron_lib import constants
from neutron_lib.plugins.ml2 import ovs_constants as ovs_const from neutron_lib.plugins.ml2 import ovs_constants as ovs_const
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import uuidutils
from pyroute2.netlink import exceptions as pyroute2_exc from pyroute2.netlink import exceptions as pyroute2_exc
from neutron.agent.common import ovs_lib from neutron.agent.common import ovs_lib
@ -387,6 +389,10 @@ class TestABCDriver(TestBase):
class TestOVSInterfaceDriver(TestBase): class TestOVSInterfaceDriver(TestBase):
def setUp(self):
super().setUp()
self.mock_sleep = mock.patch.object(time, 'sleep').start()
def test_get_device_name(self): def test_get_device_name(self):
br = interface.OVSInterfaceDriver(self.conf) br = interface.OVSInterfaceDriver(self.conf)
device_name = br.get_device_name(FakePort()) device_name = br.get_device_name(FakePort())
@ -525,13 +531,14 @@ class TestOVSInterfaceDriver(TestBase):
self.ip.ensure_namespace.return_value = namespace_obj self.ip.ensure_namespace.return_value = namespace_obj
namespace_obj.add_device_to_namespace.side_effect = ( namespace_obj.add_device_to_namespace.side_effect = (
ip_lib.NetworkInterfaceNotFound) ip_lib.NetworkInterfaceNotFound)
device = mock.MagicMock() namespace_name = uuidutils.generate_uuid()
device = mock.MagicMock(namespace=namespace_name)
self.assertRaises( self.assertRaises(
ip_lib.NetworkInterfaceNotFound, ip_lib.NetworkInterfaceNotFound,
ovs._add_device_to_namespace, ovs._add_device_to_namespace,
self.ip, device, "test-ns") self.ip, device, "test-ns")
self.assertEqual(10, namespace_obj.add_device_to_namespace.call_count) self.assertEqual(10, namespace_obj.add_device_to_namespace.call_count)
self.assertIsNone(device.namespace) self.assertEqual(namespace_name, device.namespace)
class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver): class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver):