summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Smith <dansmith@redhat.com>2014-07-03 08:09:39 -0700
committerIhar Hrachyshka <ihrachys@redhat.com>2014-08-19 23:35:25 +0200
commitd469733e59872a897737db6ed5fd92d57986aee4 (patch)
treebb09e6a4caa49e7b40fa300bf2786539205e1971
parent8f0e58aff9d5e52e0567910738d685ccc2dc7b52 (diff)
downloadnova-d469733e59872a897737db6ed5fd92d57986aee4.tar.gz
Avoid re-adding iptables rules for instances that have disappeared
The remove_filters_for_instance() method fails silently if the instance's chain is gone (i.e. it's been deleted). If this happens while we're refreshing security group rules, we will not notice this case and re-add stale rules for an old instance, breaking our firewall for new instances. This adds a quick check after we've captured the lock to see if the associated chain exists, and bails if it doesn't. Conflicts: nova/virt/firewall.py Required changes: - unit test was updated because in Havana, IptablesFirewallDriver has .instances instead of .instance_infos, and a separate .network_infos attribute. Change-Id: Ic75988939f82de49735d85fe99a9eecd4baf45c9 Related-bug: #1182131 (cherry picked from commit 6aa368b99249d01f8fd7183c15d11986ad6a6fb7) (cherry picked from commit 132d5a2064cfdc579a2e51f07e10e8eafbdf6327)
-rw-r--r--nova/network/linux_net.py6
-rw-r--r--nova/tests/virt/libvirt/test_libvirt.py24
-rw-r--r--nova/virt/firewall.py7
3 files changed, 37 insertions, 0 deletions
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 5183ed50d4..7552b1a217 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -185,6 +185,12 @@ class IptablesTable(object):
self.remove_chains = set()
self.dirty = True
+ def has_chain(self, name, wrap=True):
+ if wrap:
+ return name in self.chains
+ else:
+ return name in self.unwrapped_chains
+
def add_chain(self, name, wrap=True):
"""Adds a named chain to the table.
diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py
index b88bcac053..6f57b1a2ef 100644
--- a/nova/tests/virt/libvirt/test_libvirt.py
+++ b/nova/tests/virt/libvirt/test_libvirt.py
@@ -6163,6 +6163,8 @@ class IptablesFirewallTestCase(test.TestCase):
self.mox.StubOutWithMock(self.fw,
'add_filters_for_instance',
use_mock_anything=True)
+ self.mox.StubOutWithMock(self.fw.iptables.ipv4['filter'],
+ 'has_chain')
self.fw.instance_rules(instance_ref,
mox.IgnoreArg()).AndReturn((None, None))
@@ -6170,6 +6172,8 @@ class IptablesFirewallTestCase(test.TestCase):
mox.IgnoreArg())
self.fw.instance_rules(instance_ref,
mox.IgnoreArg()).AndReturn((None, None))
+ self.fw.iptables.ipv4['filter'].has_chain(mox.IgnoreArg()
+ ).AndReturn(True)
self.fw.add_filters_for_instance(instance_ref, mox.IgnoreArg(),
mox.IgnoreArg())
self.mox.ReplayAll()
@@ -6178,6 +6182,26 @@ class IptablesFirewallTestCase(test.TestCase):
self.fw.instances[instance_ref['id']] = instance_ref
self.fw.do_refresh_security_group_rules("fake")
+ def test_do_refresh_security_group_rules_instance_disappeared(self):
+ instance1 = {'id': 0, 'uuid': 'fake-uuid1'}
+ instance2 = {'id': 1, 'uuid': 'fake-uuid2'}
+ self.fw.instances = {0: instance1, 1: instance2}
+ self.fw.network_infos = _fake_network_info(self.stubs, 2)
+ mock_filter = mock.MagicMock()
+ with mock.patch.dict(self.fw.iptables.ipv4, {'filter': mock_filter}):
+ mock_filter.has_chain.return_value = False
+ with mock.patch.object(self.fw, 'instance_rules') as mock_ir:
+ mock_ir.return_value = (None, None)
+ self.fw.do_refresh_security_group_rules('secgroup')
+ self.assertEqual(2, mock_ir.call_count)
+ # NOTE(danms): Make sure that it is checking has_chain each time,
+ # continuing to process all the instances, and never adding the
+ # new chains back if has_chain() is False
+ mock_filter.has_chain.assert_has_calls([mock.call('inst-0'),
+ mock.call('inst-1')],
+ any_order=True)
+ self.assertEqual(0, mock_filter.add_chain.call_count)
+
def test_unfilter_instance_undefines_nwfilter(self):
admin_ctxt = context.get_admin_context()
diff --git a/nova/virt/firewall.py b/nova/virt/firewall.py
index 1c1cd0aab1..f4929c3d50 100644
--- a/nova/virt/firewall.py
+++ b/nova/virt/firewall.py
@@ -441,6 +441,13 @@ class IptablesFirewallDriver(FirewallDriver):
@utils.synchronized('iptables', external=True)
def _inner_do_refresh_rules(self, instance, ipv4_rules,
ipv6_rules):
+ chain_name = self._instance_chain_name(instance)
+ if not self.iptables.ipv4['filter'].has_chain(chain_name):
+ LOG.info(
+ _('instance chain %s disappeared during refresh, '
+ 'skipping') % chain_name,
+ instance=instance)
+ return
self.remove_filters_for_instance(instance)
self.add_filters_for_instance(instance, ipv4_rules, ipv6_rules)