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

Proposed by James Page on 2013-03-22
Status: Merged
Approved by: Chuck Short on 2013-03-22
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 2013-03-22 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
1=== modified file 'debian/changelog'
2--- debian/changelog 2013-02-21 20:49:05 +0000
3+++ debian/changelog 2013-03-22 13:42:13 +0000
4@@ -12,6 +12,23 @@
5
6 -- Adam Gandelman <adamg@ubuntu.com> Thu, 21 Feb 2013 12:40:55 -0400
7
8+nova (2012.2.3-0ubuntu2) quantal-proposed; urgency=low
9+
10+ * Re-sync with latest security updates.
11+ * SECURITY UPDATE: fix denial of service via fixed IPs when using extensions
12+ - debian/patches/CVE-2013-1838.patch: add explicit quota for fixed IP
13+ - CVE-2013-1838
14+ * SECURITY UPDATE: fix VNC token validation
15+ - debian/patches/CVE-2013-0335.patch: force console auth service to flush
16+ all tokens associated with an instance when it is deleted
17+ - CVE-2013-0335
18+ * SECURITY UPDATE: fix denial of service
19+ - CVE-2013-1664.patch: Add a new utils.safe_minidom_parse_string function
20+ and update external API facing Nova modules to use it
21+ - CVE-2013-1664
22+
23+ -- James Page <james.page@ubuntu.com> Fri, 22 Mar 2013 12:40:07 +0000
24+
25 nova (2012.2.3-0ubuntu1) quantal-proposed; urgency=low
26
27 * Dropped patches, applied upstream:
28@@ -45,6 +62,20 @@
29
30 -- Adam Gandelman <adamg@ubuntu.com> Tue, 05 Feb 2013 14:11:49 -0400
31
32+nova (2012.2.1+stable-20121212-a99a802e-0ubuntu1.4) quantal-security; urgency=low
33+
34+ * SECURITY UPDATE: fix denial of service via fixed IPs when using extensions
35+ - debian/patches/CVE-2013-1838.patch: add explicit quota for fixed IP
36+ - CVE-2013-1838
37+ - LP: #1125468
38+ * SECURITY UPDATE: fix VNC token validation
39+ - debian/patches/CVE-2013-0335.patch: force console auth service to flush
40+ all tokens associated with an instance when it is deleted
41+ - CVE-2013-0335
42+ - LP: #1125378
43+
44+ -- Jamie Strandboge <jamie@ubuntu.com> Wed, 20 Mar 2013 09:53:55 -0500
45+
46 nova (2012.2.1+stable-20121212-a99a802e-0ubuntu1.2) quantal-security; urgency=low
47
48 * SECURITY UPDATE: fix denial of service
49
50=== added file 'debian/patches/CVE-2013-0335.patch'
51--- debian/patches/CVE-2013-0335.patch 1970-01-01 00:00:00 +0000
52+++ debian/patches/CVE-2013-0335.patch 2013-03-22 13:42:13 +0000
53@@ -0,0 +1,378 @@
54+From: John Herndon <john.herndon@hp.com>
55+Date: Fri, 22 Feb 2013 20:43:58 +0000 (+0000)
56+Subject: VNC Token Validation
57+X-Git-Url: https://review.openstack.org/gitweb?p=openstack%2Fnova.git;a=commitdiff_plain;h=05a3374992bc8ba53ddc9c491b51c4b59eed0a72
58+
59+VNC Token Validation
60+
61+Force console auth service to flush all tokens
62+associated with an instance when it is deleted.
63+This will fix a bug where the console for the
64+wrong instance can be connected to via the console
65+if the correct circumstances occur. This change also
66+makes a call to veriry vnc console tokens when a
67+user attempts to connect to a console. This ensures
68+the user is connecting to the correct console.
69+
70+bug 1125378
71+Change-Id: I0d83ec6c4dbfef1af912a200ee15f8052f72da96
72+---
73+
74+--- a/nova/common/memorycache.py
75++++ b/nova/common/memorycache.py
76+@@ -62,3 +62,8 @@ class Client(object):
77+ new_value = int(value) + delta
78+ self.cache[key] = (self.cache[key][0], str(new_value))
79+ return new_value
80++
81++ def delete(self, key, time=0):
82++ """Deletes the value associated with a key."""
83++ if key in self.cache:
84++ del self.cache[key]
85+--- a/nova/compute/api.py
86++++ b/nova/compute/api.py
87+@@ -1852,7 +1852,8 @@ class API(base.Base):
88+
89+ self.consoleauth_rpcapi.authorize_console(context,
90+ connect_info['token'], console_type, connect_info['host'],
91+- connect_info['port'], connect_info['internal_access_path'])
92++ connect_info['port'], connect_info['internal_access_path'],
93++ instance["uuid"])
94+
95+ return {'url': connect_info['access_url']}
96+
97+--- a/nova/compute/manager.py
98++++ b/nova/compute/manager.py
99+@@ -52,6 +52,7 @@ from nova.compute import rpcapi as compu
100+ from nova.compute import task_states
101+ from nova.compute import utils as compute_utils
102+ from nova.compute import vm_states
103++from nova import consoleauth
104+ import nova.context
105+ from nova import exception
106+ from nova import flags
107+@@ -235,6 +236,7 @@ class ComputeManager(manager.SchedulerDe
108+ self.compute_api = compute.API()
109+ self.compute_rpcapi = compute_rpcapi.ComputeAPI()
110+ self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
111++ self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI()
112+
113+ super(ComputeManager, self).__init__(service_name="compute",
114+ *args, **kwargs)
115+@@ -926,6 +928,10 @@ class ComputeManager(manager.SchedulerDe
116+ self._notify_about_instance_usage(context, instance, "delete.end",
117+ system_metadata=system_meta)
118+
119++ if FLAGS.vnc_enabled:
120++ self.consoleauth_rpcapi.delete_tokens_for_instance(context,
121++ instance["uuid"])
122++
123+ @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
124+ @wrap_instance_fault
125+ def terminate_instance(self, context, instance):
126+@@ -1989,6 +1995,12 @@ class ComputeManager(manager.SchedulerDe
127+ return connection_info
128+
129+ @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
130++ @wrap_instance_fault
131++ def validate_console_port(self, ctxt, instance, port, console_type):
132++ console_info = self.driver.get_vnc_console(instance)
133++ return console_info['port'] == port
134++
135++ @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
136+ @reverts_task_state
137+ @wrap_instance_fault
138+ def reserve_block_device_name(self, context, instance, device):
139+--- a/nova/compute/rpcapi.py
140++++ b/nova/compute/rpcapi.py
141+@@ -259,6 +259,13 @@ class ComputeAPI(nova.openstack.common.r
142+ instance=instance_p, console_type=console_type),
143+ topic=_compute_topic(self.topic, ctxt, None, instance))
144+
145++ def validate_console_port(self, ctxt, instance, port, console_type):
146++ instance_p = jsonutils.to_primitive(instance)
147++ return self.call(ctxt, self.make_msg('validate_console_port',
148++ instance=instance_p, port=port, console_type=console_type),
149++ topic=_compute_topic(self.topic, ctxt,
150++ None, instance))
151++
152+ def host_maintenance_mode(self, ctxt, host_param, mode, host):
153+ '''Set host maintenance mode
154+
155+--- a/nova/consoleauth/manager.py
156++++ b/nova/consoleauth/manager.py
157+@@ -20,6 +20,8 @@
158+
159+ import time
160+
161++from nova.compute import rpcapi as compute_rpcapi
162++from nova.db import api as db
163+ from nova import flags
164+ from nova import manager
165+ from nova.openstack.common import cfg
166+@@ -56,10 +58,21 @@ class ConsoleAuthManager(manager.Manager
167+ from nova.common import memorycache as memcache
168+ self.mc = memcache.Client(FLAGS.memcached_servers,
169+ debug=0)
170++ self.compute_rpcapi = compute_rpcapi.ComputeAPI()
171++
172++ def _get_tokens_for_instance(self, instance_uuid):
173++ tokens_str = self.mc.get(instance_uuid.encode('UTF-8'))
174++ if not tokens_str:
175++ tokens = []
176++ else:
177++ tokens = jsonutils.loads(tokens_str)
178++ return tokens
179+
180+ def authorize_console(self, context, token, console_type, host, port,
181+- internal_access_path):
182++ internal_access_path, instance_uuid=None):
183++
184+ token_dict = {'token': token,
185++ 'instance_uuid': instance_uuid,
186+ 'console_type': console_type,
187+ 'host': host,
188+ 'port': port,
189+@@ -67,11 +80,35 @@ class ConsoleAuthManager(manager.Manager
190+ 'last_activity_at': time.time()}
191+ data = jsonutils.dumps(token_dict)
192+ self.mc.set(token.encode('UTF-8'), data, FLAGS.console_token_ttl)
193++ if instance_uuid is not None:
194++ tokens = self._get_tokens_for_instance(instance_uuid)
195++ tokens.append(token)
196++ self.mc.set(instance_uuid.encode('UTF-8'),
197++ jsonutils.dumps(tokens))
198++
199+ LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals())
200+
201++ def _validate_token(self, context, token):
202++ instance_uuid = token['instance_uuid']
203++ if instance_uuid is None:
204++ return False
205++ instance = db.instance_get_by_uuid(context, instance_uuid)
206++ return self.compute_rpcapi.validate_console_port(context,
207++ instance,
208++ token['port'],
209++ token['console_type'])
210++
211+ def check_token(self, context, token):
212+ token_str = self.mc.get(token.encode('UTF-8'))
213+ token_valid = (token_str is not None)
214+ LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals())
215+ if token_valid:
216+- return jsonutils.loads(token_str)
217++ token = jsonutils.loads(token_str)
218++ if self._validate_token(context, token):
219++ return token
220++
221++ def delete_tokens_for_instance(self, context, instance_uuid):
222++ tokens = self._get_tokens_for_instance(instance_uuid)
223++ for token in tokens:
224++ self.mc.delete(token)
225++ self.mc.delete(instance_uuid.encode('UTF-8'))
226+--- a/nova/consoleauth/rpcapi.py
227++++ b/nova/consoleauth/rpcapi.py
228+@@ -49,14 +49,20 @@ class ConsoleAuthAPI(nova.openstack.comm
229+ default_version=self.BASE_RPC_API_VERSION)
230+
231+ def authorize_console(self, ctxt, token, console_type, host, port,
232+- internal_access_path):
233++ internal_access_path, instance_uuid=None):
234+ # The remote side doesn't return anything, but we want to block
235+ # until it completes.
236+ return self.call(ctxt,
237+ self.make_msg('authorize_console',
238+ token=token, console_type=console_type,
239+ host=host, port=port,
240+- internal_access_path=internal_access_path))
241++ internal_access_path=internal_access_path,
242++ instance_uuid=instance_uuid))
243+
244+ def check_token(self, ctxt, token):
245+ return self.call(ctxt, self.make_msg('check_token', token=token))
246++
247++ def delete_tokens_for_instance(self, ctxt, instance_uuid):
248++ return self.call(ctxt,
249++ self.make_msg('delete_tokens_for_instance',
250++ instance_uuid=instance_uuid))
251+--- a/nova/tests/compute/test_compute.py
252++++ b/nova/tests/compute/test_compute.py
253+@@ -1372,6 +1372,24 @@ class ComputeTestCase(BaseTestCase):
254+ self.compute._delete_instance(self.context,
255+ instance=jsonutils.to_primitive(instance))
256+
257++ def test_delete_instance_deletes_console_auth_tokens(self):
258++ instance = self._create_fake_instance()
259++ self.flags(vnc_enabled=True)
260++
261++ self.tokens_deleted = False
262++
263++ def fake_delete_tokens(*args, **kwargs):
264++ self.tokens_deleted = True
265++
266++ cauth_rpcapi = self.compute.consoleauth_rpcapi
267++ self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance',
268++ fake_delete_tokens)
269++
270++ self.compute._delete_instance(self.context,
271++ instance=jsonutils.to_primitive(instance))
272++
273++ self.assertTrue(self.tokens_deleted)
274++
275+ def test_instance_termination_exception_sets_error(self):
276+ """Test that we handle InstanceTerminationFailure
277+ which is propagated up from the underlying driver.
278+@@ -4465,7 +4483,9 @@ class ComputeAPITestCase(BaseTestCase):
279+ 'console_type': fake_console_type,
280+ 'host': 'fake_console_host',
281+ 'port': 'fake_console_port',
282+- 'internal_access_path': 'fake_access_path'}
283++ 'internal_access_path': 'fake_access_path',
284++ 'instance_uuid': fake_instance["uuid"]}
285++
286+ fake_connect_info2 = copy.deepcopy(fake_connect_info)
287+ fake_connect_info2['access_url'] = 'fake_console_url'
288+
289+@@ -4499,6 +4519,36 @@ class ComputeAPITestCase(BaseTestCase):
290+
291+ db.instance_destroy(self.context, instance['uuid'])
292+
293++ def test_validate_console_port(self):
294++ self.flags(vnc_enabled=True)
295++ instance = jsonutils.to_primitive(self._create_fake_instance())
296++
297++ def fake_driver_get_console(*args, **kwargs):
298++ return {'host': "fake_host", 'port': "5900",
299++ 'internal_access_path': None}
300++ self.stubs.Set(self.compute.driver, "get_vnc_console",
301++ fake_driver_get_console)
302++
303++ self.assertTrue(self.compute.validate_console_port(self.context,
304++ instance,
305++ "5900",
306++ "novnc"))
307++
308++ def test_validate_console_port_wrong_port(self):
309++ self.flags(vnc_enabled=True)
310++ instance = jsonutils.to_primitive(self._create_fake_instance())
311++
312++ def fake_driver_get_console(*args, **kwargs):
313++ return {'host': "fake_host", 'port': "5900",
314++ 'internal_access_path': None}
315++ self.stubs.Set(self.compute.driver, "get_vnc_console",
316++ fake_driver_get_console)
317++
318++ self.assertFalse(self.compute.validate_console_port(self.context,
319++ instance,
320++ "wrongport",
321++ "novnc"))
322++
323+ def test_console_output(self):
324+ fake_instance = {'uuid': 'fake_uuid',
325+ 'host': 'fake_compute_host'}
326+--- a/nova/tests/compute/test_rpcapi.py
327++++ b/nova/tests/compute/test_rpcapi.py
328+@@ -168,6 +168,11 @@ class ComputeRpcAPITestCase(test.TestCas
329+ self._test_compute_api('get_vnc_console', 'call',
330+ instance=self.fake_instance, console_type='type')
331+
332++ def test_validate_console_port(self):
333++ self._test_compute_api('validate_console_port', 'call',
334++ instance=self.fake_instance, port="5900",
335++ console_type="novnc")
336++
337+ def test_host_maintenance_mode(self):
338+ self._test_compute_api('host_maintenance_mode', 'call',
339+ host_param='param', mode='mode', host='host')
340+--- a/nova/tests/consoleauth/test_consoleauth.py
341++++ b/nova/tests/consoleauth/test_consoleauth.py
342+@@ -45,8 +45,73 @@ class ConsoleauthTestCase(test.TestCase)
343+ """Test that tokens expire correctly."""
344+ token = 'mytok'
345+ self.flags(console_token_ttl=1)
346++
347++ def fake_validate_token(*args, **kwargs):
348++ return True
349++ self.stubs.Set(self.manager,
350++ "_validate_token",
351++ fake_validate_token)
352++
353+ self.manager.authorize_console(self.context, token, 'novnc',
354+- '127.0.0.1', 'host', '')
355++ '127.0.0.1', '8080', 'host', "1234")
356+ self.assertTrue(self.manager.check_token(self.context, token))
357+ time.sleep(1.1)
358+ self.assertFalse(self.manager.check_token(self.context, token))
359++
360++ def test_multiple_tokens_for_instance(self):
361++ tokens = ["token" + str(i) for i in xrange(10)]
362++ instance = "12345"
363++
364++ def fake_validate_token(*args, **kwargs):
365++ return True
366++
367++ self.stubs.Set(self.manager, "_validate_token",
368++ fake_validate_token)
369++ for token in tokens:
370++ self.manager.authorize_console(self.context, token, 'novnc',
371++ '127.0.0.1', '8080', 'host',
372++ instance)
373++
374++ for token in tokens:
375++ self.assertTrue(self.manager.check_token(self.context, token))
376++
377++ def test_delete_tokens_for_instance(self):
378++ instance = "12345"
379++ tokens = ["token" + str(i) for i in xrange(10)]
380++
381++ def fake_validate_token(*args, **kwargs):
382++ return True
383++ self.stubs.Set(self.manager, "_validate_token",
384++ fake_validate_token)
385++
386++ for token in tokens:
387++ self.manager.authorize_console(self.context, token, 'novnc',
388++ '127.0.0.1', '8080', 'host',
389++ instance)
390++ self.manager.delete_tokens_for_instance(self.context, instance)
391++ stored_tokens = self.manager._get_tokens_for_instance(instance)
392++
393++ self.assertEqual(len(stored_tokens), 0)
394++
395++ for token in tokens:
396++ self.assertFalse(self.manager.check_token(self.context, token))
397++
398++ def test_wrong_token_has_port(self):
399++ token = 'mytok'
400++
401++ def fake_validate_token(*args, **kwargs):
402++ return False
403++
404++ self.stubs.Set(self.manager, "_validate_token",
405++ fake_validate_token)
406++
407++ self.manager.authorize_console(self.context, token, 'novnc',
408++ '127.0.0.1', '8080', 'host',
409++ instance_uuid='instance')
410++ self.assertFalse(self.manager.check_token(self.context, token))
411++
412++ def test_console_no_instance_uuid(self):
413++ self.manager.authorize_console(self.context, "token", 'novnc',
414++ '127.0.0.1', '8080', 'host',
415++ instance_uuid=None)
416++ self.assertFalse(self.manager.check_token(self.context, "token"))
417+--- a/nova/tests/consoleauth/test_rpcapi.py
418++++ b/nova/tests/consoleauth/test_rpcapi.py
419+@@ -68,7 +68,11 @@ class ConsoleAuthRpcAPITestCase(test.Tes
420+ def test_authorize_console(self):
421+ self._test_consoleauth_api('authorize_console', token='token',
422+ console_type='ctype', host='h', port='p',
423+- internal_access_path='iap')
424++ internal_access_path='iap', instance_uuid="1234")
425+
426+ def test_check_token(self):
427+ self._test_consoleauth_api('check_token', token='t')
428++
429++ def test_delete_tokens_for_instnace(self):
430++ self._test_consoleauth_api('delete_tokens_for_instance',
431++ instance_uuid="instance")
432
433=== added file 'debian/patches/CVE-2013-1838.patch'
434--- debian/patches/CVE-2013-1838.patch 1970-01-01 00:00:00 +0000
435+++ debian/patches/CVE-2013-1838.patch 2013-03-22 13:42:13 +0000
436@@ -0,0 +1,476 @@
437+commit dbe94187193da8741b4b8c270c62eb4d9cb0bd8a
438+Author: Michael Still <mikal@stillhq.com>
439+Date: Fri Mar 1 20:22:39 2013 +0000
440+
441+ Add quotas for fixed ips.
442+
443+ DocImpact: there is now a default quota of 10 fixed ips per tenant.
444+ This will need to be adjusted by deployers if that number does not
445+ meet their needs.
446+
447+ Resolves bug 1125468 for folsom.
448+
449+ Change-Id: I970d540cfa6a61b7e903703f845a6453ff55f225
450+
451+--- a/nova/db/api.py
452++++ b/nova/db/api.py
453+@@ -507,6 +507,12 @@ def fixed_ip_update(context, address, va
454+ """Create a fixed ip from the values dictionary."""
455+ return IMPL.fixed_ip_update(context, address, values)
456+
457++
458++def fixed_ip_count_by_project(context, project_id, session=None):
459++ """Count fixed ips used by project."""
460++ return IMPL.fixed_ip_count_by_project(context, project_id,
461++ session=session)
462++
463+ ####################
464+
465+
466+--- a/nova/db/sqlalchemy/api.py
467++++ b/nova/db/sqlalchemy/api.py
468+@@ -1273,6 +1273,27 @@ def fixed_ip_update(context, address, va
469+ fixed_ip_ref.save(session=session)
470+
471+
472++@require_context
473++def fixed_ip_count_by_project(context, project_id, session=None):
474++ authorize_project_context(context, project_id)
475++
476++ # NOTE(mikal): Yes I know this is horrible, but I couldn't
477++ # get a query using a join working, mainly because of a failure
478++ # to be able to express the where clause sensibly. Patches
479++ # welcome.
480++ session = get_session()
481++ with session.begin():
482++ instance_uuid_query = model_query(context, models.Instance.uuid,
483++ read_deleted="no", session=session).\
484++ filter(models.Instance.project_id == \
485++ project_id)
486++ uuid_filter = models.FixedIp.instance_uuid.in_(instance_uuid_query)
487++ return model_query(context, models.FixedIp, read_deleted="no",
488++ session=session).\
489++ filter(uuid_filter).\
490++ count()
491++
492++
493+ ###################
494+
495+
496+--- a/nova/exception.py
497++++ b/nova/exception.py
498+@@ -998,6 +998,10 @@ class FloatingIpLimitExceeded(QuotaError
499+ message = _("Maximum number of floating ips exceeded")
500+
501+
502++class FixedIpLimitExceeded(QuotaError):
503++ message = _("Maximum number of fixed ips exceeded")
504++
505++
506+ class MetadataLimitExceeded(QuotaError):
507+ message = _("Maximum number of metadata items exceeds %(allowed)d")
508+
509+--- a/nova/network/manager.py
510++++ b/nova/network/manager.py
511+@@ -1294,37 +1294,53 @@ class NetworkManager(manager.SchedulerDe
512+ address = None
513+ instance_ref = self.db.instance_get(context, instance_id)
514+
515+- if network['cidr']:
516+- address = kwargs.get('address', None)
517+- if address:
518+- address = self.db.fixed_ip_associate(context,
519+- address,
520+- instance_ref['uuid'],
521+- network['id'])
522+- else:
523+- address = self.db.fixed_ip_associate_pool(context.elevated(),
524+- network['id'],
525+- instance_ref['uuid'])
526+- self._do_trigger_security_group_members_refresh_for_instance(
527+- instance_id)
528+- get_vif = self.db.virtual_interface_get_by_instance_and_network
529+- vif = get_vif(context, instance_ref['uuid'], network['id'])
530+- values = {'allocated': True,
531+- 'virtual_interface_id': vif['id']}
532+- self.db.fixed_ip_update(context, address, values)
533+-
534+- name = instance_ref['display_name']
535+-
536+- if self._validate_instance_zone_for_dns_domain(context, instance_ref):
537+- uuid = instance_ref['uuid']
538+- self.instance_dns_manager.create_entry(name, address,
539+- "A",
540+- self.instance_dns_domain)
541+- self.instance_dns_manager.create_entry(uuid, address,
542+- "A",
543+- self.instance_dns_domain)
544+- self._setup_network_on_host(context, network)
545+- return address
546++ # Check the quota; can't put this in the API because we get
547++ # called into from other places
548++ try:
549++ reservations = QUOTAS.reserve(context, fixed_ips=1)
550++ except exception.OverQuota:
551++ pid = context.project_id
552++ LOG.warn(_("Quota exceeded for %(pid)s, tried to allocate "
553++ "fixed IP") % locals())
554++ raise exception.FixedIpLimitExceeded()
555++
556++ try:
557++ if network['cidr']:
558++ address = kwargs.get('address', None)
559++ if address:
560++ address = self.db.fixed_ip_associate(context,
561++ address,
562++ instance_ref['uuid'],
563++ network['id'])
564++ else:
565++ address = self.db.fixed_ip_associate_pool(
566++ context.elevated(), network['id'],
567++ instance_ref['uuid'])
568++ self._do_trigger_security_group_members_refresh_for_instance(
569++ instance_id)
570++ get_vif = self.db.virtual_interface_get_by_instance_and_network
571++ vif = get_vif(context, instance_ref['uuid'], network['id'])
572++ values = {'allocated': True,
573++ 'virtual_interface_id': vif['id']}
574++ self.db.fixed_ip_update(context, address, values)
575++
576++ name = instance_ref['display_name']
577++
578++ if self._validate_instance_zone_for_dns_domain(context,
579++ instance_ref):
580++ uuid = instance_ref['uuid']
581++ self.instance_dns_manager.create_entry(
582++ name, address, "A", self.instance_dns_domain)
583++ self.instance_dns_manager.create_entry(
584++ uuid, address, "A", self.instance_dns_domain)
585++ self._setup_network_on_host(context, network)
586++
587++ QUOTAS.commit(context, reservations)
588++ return address
589++
590++ except Exception:
591++ with excutils.save_and_reraise_exception():
592++ QUOTAS.rollback(context, reservations)
593+
594+ def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
595+ """Returns a fixed ip to the pool."""
596+@@ -1334,6 +1350,13 @@ class NetworkManager(manager.SchedulerDe
597+ context.elevated(read_deleted='yes'),
598+ fixed_ip_ref['instance_uuid'])
599+
600++ try:
601++ reservations = QUOTAS.reserve(context, fixed_ips=-1)
602++ except Exception:
603++ reservations = None
604++ LOG.exception(_("Failed to update usages deallocating "
605++ "fixed IP"))
606++
607+ self._do_trigger_security_group_members_refresh_for_instance(
608+ instance['uuid'])
609+
610+@@ -1373,6 +1396,10 @@ class NetworkManager(manager.SchedulerDe
611+ # callback will get called by nova-dhcpbridge.
612+ self.driver.release_dhcp(dev, address, vif['address'])
613+
614++ # Commit the reservations
615++ if reservations:
616++ QUOTAS.commit(context, reservations)
617++
618+ def lease_fixed_ip(self, context, address):
619+ """Called by dhcp-bridge when ip is leased."""
620+ LOG.debug(_('Leased IP |%(address)s|'), locals(), context=context)
621+--- a/nova/quota.py
622++++ b/nova/quota.py
623+@@ -50,6 +50,10 @@ quota_opts = [
624+ cfg.IntOpt('quota_floating_ips',
625+ default=10,
626+ help='number of floating ips allowed per project'),
627++ cfg.IntOpt('quota_fixed_ips',
628++ default=10,
629++ help=('number of fixed ips allowed per project (this should be '
630++ 'at least the number of instances allowed)')),
631+ cfg.IntOpt('quota_metadata_items',
632+ default=128,
633+ help='number of metadata items allowed per instance'),
634+@@ -778,6 +782,11 @@ def _sync_floating_ips(context, project_
635+ context, project_id, session=session))
636+
637+
638++def _sync_fixed_ips(context, project_id, session):
639++ return dict(fixed_ips=db.fixed_ip_count_by_project(
640++ context, project_id, session=session))
641++
642++
643+ def _sync_security_groups(context, project_id, session):
644+ return dict(security_groups=db.security_group_count_by_project(
645+ context, project_id, session=session))
646+@@ -794,6 +803,7 @@ resources = [
647+ ReservableResource('gigabytes', _sync_volumes, 'quota_gigabytes'),
648+ ReservableResource('floating_ips', _sync_floating_ips,
649+ 'quota_floating_ips'),
650++ ReservableResource('fixed_ips', _sync_fixed_ips, 'quota_fixed_ips'),
651+ AbsoluteResource('metadata_items', 'quota_metadata_items'),
652+ AbsoluteResource('injected_files', 'quota_injected_files'),
653+ AbsoluteResource('injected_file_content_bytes',
654+--- a/nova/tests/api/openstack/compute/contrib/test_quota_classes.py
655++++ b/nova/tests/api/openstack/compute/contrib/test_quota_classes.py
656+@@ -25,10 +25,11 @@ from nova.tests.api.openstack import fak
657+ def quota_set(class_name):
658+ return {'quota_class_set': {'id': class_name, 'metadata_items': 128,
659+ 'volumes': 10, 'gigabytes': 1000, 'ram': 51200,
660+- 'floating_ips': 10, 'instances': 10, 'injected_files': 5,
661+- 'cores': 20, 'injected_file_content_bytes': 10240,
662+- 'security_groups': 10, 'security_group_rules': 20,
663+- 'key_pairs': 100, 'injected_file_path_bytes': 255}}
664++ 'floating_ips': 10, 'fixed_ips': 10, 'instances': 10,
665++ 'injected_files': 5, 'cores': 20,
666++ 'injected_file_content_bytes': 10240, 'security_groups': 10,
667++ 'security_group_rules': 20, 'key_pairs': 100,
668++ 'injected_file_path_bytes': 255}}
669+
670+
671+ class QuotaClassSetsTest(test.TestCase):
672+@@ -44,6 +45,7 @@ class QuotaClassSetsTest(test.TestCase):
673+ 'ram': 51200,
674+ 'volumes': 10,
675+ 'floating_ips': 10,
676++ 'fixed_ips': 10,
677+ 'metadata_items': 128,
678+ 'gigabytes': 1000,
679+ 'injected_files': 5,
680+@@ -91,7 +93,8 @@ class QuotaClassSetsTest(test.TestCase):
681+ body = {'quota_class_set': {'instances': 50, 'cores': 50,
682+ 'ram': 51200, 'volumes': 10,
683+ 'gigabytes': 1000, 'floating_ips': 10,
684+- 'metadata_items': 128, 'injected_files': 5,
685++ 'fixed_ips': 10, 'metadata_items': 128,
686++ 'injected_files': 5,
687+ 'injected_file_content_bytes': 10240,
688+ 'injected_file_path_bytes': 255,
689+ 'security_groups': 10,
690+@@ -139,6 +142,7 @@ class QuotaTemplateXMLSerializerTest(tes
691+ gigabytes=40,
692+ ram=50,
693+ floating_ips=60,
694++ fixed_ips=10,
695+ instances=70,
696+ injected_files=80,
697+ security_groups=10,
698+--- a/nova/tests/api/openstack/compute/contrib/test_quotas.py
699++++ b/nova/tests/api/openstack/compute/contrib/test_quotas.py
700+@@ -26,11 +26,12 @@ from nova.tests.api.openstack import fak
701+
702+ def quota_set(id):
703+ return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10,
704+- 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
705+- 'instances': 10, 'injected_files': 5, 'cores': 20,
706+- 'injected_file_content_bytes': 10240,
707+- 'security_groups': 10, 'security_group_rules': 20,
708+- 'key_pairs': 100, 'injected_file_path_bytes': 255}}
709++ 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
710++ 'fixed_ips': 10, 'instances': 10,
711++ 'injected_files': 5, 'cores': 20,
712++ 'injected_file_content_bytes': 10240,
713++ 'security_groups': 10, 'security_group_rules': 20,
714++ 'key_pairs': 100, 'injected_file_path_bytes': 255}}
715+
716+
717+ class QuotaSetsTest(test.TestCase):
718+@@ -46,6 +47,7 @@ class QuotaSetsTest(test.TestCase):
719+ 'ram': 51200,
720+ 'volumes': 10,
721+ 'floating_ips': 10,
722++ 'fixed_ips': 10,
723+ 'metadata_items': 128,
724+ 'gigabytes': 1000,
725+ 'injected_files': 5,
726+@@ -88,6 +90,7 @@ class QuotaSetsTest(test.TestCase):
727+ 'volumes': 10,
728+ 'gigabytes': 1000,
729+ 'floating_ips': 10,
730++ 'fixed_ips': 10,
731+ 'metadata_items': 128,
732+ 'injected_files': 5,
733+ 'injected_file_path_bytes': 255,
734+@@ -120,7 +123,7 @@ class QuotaSetsTest(test.TestCase):
735+ 'injected_file_path_bytes': 255,
736+ 'security_groups': 10,
737+ 'security_group_rules': 20,
738+- 'key_pairs': 100}}
739++ 'key_pairs': 100, 'fixed_ips': 10}}
740+
741+ req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/update_me',
742+ use_admin_context=True)
743+@@ -171,6 +174,7 @@ class QuotaXMLSerializerTest(test.TestCa
744+ gigabytes=40,
745+ ram=50,
746+ floating_ips=60,
747++ fixed_ips=10,
748+ instances=70,
749+ injected_files=80,
750+ security_groups=10,
751+--- a/nova/tests/network/test_manager.py
752++++ b/nova/tests/network/test_manager.py
753+@@ -30,6 +30,7 @@ from nova.openstack.common import import
754+ from nova.openstack.common import log as logging
755+ from nova.openstack.common import rpc
756+ import nova.policy
757++from nova import quota
758+ from nova import test
759+ from nova.tests import fake_network
760+ from nova import utils
761+@@ -278,6 +279,7 @@ class FlatNetworkTestCase(test.TestCase)
762+ self.mox.StubOutWithMock(db,
763+ 'virtual_interface_get_by_instance_and_network')
764+ self.mox.StubOutWithMock(db, 'fixed_ip_update')
765++ self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
766+
767+ db.fixed_ip_update(mox.IgnoreArg(),
768+ mox.IgnoreArg(),
769+@@ -291,6 +293,10 @@ class FlatNetworkTestCase(test.TestCase)
770+ db.instance_get(mox.IgnoreArg(),
771+ mox.IgnoreArg()).AndReturn({'security_groups':
772+ [{'id': 0}]})
773++
774++ quota.QUOTAS.reserve(mox.IgnoreArg(),
775++ fixed_ips=mox.IgnoreArg()).AndReturn(None)
776++
777+ db.fixed_ip_associate_pool(mox.IgnoreArg(),
778+ mox.IgnoreArg(),
779+ mox.IgnoreArg()).AndReturn('192.168.0.101')
780+@@ -310,6 +316,7 @@ class FlatNetworkTestCase(test.TestCase)
781+ self.mox.StubOutWithMock(db,
782+ 'virtual_interface_get_by_instance_and_network')
783+ self.mox.StubOutWithMock(db, 'fixed_ip_update')
784++ self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
785+
786+ db.fixed_ip_update(mox.IgnoreArg(),
787+ mox.IgnoreArg(),
788+@@ -323,6 +330,10 @@ class FlatNetworkTestCase(test.TestCase)
789+ db.instance_get(mox.IgnoreArg(),
790+ mox.IgnoreArg()).AndReturn({'security_groups':
791+ [{'id': 0}]})
792++
793++ quota.QUOTAS.reserve(mox.IgnoreArg(),
794++ fixed_ips=mox.IgnoreArg()).AndReturn(None)
795++
796+ db.fixed_ip_associate_pool(mox.IgnoreArg(),
797+ mox.IgnoreArg(),
798+ mox.IgnoreArg()).AndReturn('192.168.0.101')
799+@@ -376,6 +387,7 @@ class FlatNetworkTestCase(test.TestCase)
800+ self.mox.StubOutWithMock(db,
801+ 'virtual_interface_get_by_instance_and_network')
802+ self.mox.StubOutWithMock(db, 'fixed_ip_update')
803++ self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
804+
805+ db.fixed_ip_update(mox.IgnoreArg(),
806+ mox.IgnoreArg(),
807+@@ -390,6 +402,9 @@ class FlatNetworkTestCase(test.TestCase)
808+ mox.IgnoreArg()).AndReturn({'security_groups':
809+ [{'id': 0}]})
810+
811++ quota.QUOTAS.reserve(mox.IgnoreArg(),
812++ fixed_ips=mox.IgnoreArg()).AndReturn(None)
813++
814+ db.fixed_ip_associate_pool(mox.IgnoreArg(),
815+ mox.IgnoreArg(),
816+ mox.IgnoreArg()).AndReturn(fixedip)
817+--- a/nova/tests/test_quota.py
818++++ b/nova/tests/test_quota.py
819+@@ -723,6 +723,7 @@ class DbQuotaDriverTestCase(test.TestCas
820+ quota_volumes=10,
821+ quota_gigabytes=1000,
822+ quota_floating_ips=10,
823++ quota_fixed_ips=10,
824+ quota_metadata_items=128,
825+ quota_injected_files=5,
826+ quota_injected_file_content_bytes=10 * 1024,
827+@@ -755,6 +756,7 @@ class DbQuotaDriverTestCase(test.TestCas
828+ volumes=10,
829+ gigabytes=1000,
830+ floating_ips=10,
831++ fixed_ips=10,
832+ metadata_items=128,
833+ injected_files=5,
834+ injected_file_content_bytes=10 * 1024,
835+@@ -791,6 +793,7 @@ class DbQuotaDriverTestCase(test.TestCas
836+ volumes=10,
837+ gigabytes=500,
838+ floating_ips=10,
839++ fixed_ips=10,
840+ metadata_items=64,
841+ injected_files=5,
842+ injected_file_content_bytes=5 * 1024,
843+@@ -847,6 +850,7 @@ class DbQuotaDriverTestCase(test.TestCas
844+ self._stub_quota_class_get_all_by_name()
845+
846+ def test_get_project_quotas(self):
847++ self.maxDiff = None
848+ self._stub_get_by_project()
849+ result = self.driver.get_project_quotas(
850+ FakeContext('test_project', 'test_class'),
851+@@ -888,6 +892,11 @@ class DbQuotaDriverTestCase(test.TestCas
852+ in_use=2,
853+ reserved=0,
854+ ),
855++ fixed_ips=dict(
856++ limit=10,
857++ in_use=0,
858++ reserved=0,
859++ ),
860+ metadata_items=dict(
861+ limit=64,
862+ in_use=0,
863+@@ -926,6 +935,7 @@ class DbQuotaDriverTestCase(test.TestCas
864+ ))
865+
866+ def test_get_project_quotas_alt_context_no_class(self):
867++ self.maxDiff = None
868+ self._stub_get_by_project()
869+ result = self.driver.get_project_quotas(
870+ FakeContext('other_project', 'other_class'),
871+@@ -966,6 +976,11 @@ class DbQuotaDriverTestCase(test.TestCas
872+ in_use=2,
873+ reserved=0,
874+ ),
875++ fixed_ips=dict(
876++ limit=10,
877++ in_use=0,
878++ reserved=0,
879++ ),
880+ metadata_items=dict(
881+ limit=128,
882+ in_use=0,
883+@@ -1004,6 +1019,7 @@ class DbQuotaDriverTestCase(test.TestCas
884+ ))
885+
886+ def test_get_project_quotas_alt_context_with_class(self):
887++ self.maxDiff = None
888+ self._stub_get_by_project()
889+ result = self.driver.get_project_quotas(
890+ FakeContext('other_project', 'other_class'),
891+@@ -1045,6 +1061,11 @@ class DbQuotaDriverTestCase(test.TestCas
892+ in_use=2,
893+ reserved=0,
894+ ),
895++ fixed_ips=dict(
896++ limit=10,
897++ in_use=0,
898++ reserved=0,
899++ ),
900+ metadata_items=dict(
901+ limit=64,
902+ in_use=0,
903+@@ -1145,6 +1166,9 @@ class DbQuotaDriverTestCase(test.TestCas
904+ floating_ips=dict(
905+ limit=10,
906+ ),
907++ fixed_ips=dict(
908++ limit=10,
909++ ),
910+ metadata_items=dict(
911+ limit=64,
912+ ),
913
914=== modified file 'debian/patches/series'
915--- debian/patches/series 2012-11-02 13:33:39 +0000
916+++ debian/patches/series 2013-03-22 13:42:13 +0000
917@@ -4,3 +4,5 @@
918 fix-docs-build-without-network.patch
919 avoid_setuptools_git_dependency.patch
920 rbd-security.patch
921+CVE-2013-1838.patch
922+CVE-2013-0335.patch

Subscribers

People subscribed via source and target branches