Merge lp:~sleepsonthefloor/nova/lp783705 into lp:~hudson-openstack/nova/trunk

Proposed by Anthony Young
Status: Merged
Approved by: Devin Carlen
Approved revision: 1079
Merged at revision: 1153
Proposed branch: lp:~sleepsonthefloor/nova/lp783705
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 179 lines (+131/-2)
2 files modified
nova/tests/test_libvirt.py (+107/-0)
nova/virt/libvirt/firewall.py (+24/-2)
To merge this branch: bzr merge lp:~sleepsonthefloor/nova/lp783705
Reviewer Review Type Date Requested Status
Devin Carlen (community) Approve
Vish Ishaya (community) Approve
Review via email: mp+63437@code.launchpad.net

This proposal supersedes a proposal from 2011-05-16.

Description of the change

This branch removes nwfilter rules when instances are terminated to prevent resource leakage and serious eventual performance degradation. Without this patch, launching instances and restarting nova-compute eventually become very slow.

To post a comment you must log in.
Revision history for this message
Devin Carlen (devcamcar) wrote : Posted in a previous version of this proposal

lgtm

review: Approve
Revision history for this message
Matt Dietz (cerberus) wrote : Posted in a previous version of this proposal

I can't vouch for the implementation, but syntactically looks good. However, do you could you could maybe implement a few more tests to show handling of the exception cases?

Revision history for this message
Vish Ishaya (vishvananda) wrote : Posted in a previous version of this proposal

tests looks good, but you ned another trunk merge

review: Needs Fixing
Revision history for this message
Vish Ishaya (vishvananda) wrote :

ok looks good now

review: Approve
Revision history for this message
Devin Carlen (devcamcar) wrote :

