Browse Source

Ignore missing ports when handling security groups

This issue affects the neutron OVS agent, when using the Hyper-V
security groups driver.

In quite a few situations, OVS ports may be leaked. This becomes
more troublesome as the OVS agent will try to update rules for
Hyper-V switch ports that do not exist anymore and fail.

Note that the agent handles multiple ports at a time. If one of
them is a leaked port, the others won't get processed.

The security groups driver may just ignore missing ports.

Closes-Bug: #1794975

Change-Id: Ic59ba7ca68828dcead99973fadc4ec798e116833
Lucian Petrut 6 months ago
parent
commit
15a7db5e4f

+ 24
- 0
networking_hyperv/neutron/_common_utils.py View File

@@ -15,7 +15,12 @@
15 15
 #    under the License.
16 16
 
17 17
 import inspect
18
+from os_win import exceptions as os_win_exc
18 19
 from oslo_concurrency import lockutils
20
+from oslo_log import log as logging
21
+from oslo_utils import reflection
22
+
23
+LOG = logging.getLogger(__name__)
19 24
 
20 25
 
21 26
 def get_port_synchronized_decorator(lock_prefix):
@@ -36,3 +41,22 @@ def get_port_synchronized_decorator(lock_prefix):
36 41
             return inner()
37 42
         return wrapper
38 43
     return _port_synchronized
44
+
45
+
46
+def ignore_missing_ports(f):
47
+    def wrapper(*args, **kwargs):
48
+        # The decorated method is expected to accept a port as argument.
49
+        call_args = inspect.getcallargs(f, *args, **kwargs)
50
+        port_id = (call_args.get('port_id') or
51
+                   call_args.get('port', {}).get('id'))
52
+
53
+        try:
54
+            return f(*args, **kwargs)
55
+        except (os_win_exc.HyperVPortNotFoundException,
56
+                os_win_exc.HyperVvNicNotFound):
57
+            func_name = reflection.get_callable_name(f)
58
+            LOG.warning("Could not find port '%(port_id)s' while executing "
59
+                        "'%(func_name)s'. It may have been removed.",
60
+                        dict(port_id=port_id,
61
+                             func_name=func_name))
62
+    return wrapper

+ 3
- 0
networking_hyperv/neutron/security_groups_driver.py View File

@@ -132,6 +132,7 @@ class HyperVSecurityGroupsDriverMixin(object):
132 132
             newports[port['id']] = _rules
133 133
         return newports
134 134
 
135
+    @c_utils.ignore_missing_ports
135 136
     def prepare_port_filter(self, port):
136 137
         if not port.get('port_security_enabled'):
137 138
             LOG.info('Port %s does not have security enabled. '
@@ -214,6 +215,7 @@ class HyperVSecurityGroupsDriverMixin(object):
214 215
     def apply_port_filter(self, port):
215 216
         LOG.info('Applying port filter.')
216 217
 
218
+    @c_utils.ignore_missing_ports
217 219
     def update_port_filter(self, port):
218 220
         if not port.get('port_security_enabled'):
219 221
             LOG.info('Port %s does not have security enabled. '
@@ -264,6 +266,7 @@ class HyperVSecurityGroupsDriverMixin(object):
264 266
         self._security_ports[port['device']] = port
265 267
         self._sec_group_rules[port['id']] = added_rules[port['id']]
266 268
 
269
+    @c_utils.ignore_missing_ports
267 270
     def remove_port_filter(self, port):
268 271
         LOG.info('Removing port filter')
269 272
         self._security_ports.pop(port['device'], None)

+ 18
- 0
networking_hyperv/tests/unit/neutron/test_common_utils.py View File

@@ -14,12 +14,15 @@
14 14
 #    License for the specific language governing permissions and limitations
15 15
 #    under the License.
16 16
 
17
+import ddt
17 18
 import mock
19
+from os_win import exceptions as os_win_exc
18 20
 
19 21
 from networking_hyperv.neutron import _common_utils
20 22
 from networking_hyperv.tests import base
21 23
 
22 24
 
25
+@ddt.ddt
23 26
 class TestCommonUtils(base.BaseTestCase):
24 27
 
25 28
     @mock.patch.object(_common_utils.lockutils, 'synchronized_with_prefix')
@@ -42,3 +45,18 @@ class TestCommonUtils(base.BaseTestCase):
42 45
         mock_synchronized.assert_called_once_with(expected_lock_name)
43 46
         fake_method_side_effect.assert_called_once_with(
44 47
             mock.sentinel.arg, mock.sentinel.port_id)
48
+
49
+    @ddt.data(os_win_exc.HyperVPortNotFoundException(message='test'),
50
+              os_win_exc.HyperVvNicNotFound(message='test'))
51
+    def test_ignore_missing_ports_decorator(self, exc):
52
+        fake_method_side_effect = mock.Mock()
53
+        fake_method_side_effect.side_effect = exc
54
+
55
+        @_common_utils.ignore_missing_ports
56
+        def fake_method(fake_arg, port_id):
57
+            fake_method_side_effect(fake_arg, port_id)
58
+
59
+        fake_method(mock.sentinel.arg, mock.sentinel.port_id)
60
+
61
+        fake_method_side_effect.assert_called_once_with(
62
+            mock.sentinel.arg, mock.sentinel.port_id)

Loading…
Cancel
Save