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