re-lgtm

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/tests/test_libvirt.py'
2--- nova/tests/test_libvirt.py 2011-06-02 23:24:09 +0000
3+++ nova/tests/test_libvirt.py 2011-06-03 21:46:25 +0000
4@@ -724,6 +724,31 @@
5 super(LibvirtConnTestCase, self).tearDown()
6
7
8+class NWFilterFakes:
9+ def __init__(self):
10+ self.filters = {}
11+
12+ def nwfilterLookupByName(self, name):
13+ if name in self.filters:
14+ return self.filters[name]
15+ raise libvirt.libvirtError('Filter Not Found')
16+
17+ def filterDefineXMLMock(self, xml):
18+ class FakeNWFilterInternal:
19+ def __init__(self, parent, name):
20+ self.name = name
21+ self.parent = parent
22+
23+ def undefine(self):
24+ del self.parent.filters[self.name]
25+ pass
26+ tree = xml_to_tree(xml)
27+ name = tree.get('name')
28+ if name not in self.filters:
29+ self.filters[name] = FakeNWFilterInternal(self, name)
30+ return True
31+
32+
33 class IptablesFirewallTestCase(test.TestCase):
34 def setUp(self):
35 super(IptablesFirewallTestCase, self).setUp()
36@@ -741,6 +766,20 @@
37 self.fw = firewall.IptablesFirewallDriver(
38 get_connection=lambda: self.fake_libvirt_connection)
39
40+ def lazy_load_library_exists(self):
41+ """check if libvirt is available."""
42+ # try to connect libvirt. if fail, skip test.
43+ try:
44+ import libvirt
45+ import libxml2
46+ except ImportError:
47+ return False
48+ global libvirt
49+ libvirt = __import__('libvirt')
50+ connection.libvirt = __import__('libvirt')
51+ connection.libxml2 = __import__('libxml2')
52+ return True
53+
54 def tearDown(self):
55 self.manager.delete_project(self.project)
56 self.manager.delete_user(self.user)
57@@ -946,6 +985,40 @@
58 self.mox.ReplayAll()
59 self.fw.do_refresh_security_group_rules("fake")
60
61+ def test_unfilter_instance_undefines_nwfilter(self):
62+ # Skip if non-libvirt environment
63+ if not self.lazy_load_library_exists():
64+ return
65+
66+ admin_ctxt = context.get_admin_context()
67+
68+ fakefilter = NWFilterFakes()
69+ self.fw.nwfilter._conn.nwfilterDefineXML =\
70+ fakefilter.filterDefineXMLMock
71+ self.fw.nwfilter._conn.nwfilterLookupByName =\
72+ fakefilter.nwfilterLookupByName
73+
74+ instance_ref = self._create_instance_ref()
75+ inst_id = instance_ref['id']
76+ instance = db.instance_get(self.context, inst_id)
77+
78+ ip = '10.11.12.13'
79+ network_ref = db.project_get_network(self.context, 'fake')
80+ fixed_ip = {'address': ip, 'network_id': network_ref['id']}
81+ db.fixed_ip_create(admin_ctxt, fixed_ip)
82+ db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
83+ 'instance_id': inst_id})
84+ self.fw.setup_basic_filtering(instance)
85+ self.fw.prepare_instance_filter(instance)
86+ self.fw.apply_instance_filter(instance)
87+ original_filter_count = len(fakefilter.filters)
88+ self.fw.unfilter_instance(instance)
89+
90+ # should undefine just the instance filter
91+ self.assertEqual(original_filter_count - len(fakefilter.filters), 1)
92+
93+ db.instance_destroy(admin_ctxt, instance_ref['id'])
94+
95
96 class NWFilterTestCase(test.TestCase):
97 def setUp(self):
98@@ -1122,3 +1195,37 @@
99 network_info,
100 "fake")
101 self.assertEquals(len(result), 3)
102+
103+ def test_unfilter_instance_undefines_nwfilters(self):
104+ admin_ctxt = context.get_admin_context()
105+
106+ fakefilter = NWFilterFakes()
107+ self.fw._conn.nwfilterDefineXML = fakefilter.filterDefineXMLMock
108+ self.fw._conn.nwfilterLookupByName = fakefilter.nwfilterLookupByName
109+
110+ instance_ref = self._create_instance()
111+ inst_id = instance_ref['id']
112+
113+ self.security_group = self.setup_and_return_security_group()
114+
115+ db.instance_add_security_group(self.context, inst_id,
116+ self.security_group.id)
117+
118+ instance = db.instance_get(self.context, inst_id)
119+
120+ ip = '10.11.12.13'
121+ network_ref = db.project_get_network(self.context, 'fake')
122+ fixed_ip = {'address': ip, 'network_id': network_ref['id']}
123+ db.fixed_ip_create(admin_ctxt, fixed_ip)
124+ db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
125+ 'instance_id': inst_id})
126+ self.fw.setup_basic_filtering(instance)
127+ self.fw.prepare_instance_filter(instance)
128+ self.fw.apply_instance_filter(instance)
129+ original_filter_count = len(fakefilter.filters)
130+ self.fw.unfilter_instance(instance)
131+
132+ # should undefine 2 filters: instance and instance-secgroup
133+ self.assertEqual(original_filter_count - len(fakefilter.filters), 2)
134+
135+ db.instance_destroy(admin_ctxt, instance_ref['id'])
136
137=== modified file 'nova/virt/libvirt/firewall.py'
138--- nova/virt/libvirt/firewall.py 2011-05-26 22:14:38 +0000
139+++ nova/virt/libvirt/firewall.py 2011-06-03 21:46:25 +0000
140@@ -285,8 +285,29 @@
141 tpool.execute(self._conn.nwfilterDefineXML, xml)
142
143 def unfilter_instance(self, instance):
144- # Nothing to do
145- pass
146+ """Clear out the nwfilter rules."""
147+ network_info = netutils.get_network_info(instance)
148+ instance_name = instance.name
149+ for (network, mapping) in network_info:
150+ nic_id = mapping['mac'].replace(':', '')
151+ instance_filter_name = self._instance_filter_name(instance, nic_id)
152+
153+ try:
154+ self._conn.nwfilterLookupByName(instance_filter_name).\
155+ undefine()
156+ except libvirt.libvirtError:
157+ LOG.debug(_('The nwfilter(%(instance_filter_name)s) '
158+ 'for %(instance_name)s is not found.') % locals())
159+
160+ instance_secgroup_filter_name =\
161+ '%s-secgroup' % (self._instance_filter_name(instance))
162+
163+ try:
164+ self._conn.nwfilterLookupByName(instance_secgroup_filter_name)\
165+ .undefine()
166+ except libvirt.libvirtError:
167+ LOG.debug(_('The nwfilter(%(instance_secgroup_filter_name)s) '
168+ 'for %(instance_name)s is not found.') % locals())
169
170 def prepare_instance_filter(self, instance, network_info=None):
171 """
172@@ -452,6 +473,7 @@
173 if self.instances.pop(instance['id'], None):
174 self.remove_filters_for_instance(instance)
175 self.iptables.apply()
176+ self.nwfilter.unfilter_instance(instance)
177 else:
178 LOG.info(_('Attempted to unfilter instance %s which is not '
179 'filtered'), instance['id'])