File tree Expand file tree Collapse file tree 4 files changed +55
-2
lines changed Expand file tree Collapse file tree 4 files changed +55
-2
lines changed Original file line number Diff line number Diff line change @@ -260,7 +260,22 @@ def ensure_namespace(self, name):
260
260
return ip
261
261
262
262
def namespace_is_empty (self ):
263
- return not self .get_devices ()
263
+ try :
264
+ return not self .get_devices ()
265
+ except OSError as e :
266
+ # This can happen if we previously got terminated in the middle of
267
+ # removing this namespace. In this case the bind mount of the
268
+ # namespace under /var/run/netns will be removed, but the namespace
269
+ # file is still there. As the bind mount is gone we can no longer
270
+ # access the namespace to validate that it is empty. But since it
271
+ # should have already been removed we are sure that the check has
272
+ # passed the last time and since the namespace is unuseable that
273
+ # can not have changed.
274
+ # Future calls to pyroute2 to remove that namespace will clean up
275
+ # the leftover file.
276
+ if e .errno == errno .EINVAL :
277
+ return True
278
+ raise e
264
279
265
280
def garbage_collect_namespace (self ):
266
281
"""Conditionally destroy the namespace if it is empty."""
Original file line number Diff line number Diff line change @@ -371,7 +371,10 @@ def sync(self):
371
371
ns .startswith (NS_PREFIX ) and
372
372
ns not in metadata_namespaces ]
373
373
for ns in unused_namespaces :
374
- self .teardown_datapath (self ._get_datapath_name (ns ))
374
+ try :
375
+ self .teardown_datapath (self ._get_datapath_name (ns ))
376
+ except Exception :
377
+ LOG .exception ('Error unable to destroy namespace: %s' , ns )
375
378
376
379
# resync all network namespaces based on the associated datapaths,
377
380
# even those that are already running. This is to make sure
Original file line number Diff line number Diff line change @@ -357,6 +357,21 @@ def test_garbage_collect_namespace_existing_not_empty(self):
357
357
self .assertNotIn (mock .call ().delete ('ns' ),
358
358
ip_ns_cmd_cls .mock_calls )
359
359
360
+ def test_garbage_collect_namespace_existing_broken (self ):
361
+ with mock .patch .object (ip_lib , 'IpNetnsCommand' ) as ip_ns_cmd_cls :
362
+ ip_ns_cmd_cls .return_value .exists .return_value = True
363
+
364
+ ip = ip_lib .IPWrapper (namespace = 'ns' )
365
+
366
+ with mock .patch .object (ip , 'get_devices' ,
367
+ side_effect = OSError (errno .EINVAL , None )
368
+ ) as mock_get_devices :
369
+ self .assertTrue (ip .garbage_collect_namespace ())
370
+
371
+ mock_get_devices .assert_called_once_with ()
372
+ expected = [mock .call ().delete ('ns' )]
373
+ ip_ns_cmd_cls .assert_has_calls (expected )
374
+
360
375
@mock .patch .object (priv_lib , 'create_interface' )
361
376
def test_add_vlan (self , create ):
362
377
retval = ip_lib .IPWrapper ().add_vlan ('eth0.1' , 'eth0' , '1' )
Original file line number Diff line number Diff line change @@ -124,6 +124,26 @@ def test_sync_teardown_namespace(self):
124
124
lnn .assert_called_once_with ()
125
125
tdp .assert_called_once_with ('3' )
126
126
127
+ def test_sync_teardown_namespace_does_not_crash_on_error (self ):
128
+ """Test that sync tears down unneeded metadata namespaces.
129
+ Even if that fails it continues to provision other datapaths
130
+ """
131
+ with mock .patch .object (
132
+ self .agent , 'provision_datapath' ) as pdp ,\
133
+ mock .patch .object (
134
+ ip_lib , 'list_network_namespaces' ,
135
+ return_value = ['ovnmeta-1' , 'ovnmeta-2' , 'ovnmeta-3' ,
136
+ 'ns1' , 'ns2' ]) as lnn ,\
137
+ mock .patch .object (
138
+ self .agent , 'teardown_datapath' ,
139
+ side_effect = Exception ()) as tdp :
140
+ self .agent .sync ()
141
+
142
+ pdp .assert_has_calls ([mock .call (p ) for p in self .ports ],
143
+ any_order = True )
144
+ lnn .assert_called_once_with ()
145
+ tdp .assert_called_once_with ('3' )
146
+
127
147
def test_get_networks_port_bindings (self ):
128
148
"""Test get_networks_port_bindings returns only the port binding
129
149
objects for ports with VIF type empty ('') or 'external'.
You can’t perform that action at this time.
0 commit comments