Merge lp:~james-page/nova/folsom-resync into lp:~openstack-ubuntu-testing/nova/folsom

Proposed by James Page
Status: Merged
Approved by: Chuck Short
Approved revision: 479
Merged at revision: 479
Proposed branch: lp:~james-page/nova/folsom-resync
Merge into: lp:~openstack-ubuntu-testing/nova/folsom
Diff against target: 922 lines (+887/-0)
4 files modified
debian/changelog (+31/-0)
debian/patches/CVE-2013-0335.patch (+378/-0)
debian/patches/CVE-2013-1838.patch (+476/-0)
debian/patches/series (+2/-0)
To merge this branch: bzr merge lp:~james-page/nova/folsom-resync
Reviewer Review Type Date Requested Status
Openstack Ubuntu Testers Pending
Review via email: mp+154955@code.launchpad.net

Description of the change

Resync of pending SRU with security updates.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/changelog'
--- debian/changelog 2013-02-21 20:49:05 +0000
+++ debian/changelog 2013-03-22 13:42:13 +0000
@@ -12,6 +12,23 @@
1212
13 -- Adam Gandelman <adamg@ubuntu.com> Thu, 21 Feb 2013 12:40:55 -040013 -- Adam Gandelman <adamg@ubuntu.com> Thu, 21 Feb 2013 12:40:55 -0400
1414
15nova (2012.2.3-0ubuntu2) quantal-proposed; urgency=low
16
17 * Re-sync with latest security updates.
18 * SECURITY UPDATE: fix denial of service via fixed IPs when using extensions
19 - debian/patches/CVE-2013-1838.patch: add explicit quota for fixed IP
20 - CVE-2013-1838
21 * SECURITY UPDATE: fix VNC token validation
22 - debian/patches/CVE-2013-0335.patch: force console auth service to flush
23 all tokens associated with an instance when it is deleted
24 - CVE-2013-0335
25 * SECURITY UPDATE: fix denial of service
26 - CVE-2013-1664.patch: Add a new utils.safe_minidom_parse_string function
27 and update external API facing Nova modules to use it
28 - CVE-2013-1664
29
30 -- James Page <james.page@ubuntu.com> Fri, 22 Mar 2013 12:40:07 +0000
31
15nova (2012.2.3-0ubuntu1) quantal-proposed; urgency=low32nova (2012.2.3-0ubuntu1) quantal-proposed; urgency=low
1633
17 * Dropped patches, applied upstream:34 * Dropped patches, applied upstream:
@@ -45,6 +62,20 @@
4562
46 -- Adam Gandelman <adamg@ubuntu.com> Tue, 05 Feb 2013 14:11:49 -040063 -- Adam Gandelman <adamg@ubuntu.com> Tue, 05 Feb 2013 14:11:49 -0400
4764
65nova (2012.2.1+stable-20121212-a99a802e-0ubuntu1.4) quantal-security; urgency=low
66
67 * SECURITY UPDATE: fix denial of service via fixed IPs when using extensions
68 - debian/patches/CVE-2013-1838.patch: add explicit quota for fixed IP
69 - CVE-2013-1838
70 - LP: #1125468
71 * SECURITY UPDATE: fix VNC token validation
72 - debian/patches/CVE-2013-0335.patch: force console auth service to flush
73 all tokens associated with an instance when it is deleted
74 - CVE-2013-0335
75 - LP: #1125378
76
77 -- Jamie Strandboge <jamie@ubuntu.com> Wed, 20 Mar 2013 09:53:55 -0500
78
48nova (2012.2.1+stable-20121212-a99a802e-0ubuntu1.2) quantal-security; urgency=low79nova (2012.2.1+stable-20121212-a99a802e-0ubuntu1.2) quantal-security; urgency=low
4980
50 * SECURITY UPDATE: fix denial of service81 * SECURITY UPDATE: fix denial of service
5182
=== added file 'debian/patches/CVE-2013-0335.patch'
--- debian/patches/CVE-2013-0335.patch 1970-01-01 00:00:00 +0000
+++ debian/patches/CVE-2013-0335.patch 2013-03-22 13:42:13 +0000
@@ -0,0 +1,378 @@
1From: John Herndon <john.herndon@hp.com>
2Date: Fri, 22 Feb 2013 20:43:58 +0000 (+0000)
3Subject: VNC Token Validation
4X-Git-Url: https://review.openstack.org/gitweb?p=openstack%2Fnova.git;a=commitdiff_plain;h=05a3374992bc8ba53ddc9c491b51c4b59eed0a72
5
6VNC Token Validation
7
8Force console auth service to flush all tokens
9associated with an instance when it is deleted.
10This will fix a bug where the console for the
11wrong instance can be connected to via the console
12if the correct circumstances occur. This change also
13makes a call to veriry vnc console tokens when a
14user attempts to connect to a console. This ensures
15the user is connecting to the correct console.
16
17bug 1125378
18Change-Id: I0d83ec6c4dbfef1af912a200ee15f8052f72da96
19---
20
21--- a/nova/common/memorycache.py
22+++ b/nova/common/memorycache.py
23@@ -62,3 +62,8 @@ class Client(object):
24 new_value = int(value) + delta
25 self.cache[key] = (self.cache[key][0], str(new_value))
26 return new_value
27+
28+ def delete(self, key, time=0):
29+ """Deletes the value associated with a key."""
30+ if key in self.cache:
31+ del self.cache[key]
32--- a/nova/compute/api.py
33+++ b/nova/compute/api.py
34@@ -1852,7 +1852,8 @@ class API(base.Base):
35
36 self.consoleauth_rpcapi.authorize_console(context,
37 connect_info['token'], console_type, connect_info['host'],
38- connect_info['port'], connect_info['internal_access_path'])
39+ connect_info['port'], connect_info['internal_access_path'],
40+ instance["uuid"])
41
42 return {'url': connect_info['access_url']}
43
44--- a/nova/compute/manager.py
45+++ b/nova/compute/manager.py
46@@ -52,6 +52,7 @@ from nova.compute import rpcapi as compu
47 from nova.compute import task_states
48 from nova.compute import utils as compute_utils
49 from nova.compute import vm_states
50+from nova import consoleauth
51 import nova.context
52 from nova import exception
53 from nova import flags
54@@ -235,6 +236,7 @@ class ComputeManager(manager.SchedulerDe
55 self.compute_api = compute.API()
56 self.compute_rpcapi = compute_rpcapi.ComputeAPI()
57 self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
58+ self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI()
59
60 super(ComputeManager, self).__init__(service_name="compute",
61 *args, **kwargs)
62@@ -926,6 +928,10 @@ class ComputeManager(manager.SchedulerDe
63 self._notify_about_instance_usage(context, instance, "delete.end",
64 system_metadata=system_meta)
65
66+ if FLAGS.vnc_enabled:
67+ self.consoleauth_rpcapi.delete_tokens_for_instance(context,
68+ instance["uuid"])
69+
70 @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
71 @wrap_instance_fault
72 def terminate_instance(self, context, instance):
73@@ -1989,6 +1995,12 @@ class ComputeManager(manager.SchedulerDe
74 return connection_info
75
76 @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
77+ @wrap_instance_fault
78+ def validate_console_port(self, ctxt, instance, port, console_type):
79+ console_info = self.driver.get_vnc_console(instance)
80+ return console_info['port'] == port
81+
82+ @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
83 @reverts_task_state
84 @wrap_instance_fault
85 def reserve_block_device_name(self, context, instance, device):
86--- a/nova/compute/rpcapi.py
87+++ b/nova/compute/rpcapi.py
88@@ -259,6 +259,13 @@ class ComputeAPI(nova.openstack.common.r
89 instance=instance_p, console_type=console_type),
90 topic=_compute_topic(self.topic, ctxt, None, instance))
91
92+ def validate_console_port(self, ctxt, instance, port, console_type):
93+ instance_p = jsonutils.to_primitive(instance)
94+ return self.call(ctxt, self.make_msg('validate_console_port',
95+ instance=instance_p, port=port, console_type=console_type),
96+ topic=_compute_topic(self.topic, ctxt,
97+ None, instance))
98+
99 def host_maintenance_mode(self, ctxt, host_param, mode, host):
100 '''Set host maintenance mode
101
102--- a/nova/consoleauth/manager.py
103+++ b/nova/consoleauth/manager.py
104@@ -20,6 +20,8 @@
105
106 import time
107
108+from nova.compute import rpcapi as compute_rpcapi
109+from nova.db import api as db
110 from nova import flags
111 from nova import manager
112 from nova.openstack.common import cfg
113@@ -56,10 +58,21 @@ class ConsoleAuthManager(manager.Manager
114 from nova.common import memorycache as memcache
115 self.mc = memcache.Client(FLAGS.memcached_servers,
116 debug=0)
117+ self.compute_rpcapi = compute_rpcapi.ComputeAPI()
118+
119+ def _get_tokens_for_instance(self, instance_uuid):
120+ tokens_str = self.mc.get(instance_uuid.encode('UTF-8'))
121+ if not tokens_str:
122+ tokens = []
123+ else:
124+ tokens = jsonutils.loads(tokens_str)
125+ return tokens
126
127 def authorize_console(self, context, token, console_type, host, port,
128- internal_access_path):
129+ internal_access_path, instance_uuid=None):
130+
131 token_dict = {'token': token,
132+ 'instance_uuid': instance_uuid,
133 'console_type': console_type,
134 'host': host,
135 'port': port,
136@@ -67,11 +80,35 @@ class ConsoleAuthManager(manager.Manager
137 'last_activity_at': time.time()}
138 data = jsonutils.dumps(token_dict)
139 self.mc.set(token.encode('UTF-8'), data, FLAGS.console_token_ttl)
140+ if instance_uuid is not None:
141+ tokens = self._get_tokens_for_instance(instance_uuid)
142+ tokens.append(token)
143+ self.mc.set(instance_uuid.encode('UTF-8'),
144+ jsonutils.dumps(tokens))
145+
146 LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals())
147
148+ def _validate_token(self, context, token):
149+ instance_uuid = token['instance_uuid']
150+ if instance_uuid is None:
151+ return False
152+ instance = db.instance_get_by_uuid(context, instance_uuid)
153+ return self.compute_rpcapi.validate_console_port(context,
154+ instance,
155+ token['port'],
156+ token['console_type'])
157+
158 def check_token(self, context, token):
159 token_str = self.mc.get(token.encode('UTF-8'))
160 token_valid = (token_str is not None)
161 LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals())
162 if token_valid:
163- return jsonutils.loads(token_str)
164+ token = jsonutils.loads(token_str)
165+ if self._validate_token(context, token):
166+ return token
167+
168+ def delete_tokens_for_instance(self, context, instance_uuid):
169+ tokens = self._get_tokens_for_instance(instance_uuid)
170+ for token in tokens:
171+ self.mc.delete(token)
172+ self.mc.delete(instance_uuid.encode('UTF-8'))
173--- a/nova/consoleauth/rpcapi.py
174+++ b/nova/consoleauth/rpcapi.py
175@@ -49,14 +49,20 @@ class ConsoleAuthAPI(nova.openstack.comm
176 default_version=self.BASE_RPC_API_VERSION)
177
178 def authorize_console(self, ctxt, token, console_type, host, port,
179- internal_access_path):
180+ internal_access_path, instance_uuid=None):
181 # The remote side doesn't return anything, but we want to block
182 # until it completes.
183 return self.call(ctxt,
184 self.make_msg('authorize_console',
185 token=token, console_type=console_type,
186 host=host, port=port,
187- internal_access_path=internal_access_path))
188+ internal_access_path=internal_access_path,
189+ instance_uuid=instance_uuid))
190
191 def check_token(self, ctxt, token):
192 return self.call(ctxt, self.make_msg('check_token', token=token))
193+
194+ def delete_tokens_for_instance(self, ctxt, instance_uuid):
195+ return self.call(ctxt,
196+ self.make_msg('delete_tokens_for_instance',
197+ instance_uuid=instance_uuid))
198--- a/nova/tests/compute/test_compute.py
199+++ b/nova/tests/compute/test_compute.py
200@@ -1372,6 +1372,24 @@ class ComputeTestCase(BaseTestCase):
201 self.compute._delete_instance(self.context,
202 instance=jsonutils.to_primitive(instance))
203
204+ def test_delete_instance_deletes_console_auth_tokens(self):
205+ instance = self._create_fake_instance()
206+ self.flags(vnc_enabled=True)
207+
208+ self.tokens_deleted = False
209+
210+ def fake_delete_tokens(*args, **kwargs):
211+ self.tokens_deleted = True
212+
213+ cauth_rpcapi = self.compute.consoleauth_rpcapi
214+ self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance',
215+ fake_delete_tokens)
216+
217+ self.compute._delete_instance(self.context,
218+ instance=jsonutils.to_primitive(instance))
219+
220+ self.assertTrue(self.tokens_deleted)
221+
222 def test_instance_termination_exception_sets_error(self):
223 """Test that we handle InstanceTerminationFailure
224 which is propagated up from the underlying driver.
225@@ -4465,7 +4483,9 @@ class ComputeAPITestCase(BaseTestCase):
226 'console_type': fake_console_type,
227 'host': 'fake_console_host',
228 'port': 'fake_console_port',
229- 'internal_access_path': 'fake_access_path'}
230+ 'internal_access_path': 'fake_access_path',
231+ 'instance_uuid': fake_instance["uuid"]}
232+
233 fake_connect_info2 = copy.deepcopy(fake_connect_info)
234 fake_connect_info2['access_url'] = 'fake_console_url'
235
236@@ -4499,6 +4519,36 @@ class ComputeAPITestCase(BaseTestCase):
237
238 db.instance_destroy(self.context, instance['uuid'])
239
240+ def test_validate_console_port(self):
241+ self.flags(vnc_enabled=True)
242+ instance = jsonutils.to_primitive(self._create_fake_instance())
243+
244+ def fake_driver_get_console(*args, **kwargs):
245+ return {'host': "fake_host", 'port': "5900",
246+ 'internal_access_path': None}
247+ self.stubs.Set(self.compute.driver, "get_vnc_console",
248+ fake_driver_get_console)
249+
250+ self.assertTrue(self.compute.validate_console_port(self.context,
251+ instance,
252+ "5900",
253+ "novnc"))
254+
255+ def test_validate_console_port_wrong_port(self):
256+ self.flags(vnc_enabled=True)
257+ instance = jsonutils.to_primitive(self._create_fake_instance())
258+
259+ def fake_driver_get_console(*args, **kwargs):
260+ return {'host': "fake_host", 'port': "5900",
261+ 'internal_access_path': None}
262+ self.stubs.Set(self.compute.driver, "get_vnc_console",
263+ fake_driver_get_console)
264+
265+ self.assertFalse(self.compute.validate_console_port(self.context,
266+ instance,
267+ "wrongport",
268+ "novnc"))
269+
270 def test_console_output(self):
271 fake_instance = {'uuid': 'fake_uuid',
272 'host': 'fake_compute_host'}
273--- a/nova/tests/compute/test_rpcapi.py
274+++ b/nova/tests/compute/test_rpcapi.py
275@@ -168,6 +168,11 @@ class ComputeRpcAPITestCase(test.TestCas
276 self._test_compute_api('get_vnc_console', 'call',
277 instance=self.fake_instance, console_type='type')
278
279+ def test_validate_console_port(self):
280+ self._test_compute_api('validate_console_port', 'call',
281+ instance=self.fake_instance, port="5900",
282+ console_type="novnc")
283+
284 def test_host_maintenance_mode(self):
285 self._test_compute_api('host_maintenance_mode', 'call',
286 host_param='param', mode='mode', host='host')
287--- a/nova/tests/consoleauth/test_consoleauth.py
288+++ b/nova/tests/consoleauth/test_consoleauth.py
289@@ -45,8 +45,73 @@ class ConsoleauthTestCase(test.TestCase)
290 """Test that tokens expire correctly."""
291 token = 'mytok'
292 self.flags(console_token_ttl=1)
293+
294+ def fake_validate_token(*args, **kwargs):
295+ return True
296+ self.stubs.Set(self.manager,
297+ "_validate_token",
298+ fake_validate_token)
299+
300 self.manager.authorize_console(self.context, token, 'novnc',
301- '127.0.0.1', 'host', '')
302+ '127.0.0.1', '8080', 'host', "1234")
303 self.assertTrue(self.manager.check_token(self.context, token))
304 time.sleep(1.1)
305 self.assertFalse(self.manager.check_token(self.context, token))
306+
307+ def test_multiple_tokens_for_instance(self):
308+ tokens = ["token" + str(i) for i in xrange(10)]
309+ instance = "12345"
310+
311+ def fake_validate_token(*args, **kwargs):
312+ return True
313+
314+ self.stubs.Set(self.manager, "_validate_token",
315+ fake_validate_token)
316+ for token in tokens:
317+ self.manager.authorize_console(self.context, token, 'novnc',
318+ '127.0.0.1', '8080', 'host',
319+ instance)
320+
321+ for token in tokens:
322+ self.assertTrue(self.manager.check_token(self.context, token))
323+
324+ def test_delete_tokens_for_instance(self):
325+ instance = "12345"
326+ tokens = ["token" + str(i) for i in xrange(10)]
327+
328+ def fake_validate_token(*args, **kwargs):
329+ return True
330+ self.stubs.Set(self.manager, "_validate_token",
331+ fake_validate_token)
332+
333+ for token in tokens:
334+ self.manager.authorize_console(self.context, token, 'novnc',
335+ '127.0.0.1', '8080', 'host',
336+ instance)
337+ self.manager.delete_tokens_for_instance(self.context, instance)
338+ stored_tokens = self.manager._get_tokens_for_instance(instance)
339+
340+ self.assertEqual(len(stored_tokens), 0)
341+
342+ for token in tokens:
343+ self.assertFalse(self.manager.check_token(self.context, token))
344+
345+ def test_wrong_token_has_port(self):
346+ token = 'mytok'
347+
348+ def fake_validate_token(*args, **kwargs):
349+ return False
350+
351+ self.stubs.Set(self.manager, "_validate_token",
352+ fake_validate_token)
353+
354+ self.manager.authorize_console(self.context, token, 'novnc',
355+ '127.0.0.1', '8080', 'host',
356+ instance_uuid='instance')
357+ self.assertFalse(self.manager.check_token(self.context, token))
358+
359+ def test_console_no_instance_uuid(self):
360+ self.manager.authorize_console(self.context, "token", 'novnc',
361+ '127.0.0.1', '8080', 'host',
362+ instance_uuid=None)
363+ self.assertFalse(self.manager.check_token(self.context, "token"))
364--- a/nova/tests/consoleauth/test_rpcapi.py
365+++ b/nova/tests/consoleauth/test_rpcapi.py
366@@ -68,7 +68,11 @@ class ConsoleAuthRpcAPITestCase(test.Tes
367 def test_authorize_console(self):
368 self._test_consoleauth_api('authorize_console', token='token',
369 console_type='ctype', host='h', port='p',
370- internal_access_path='iap')
371+ internal_access_path='iap', instance_uuid="1234")
372
373 def test_check_token(self):
374 self._test_consoleauth_api('check_token', token='t')
375+
376+ def test_delete_tokens_for_instnace(self):
377+ self._test_consoleauth_api('delete_tokens_for_instance',
378+ instance_uuid="instance")
0379
=== added file 'debian/patches/CVE-2013-1838.patch'
--- debian/patches/CVE-2013-1838.patch 1970-01-01 00:00:00 +0000
+++ debian/patches/CVE-2013-1838.patch 2013-03-22 13:42:13 +0000
@@ -0,0 +1,476 @@
1commit dbe94187193da8741b4b8c270c62eb4d9cb0bd8a
2Author: Michael Still <mikal@stillhq.com>
3Date: Fri Mar 1 20:22:39 2013 +0000
4
5 Add quotas for fixed ips.
6
7 DocImpact: there is now a default quota of 10 fixed ips per tenant.
8 This will need to be adjusted by deployers if that number does not
9 meet their needs.
10
11 Resolves bug 1125468 for folsom.
12
13 Change-Id: I970d540cfa6a61b7e903703f845a6453ff55f225
14
15--- a/nova/db/api.py
16+++ b/nova/db/api.py
17@@ -507,6 +507,12 @@ def fixed_ip_update(context, address, va
18 """Create a fixed ip from the values dictionary."""
19 return IMPL.fixed_ip_update(context, address, values)
20
21+
22+def fixed_ip_count_by_project(context, project_id, session=None):
23+ """Count fixed ips used by project."""
24+ return IMPL.fixed_ip_count_by_project(context, project_id,
25+ session=session)
26+
27 ####################
28
29
30--- a/nova/db/sqlalchemy/api.py
31+++ b/nova/db/sqlalchemy/api.py
32@@ -1273,6 +1273,27 @@ def fixed_ip_update(context, address, va
33 fixed_ip_ref.save(session=session)
34
35
36+@require_context
37+def fixed_ip_count_by_project(context, project_id, session=None):
38+ authorize_project_context(context, project_id)
39+
40+ # NOTE(mikal): Yes I know this is horrible, but I couldn't
41+ # get a query using a join working, mainly because of a failure
42+ # to be able to express the where clause sensibly. Patches
43+ # welcome.
44+ session = get_session()
45+ with session.begin():
46+ instance_uuid_query = model_query(context, models.Instance.uuid,
47+ read_deleted="no", session=session).\
48+ filter(models.Instance.project_id == \
49+ project_id)
50+ uuid_filter = models.FixedIp.instance_uuid.in_(instance_uuid_query)
51+ return model_query(context, models.FixedIp, read_deleted="no",
52+ session=session).\
53+ filter(uuid_filter).\
54+ count()
55+
56+
57 ###################
58
59
60--- a/nova/exception.py
61+++ b/nova/exception.py
62@@ -998,6 +998,10 @@ class FloatingIpLimitExceeded(QuotaError
63 message = _("Maximum number of floating ips exceeded")
64
65
66+class FixedIpLimitExceeded(QuotaError):
67+ message = _("Maximum number of fixed ips exceeded")
68+
69+
70 class MetadataLimitExceeded(QuotaError):
71 message = _("Maximum number of metadata items exceeds %(allowed)d")
72
73--- a/nova/network/manager.py
74+++ b/nova/network/manager.py
75@@ -1294,37 +1294,53 @@ class NetworkManager(manager.SchedulerDe
76 address = None
77 instance_ref = self.db.instance_get(context, instance_id)
78
79- if network['cidr']:
80- address = kwargs.get('address', None)
81- if address:
82- address = self.db.fixed_ip_associate(context,
83- address,
84- instance_ref['uuid'],
85- network['id'])
86- else:
87- address = self.db.fixed_ip_associate_pool(context.elevated(),
88- network['id'],
89- instance_ref['uuid'])
90- self._do_trigger_security_group_members_refresh_for_instance(
91- instance_id)
92- get_vif = self.db.virtual_interface_get_by_instance_and_network
93- vif = get_vif(context, instance_ref['uuid'], network['id'])
94- values = {'allocated': True,
95- 'virtual_interface_id': vif['id']}
96- self.db.fixed_ip_update(context, address, values)
97-
98- name = instance_ref['display_name']
99-
100- if self._validate_instance_zone_for_dns_domain(context, instance_ref):
101- uuid = instance_ref['uuid']
102- self.instance_dns_manager.create_entry(name, address,
103- "A",
104- self.instance_dns_domain)
105- self.instance_dns_manager.create_entry(uuid, address,
106- "A",
107- self.instance_dns_domain)
108- self._setup_network_on_host(context, network)
109- return address
110+ # Check the quota; can't put this in the API because we get
111+ # called into from other places
112+ try:
113+ reservations = QUOTAS.reserve(context, fixed_ips=1)
114+ except exception.OverQuota:
115+ pid = context.project_id
116+ LOG.warn(_("Quota exceeded for %(pid)s, tried to allocate "
117+ "fixed IP") % locals())
118+ raise exception.FixedIpLimitExceeded()
119+
120+ try:
121+ if network['cidr']:
122+ address = kwargs.get('address', None)
123+ if address:
124+ address = self.db.fixed_ip_associate(context,
125+ address,
126+ instance_ref['uuid'],
127+ network['id'])
128+ else:
129+ address = self.db.fixed_ip_associate_pool(
130+ context.elevated(), network['id'],
131+ instance_ref['uuid'])
132+ self._do_trigger_security_group_members_refresh_for_instance(
133+ instance_id)
134+ get_vif = self.db.virtual_interface_get_by_instance_and_network
135+ vif = get_vif(context, instance_ref['uuid'], network['id'])
136+ values = {'allocated': True,
137+ 'virtual_interface_id': vif['id']}
138+ self.db.fixed_ip_update(context, address, values)
139+
140+ name = instance_ref['display_name']
141+
142+ if self._validate_instance_zone_for_dns_domain(context,
143+ instance_ref):
144+ uuid = instance_ref['uuid']
145+ self.instance_dns_manager.create_entry(
146+ name, address, "A", self.instance_dns_domain)
147+ self.instance_dns_manager.create_entry(
148+ uuid, address, "A", self.instance_dns_domain)
149+ self._setup_network_on_host(context, network)
150+
151+ QUOTAS.commit(context, reservations)
152+ return address
153+
154+ except Exception:
155+ with excutils.save_and_reraise_exception():
156+ QUOTAS.rollback(context, reservations)
157
158 def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
159 """Returns a fixed ip to the pool."""
160@@ -1334,6 +1350,13 @@ class NetworkManager(manager.SchedulerDe
161 context.elevated(read_deleted='yes'),
162 fixed_ip_ref['instance_uuid'])
163
164+ try:
165+ reservations = QUOTAS.reserve(context, fixed_ips=-1)
166+ except Exception:
167+ reservations = None
168+ LOG.exception(_("Failed to update usages deallocating "
169+ "fixed IP"))
170+
171 self._do_trigger_security_group_members_refresh_for_instance(
172 instance['uuid'])
173
174@@ -1373,6 +1396,10 @@ class NetworkManager(manager.SchedulerDe
175 # callback will get called by nova-dhcpbridge.
176 self.driver.release_dhcp(dev, address, vif['address'])
177
178+ # Commit the reservations
179+ if reservations:
180+ QUOTAS.commit(context, reservations)
181+
182 def lease_fixed_ip(self, context, address):
183 """Called by dhcp-bridge when ip is leased."""
184 LOG.debug(_('Leased IP |%(address)s|'), locals(), context=context)
185--- a/nova/quota.py
186+++ b/nova/quota.py
187@@ -50,6 +50,10 @@ quota_opts = [
188 cfg.IntOpt('quota_floating_ips',
189 default=10,
190 help='number of floating ips allowed per project'),
191+ cfg.IntOpt('quota_fixed_ips',
192+ default=10,
193+ help=('number of fixed ips allowed per project (this should be '
194+ 'at least the number of instances allowed)')),
195 cfg.IntOpt('quota_metadata_items',
196 default=128,
197 help='number of metadata items allowed per instance'),
198@@ -778,6 +782,11 @@ def _sync_floating_ips(context, project_
199 context, project_id, session=session))
200
201
202+def _sync_fixed_ips(context, project_id, session):
203+ return dict(fixed_ips=db.fixed_ip_count_by_project(
204+ context, project_id, session=session))
205+
206+
207 def _sync_security_groups(context, project_id, session):
208 return dict(security_groups=db.security_group_count_by_project(
209 context, project_id, session=session))
210@@ -794,6 +803,7 @@ resources = [
211 ReservableResource('gigabytes', _sync_volumes, 'quota_gigabytes'),
212 ReservableResource('floating_ips', _sync_floating_ips,
213 'quota_floating_ips'),
214+ ReservableResource('fixed_ips', _sync_fixed_ips, 'quota_fixed_ips'),
215 AbsoluteResource('metadata_items', 'quota_metadata_items'),
216 AbsoluteResource('injected_files', 'quota_injected_files'),
217 AbsoluteResource('injected_file_content_bytes',
218--- a/nova/tests/api/openstack/compute/contrib/test_quota_classes.py
219+++ b/nova/tests/api/openstack/compute/contrib/test_quota_classes.py
220@@ -25,10 +25,11 @@ from nova.tests.api.openstack import fak
221 def quota_set(class_name):
222 return {'quota_class_set': {'id': class_name, 'metadata_items': 128,
223 'volumes': 10, 'gigabytes': 1000, 'ram': 51200,
224- 'floating_ips': 10, 'instances': 10, 'injected_files': 5,
225- 'cores': 20, 'injected_file_content_bytes': 10240,
226- 'security_groups': 10, 'security_group_rules': 20,
227- 'key_pairs': 100, 'injected_file_path_bytes': 255}}
228+ 'floating_ips': 10, 'fixed_ips': 10, 'instances': 10,
229+ 'injected_files': 5, 'cores': 20,
230+ 'injected_file_content_bytes': 10240, 'security_groups': 10,
231+ 'security_group_rules': 20, 'key_pairs': 100,
232+ 'injected_file_path_bytes': 255}}
233
234
235 class QuotaClassSetsTest(test.TestCase):
236@@ -44,6 +45,7 @@ class QuotaClassSetsTest(test.TestCase):
237 'ram': 51200,
238 'volumes': 10,
239 'floating_ips': 10,
240+ 'fixed_ips': 10,
241 'metadata_items': 128,
242 'gigabytes': 1000,
243 'injected_files': 5,
244@@ -91,7 +93,8 @@ class QuotaClassSetsTest(test.TestCase):
245 body = {'quota_class_set': {'instances': 50, 'cores': 50,
246 'ram': 51200, 'volumes': 10,
247 'gigabytes': 1000, 'floating_ips': 10,
248- 'metadata_items': 128, 'injected_files': 5,
249+ 'fixed_ips': 10, 'metadata_items': 128,
250+ 'injected_files': 5,
251 'injected_file_content_bytes': 10240,
252 'injected_file_path_bytes': 255,
253 'security_groups': 10,
254@@ -139,6 +142,7 @@ class QuotaTemplateXMLSerializerTest(tes
255 gigabytes=40,
256 ram=50,
257 floating_ips=60,
258+ fixed_ips=10,
259 instances=70,
260 injected_files=80,
261 security_groups=10,
262--- a/nova/tests/api/openstack/compute/contrib/test_quotas.py
263+++ b/nova/tests/api/openstack/compute/contrib/test_quotas.py
264@@ -26,11 +26,12 @@ from nova.tests.api.openstack import fak
265
266 def quota_set(id):
267 return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10,
268- 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
269- 'instances': 10, 'injected_files': 5, 'cores': 20,
270- 'injected_file_content_bytes': 10240,
271- 'security_groups': 10, 'security_group_rules': 20,
272- 'key_pairs': 100, 'injected_file_path_bytes': 255}}
273+ 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
274+ 'fixed_ips': 10, 'instances': 10,
275+ 'injected_files': 5, 'cores': 20,
276+ 'injected_file_content_bytes': 10240,
277+ 'security_groups': 10, 'security_group_rules': 20,
278+ 'key_pairs': 100, 'injected_file_path_bytes': 255}}
279
280
281 class QuotaSetsTest(test.TestCase):
282@@ -46,6 +47,7 @@ class QuotaSetsTest(test.TestCase):
283 'ram': 51200,
284 'volumes': 10,
285 'floating_ips': 10,
286+ 'fixed_ips': 10,
287 'metadata_items': 128,
288 'gigabytes': 1000,
289 'injected_files': 5,
290@@ -88,6 +90,7 @@ class QuotaSetsTest(test.TestCase):
291 'volumes': 10,
292 'gigabytes': 1000,
293 'floating_ips': 10,
294+ 'fixed_ips': 10,
295 'metadata_items': 128,
296 'injected_files': 5,
297 'injected_file_path_bytes': 255,
298@@ -120,7 +123,7 @@ class QuotaSetsTest(test.TestCase):
299 'injected_file_path_bytes': 255,
300 'security_groups': 10,
301 'security_group_rules': 20,
302- 'key_pairs': 100}}
303+ 'key_pairs': 100, 'fixed_ips': 10}}
304
305 req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/update_me',
306 use_admin_context=True)
307@@ -171,6 +174,7 @@ class QuotaXMLSerializerTest(test.TestCa
308 gigabytes=40,
309 ram=50,
310 floating_ips=60,
311+ fixed_ips=10,
312 instances=70,
313 injected_files=80,
314 security_groups=10,
315--- a/nova/tests/network/test_manager.py
316+++ b/nova/tests/network/test_manager.py
317@@ -30,6 +30,7 @@ from nova.openstack.common import import
318 from nova.openstack.common import log as logging
319 from nova.openstack.common import rpc
320 import nova.policy
321+from nova import quota
322 from nova import test
323 from nova.tests import fake_network
324 from nova import utils
325@@ -278,6 +279,7 @@ class FlatNetworkTestCase(test.TestCase)
326 self.mox.StubOutWithMock(db,
327 'virtual_interface_get_by_instance_and_network')
328 self.mox.StubOutWithMock(db, 'fixed_ip_update')
329+ self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
330
331 db.fixed_ip_update(mox.IgnoreArg(),
332 mox.IgnoreArg(),
333@@ -291,6 +293,10 @@ class FlatNetworkTestCase(test.TestCase)
334 db.instance_get(mox.IgnoreArg(),
335 mox.IgnoreArg()).AndReturn({'security_groups':
336 [{'id': 0}]})
337+
338+ quota.QUOTAS.reserve(mox.IgnoreArg(),
339+ fixed_ips=mox.IgnoreArg()).AndReturn(None)
340+
341 db.fixed_ip_associate_pool(mox.IgnoreArg(),
342 mox.IgnoreArg(),
343 mox.IgnoreArg()).AndReturn('192.168.0.101')
344@@ -310,6 +316,7 @@ class FlatNetworkTestCase(test.TestCase)
345 self.mox.StubOutWithMock(db,
346 'virtual_interface_get_by_instance_and_network')
347 self.mox.StubOutWithMock(db, 'fixed_ip_update')
348+ self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
349
350 db.fixed_ip_update(mox.IgnoreArg(),
351 mox.IgnoreArg(),
352@@ -323,6 +330,10 @@ class FlatNetworkTestCase(test.TestCase)
353 db.instance_get(mox.IgnoreArg(),
354 mox.IgnoreArg()).AndReturn({'security_groups':
355 [{'id': 0}]})
356+
357+ quota.QUOTAS.reserve(mox.IgnoreArg(),
358+ fixed_ips=mox.IgnoreArg()).AndReturn(None)
359+
360 db.fixed_ip_associate_pool(mox.IgnoreArg(),
361 mox.IgnoreArg(),
362 mox.IgnoreArg()).AndReturn('192.168.0.101')
363@@ -376,6 +387,7 @@ class FlatNetworkTestCase(test.TestCase)
364 self.mox.StubOutWithMock(db,
365 'virtual_interface_get_by_instance_and_network')
366 self.mox.StubOutWithMock(db, 'fixed_ip_update')
367+ self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
368
369 db.fixed_ip_update(mox.IgnoreArg(),
370 mox.IgnoreArg(),
371@@ -390,6 +402,9 @@ class FlatNetworkTestCase(test.TestCase)
372 mox.IgnoreArg()).AndReturn({'security_groups':
373 [{'id': 0}]})
374
375+ quota.QUOTAS.reserve(mox.IgnoreArg(),
376+ fixed_ips=mox.IgnoreArg()).AndReturn(None)
377+
378 db.fixed_ip_associate_pool(mox.IgnoreArg(),
379 mox.IgnoreArg(),
380 mox.IgnoreArg()).AndReturn(fixedip)
381--- a/nova/tests/test_quota.py
382+++ b/nova/tests/test_quota.py
383@@ -723,6 +723,7 @@ class DbQuotaDriverTestCase(test.TestCas
384 quota_volumes=10,
385 quota_gigabytes=1000,
386 quota_floating_ips=10,
387+ quota_fixed_ips=10,
388 quota_metadata_items=128,
389 quota_injected_files=5,
390 quota_injected_file_content_bytes=10 * 1024,
391@@ -755,6 +756,7 @@ class DbQuotaDriverTestCase(test.TestCas
392 volumes=10,
393 gigabytes=1000,
394 floating_ips=10,
395+ fixed_ips=10,
396 metadata_items=128,
397 injected_files=5,
398 injected_file_content_bytes=10 * 1024,
399@@ -791,6 +793,7 @@ class DbQuotaDriverTestCase(test.TestCas
400 volumes=10,
401 gigabytes=500,
402 floating_ips=10,
403+ fixed_ips=10,
404 metadata_items=64,
405 injected_files=5,
406 injected_file_content_bytes=5 * 1024,
407@@ -847,6 +850,7 @@ class DbQuotaDriverTestCase(test.TestCas
408 self._stub_quota_class_get_all_by_name()
409
410 def test_get_project_quotas(self):
411+ self.maxDiff = None
412 self._stub_get_by_project()
413 result = self.driver.get_project_quotas(
414 FakeContext('test_project', 'test_class'),
415@@ -888,6 +892,11 @@ class DbQuotaDriverTestCase(test.TestCas
416 in_use=2,
417 reserved=0,
418 ),
419+ fixed_ips=dict(
420+ limit=10,
421+ in_use=0,
422+ reserved=0,
423+ ),
424 metadata_items=dict(
425 limit=64,
426 in_use=0,
427@@ -926,6 +935,7 @@ class DbQuotaDriverTestCase(test.TestCas
428 ))
429
430 def test_get_project_quotas_alt_context_no_class(self):
431+ self.maxDiff = None
432 self._stub_get_by_project()
433 result = self.driver.get_project_quotas(
434 FakeContext('other_project', 'other_class'),
435@@ -966,6 +976,11 @@ class DbQuotaDriverTestCase(test.TestCas
436 in_use=2,
437 reserved=0,
438 ),
439+ fixed_ips=dict(
440+ limit=10,
441+ in_use=0,
442+ reserved=0,
443+ ),
444 metadata_items=dict(
445 limit=128,
446 in_use=0,
447@@ -1004,6 +1019,7 @@ class DbQuotaDriverTestCase(test.TestCas
448 ))
449
450 def test_get_project_quotas_alt_context_with_class(self):
451+ self.maxDiff = None
452 self._stub_get_by_project()
453 result = self.driver.get_project_quotas(
454 FakeContext('other_project', 'other_class'),
455@@ -1045,6 +1061,11 @@ class DbQuotaDriverTestCase(test.TestCas
456 in_use=2,
457 reserved=0,
458 ),
459+ fixed_ips=dict(
460+ limit=10,
461+ in_use=0,
462+ reserved=0,
463+ ),
464 metadata_items=dict(
465 limit=64,
466 in_use=0,
467@@ -1145,6 +1166,9 @@ class DbQuotaDriverTestCase(test.TestCas
468 floating_ips=dict(
469 limit=10,
470 ),
471+ fixed_ips=dict(
472+ limit=10,
473+ ),
474 metadata_items=dict(
475 limit=64,
476 ),
0477
=== modified file 'debian/patches/series'
--- debian/patches/series 2012-11-02 13:33:39 +0000
+++ debian/patches/series 2013-03-22 13:42:13 +0000
@@ -4,3 +4,5 @@
4fix-docs-build-without-network.patch4fix-docs-build-without-network.patch
5avoid_setuptools_git_dependency.patch5avoid_setuptools_git_dependency.patch
6rbd-security.patch6rbd-security.patch
7CVE-2013-1838.patch
8CVE-2013-0335.patch

Subscribers

People subscribed via source and target branches