Merge lp:~billy-olsen/nova/kilo+lp1457517 into lp:~ubuntu-server-dev/nova/kilo
- kilo+lp1457517
- Merge into kilo
Proposed by
Billy Olsen
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 781 | ||||
Proposed branch: | lp:~billy-olsen/nova/kilo+lp1457517 | ||||
Merge into: | lp:~ubuntu-server-dev/nova/kilo | ||||
Diff against target: |
524 lines (+500/-0) 3 files modified
debian/changelog (+5/-0) debian/patches/not-check-disk-size.patch (+494/-0) debian/patches/series (+1/-0) |
||||
To merge this branch: | bzr merge lp:~billy-olsen/nova/kilo+lp1457517 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Server Developers | Pending | ||
Review via email: mp+270466@code.launchpad.net |
Commit message
Description of the change
This is a resubmission of Liang's branch for including the upstream patch for not considering a flavor's volume size when booting from volume. His original branch is located at lp:~cbjchen/nova/kilo-sru-lp1457517 but he's currently unavailable.
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 2015-08-18 21:07:07 +0000 | |||
3 | +++ debian/changelog 2015-09-08 23:22:49 +0000 | |||
4 | @@ -1,7 +1,12 @@ | |||
5 | 1 | nova (1:2015.1.1-0ubuntu2) UNRELEASED; urgency=medium | 1 | nova (1:2015.1.1-0ubuntu2) UNRELEASED; urgency=medium |
6 | 2 | 2 | ||
7 | 3 | [ Corey Bryant ] | ||
8 | 3 | * d/rules: Prevent dh_python2 from guessing dependencies. | 4 | * d/rules: Prevent dh_python2 from guessing dependencies. |
9 | 4 | 5 | ||
10 | 6 | [ Liang Chen ] | ||
11 | 7 | * d/p/not-check-disk-size.patch: Fix booting from volume error | ||
12 | 8 | when flavor disk too small (LP: #1457517) | ||
13 | 9 | |||
14 | 5 | -- Corey Bryant <corey.bryant@canonical.com> Thu, 13 Aug 2015 15:13:43 -0400 | 10 | -- Corey Bryant <corey.bryant@canonical.com> Thu, 13 Aug 2015 15:13:43 -0400 |
15 | 6 | 11 | ||
16 | 7 | nova (1:2015.1.1-0ubuntu1) vivid; urgency=medium | 12 | nova (1:2015.1.1-0ubuntu1) vivid; urgency=medium |
17 | 8 | 13 | ||
18 | === added file 'debian/patches/not-check-disk-size.patch' | |||
19 | --- debian/patches/not-check-disk-size.patch 1970-01-01 00:00:00 +0000 | |||
20 | +++ debian/patches/not-check-disk-size.patch 2015-09-08 23:22:49 +0000 | |||
21 | @@ -0,0 +1,494 @@ | |||
22 | 1 | From 8794b938dcb983b7c918718807c2396cb255b4ce Mon Sep 17 00:00:00 2001 | ||
23 | 2 | From: Matthew Booth <mbooth@redhat.com> | ||
24 | 3 | Date: Wed, 22 Jul 2015 14:56:52 +0100 | ||
25 | 4 | Subject: [PATCH 1/1] Don't check flavor disk size when booting from volume | ||
26 | 5 | |||
27 | 6 | When creating a volume from an image, cinder copies the image metadata | ||
28 | 7 | into volume properties. When booting from the volume, we read this | ||
29 | 8 | metadata from the volume and use it as image metadata once again. | ||
30 | 9 | |||
31 | 10 | While fixing the check against min_ram, | ||
32 | 11 | change I861a78b5c7efa71e4bf7206d388b8d0d8048c78e introduced a | ||
33 | 12 | regression which prevents a user from booting a volume which is larger | ||
34 | 13 | than the flavor's disk. As we are not creating this disk, this check | ||
35 | 14 | does not make sense. Similarly, it checks the image metadata's | ||
36 | 15 | min_disk against the flavor disk size, which is not being used. | ||
37 | 16 | |||
38 | 17 | This change leaves the image metadata check unaltered when creating a | ||
39 | 18 | flavor disk. When booting from a volume, we check min_disk from image | ||
40 | 19 | metadata against the actual size of the volume. We don't check the | ||
41 | 20 | volume size at all. The check against min_ram is retained unaltered. | ||
42 | 21 | |||
43 | 22 | Closes-Bug: #1457517 | ||
44 | 23 | Closes-Bug: #1459491 | ||
45 | 24 | Closes-Bug: #1466305 | ||
46 | 25 | Change-Id: I264493172da20b664df571e32876030246c2a87c | ||
47 | 26 | (cherry picked from commit 642c986f0636d52a9ba279c87e25082b4aa9b3b8) | ||
48 | 27 | --- | ||
49 | 28 | nova/compute/api.py | 101 +++++++++++++++----- | ||
50 | 29 | nova/tests/unit/compute/test_compute.py | 127 +++++++++++++++++++++++--- | ||
51 | 30 | nova/tests/unit/compute/test_compute_api.py | 11 ++- | ||
52 | 31 | nova/tests/unit/compute/test_compute_cells.py | 8 +- | ||
53 | 32 | 4 files changed, 203 insertions(+), 44 deletions(-) | ||
54 | 33 | |||
55 | 34 | diff --git a/nova/compute/api.py b/nova/compute/api.py | ||
56 | 35 | index 6634918..5937895 100644 | ||
57 | 36 | --- a/nova/compute/api.py | ||
58 | 37 | +++ b/nova/compute/api.py | ||
59 | 38 | @@ -651,7 +651,8 @@ class API(base.Base): | ||
60 | 39 | # reason, we rely on the DB to cast True to a String. | ||
61 | 40 | return True if bool_val else '' | ||
62 | 41 | |||
63 | 42 | - def _check_requested_image(self, context, image_id, image, instance_type): | ||
64 | 43 | + def _check_requested_image(self, context, image_id, image, | ||
65 | 44 | + instance_type, root_bdm): | ||
66 | 45 | if not image: | ||
67 | 46 | return | ||
68 | 47 | |||
69 | 48 | @@ -668,15 +669,63 @@ class API(base.Base): | ||
70 | 49 | if instance_type['memory_mb'] < int(image.get('min_ram') or 0): | ||
71 | 50 | raise exception.FlavorMemoryTooSmall() | ||
72 | 51 | |||
73 | 52 | - # NOTE(johannes): root_gb is allowed to be 0 for legacy reasons | ||
74 | 53 | - # since libvirt interpreted the value differently than other | ||
75 | 54 | - # drivers. A value of 0 means don't check size. | ||
76 | 55 | - root_gb = instance_type['root_gb'] | ||
77 | 56 | - if root_gb: | ||
78 | 57 | - if int(image.get('size') or 0) > root_gb * (1024 ** 3): | ||
79 | 58 | - raise exception.FlavorDiskTooSmall() | ||
80 | 59 | + # Image min_disk is in gb, size is in bytes. For sanity, have them both | ||
81 | 60 | + # in bytes. | ||
82 | 61 | + image_min_disk = int(image.get('min_disk') or 0) * units.Gi | ||
83 | 62 | + image_size = int(image.get('size') or 0) | ||
84 | 63 | + | ||
85 | 64 | + # Target disk is a volume. Don't check flavor disk size because it | ||
86 | 65 | + # doesn't make sense, and check min_disk against the volume size. | ||
87 | 66 | + if (root_bdm is not None and root_bdm.is_volume): | ||
88 | 67 | + # There are 2 possibilities here: either the target volume already | ||
89 | 68 | + # exists, or it doesn't, in which case the bdm will contain the | ||
90 | 69 | + # intended volume size. | ||
91 | 70 | + # | ||
92 | 71 | + # Cinder does its own check against min_disk, so if the target | ||
93 | 72 | + # volume already exists this has already been done and we don't | ||
94 | 73 | + # need to check it again here. In this case, volume_size may not be | ||
95 | 74 | + # set on the bdm. | ||
96 | 75 | + # | ||
97 | 76 | + # If we're going to create the volume, the bdm will contain | ||
98 | 77 | + # volume_size. Therefore we should check it if it exists. This will | ||
99 | 78 | + # still be checked again by cinder when the volume is created, but | ||
100 | 79 | + # that will not happen until the request reaches a host. By | ||
101 | 80 | + # checking it here, the user gets an immediate and useful failure | ||
102 | 81 | + # indication. | ||
103 | 82 | + # | ||
104 | 83 | + # The third possibility is that we have failed to consider | ||
105 | 84 | + # something, and there are actually more than 2 possibilities. In | ||
106 | 85 | + # this case cinder will still do the check at volume creation time. | ||
107 | 86 | + # The behaviour will still be correct, but the user will not get an | ||
108 | 87 | + # immediate failure from the api, and will instead have to | ||
109 | 88 | + # determine why the instance is in an error state with a task of | ||
110 | 89 | + # block_device_mapping. | ||
111 | 90 | + # | ||
112 | 91 | + # We could reasonably refactor this check into _validate_bdm at | ||
113 | 92 | + # some future date, as the various size logic is already split out | ||
114 | 93 | + # in there. | ||
115 | 94 | + dest_size = root_bdm.volume_size | ||
116 | 95 | + if dest_size is not None: | ||
117 | 96 | + dest_size *= units.Gi | ||
118 | 97 | + | ||
119 | 98 | + if image_min_disk > dest_size: | ||
120 | 99 | + # TODO(mdbooth) Raise a more descriptive exception here. | ||
121 | 100 | + # This is the exception which calling code expects, but | ||
122 | 101 | + # it's potentially misleading to the user. | ||
123 | 102 | + raise exception.FlavorDiskTooSmall() | ||
124 | 103 | + | ||
125 | 104 | + # Target disk is a local disk whose size is taken from the flavor | ||
126 | 105 | + else: | ||
127 | 106 | + dest_size = instance_type['root_gb'] * units.Gi | ||
128 | 107 | + | ||
129 | 108 | + # NOTE(johannes): root_gb is allowed to be 0 for legacy reasons | ||
130 | 109 | + # since libvirt interpreted the value differently than other | ||
131 | 110 | + # drivers. A value of 0 means don't check size. | ||
132 | 111 | + if dest_size != 0: | ||
133 | 112 | + if image_size > dest_size: | ||
134 | 113 | + raise exception.FlavorDiskTooSmall() | ||
135 | 114 | |||
136 | 115 | - if int(image.get('min_disk') or 0) > root_gb: | ||
137 | 116 | + if image_min_disk > dest_size: | ||
138 | 117 | raise exception.FlavorDiskTooSmall() | ||
139 | 118 | |||
140 | 119 | def _get_image_defined_bdms(self, base_options, instance_type, image_meta, | ||
141 | 120 | @@ -767,10 +816,11 @@ class API(base.Base): | ||
142 | 121 | |||
143 | 122 | def _checks_for_create_and_rebuild(self, context, image_id, image, | ||
144 | 123 | instance_type, metadata, | ||
145 | 124 | - files_to_inject): | ||
146 | 125 | + files_to_inject, root_bdm): | ||
147 | 126 | self._check_metadata_properties_quota(context, metadata) | ||
148 | 127 | self._check_injected_file_quota(context, files_to_inject) | ||
149 | 128 | - self._check_requested_image(context, image_id, image, instance_type) | ||
150 | 129 | + self._check_requested_image(context, image_id, image, | ||
151 | 130 | + instance_type, root_bdm) | ||
152 | 131 | |||
153 | 132 | def _validate_and_build_base_options(self, context, instance_type, | ||
154 | 133 | boot_meta, image_href, image_id, | ||
155 | 134 | @@ -778,7 +828,7 @@ class API(base.Base): | ||
156 | 135 | display_description, key_name, | ||
157 | 136 | key_data, security_groups, | ||
158 | 137 | availability_zone, forced_host, | ||
159 | 138 | - user_data, metadata, injected_files, | ||
160 | 139 | + user_data, metadata, | ||
161 | 140 | access_ip_v4, access_ip_v6, | ||
162 | 141 | requested_networks, config_drive, | ||
163 | 142 | auto_disk_config, reservation_id, | ||
164 | 143 | @@ -810,9 +860,6 @@ class API(base.Base): | ||
165 | 144 | except base64.binascii.Error: | ||
166 | 145 | raise exception.InstanceUserDataMalformed() | ||
167 | 146 | |||
168 | 147 | - self._checks_for_create_and_rebuild(context, image_id, boot_meta, | ||
169 | 148 | - instance_type, metadata, injected_files) | ||
170 | 149 | - | ||
171 | 150 | self._check_requested_secgroups(context, security_groups) | ||
172 | 151 | |||
173 | 152 | # Note: max_count is the number of instances requested by the user, | ||
174 | 153 | @@ -1095,7 +1142,7 @@ class API(base.Base): | ||
175 | 154 | instance_type, boot_meta, image_href, image_id, kernel_id, | ||
176 | 155 | ramdisk_id, display_name, display_description, | ||
177 | 156 | key_name, key_data, security_groups, availability_zone, | ||
178 | 157 | - forced_host, user_data, metadata, injected_files, access_ip_v4, | ||
179 | 158 | + forced_host, user_data, metadata, access_ip_v4, | ||
180 | 159 | access_ip_v6, requested_networks, config_drive, | ||
181 | 160 | auto_disk_config, reservation_id, max_count) | ||
182 | 161 | |||
183 | 162 | @@ -1115,6 +1162,12 @@ class API(base.Base): | ||
184 | 163 | base_options, instance_type, boot_meta, min_count, max_count, | ||
185 | 164 | block_device_mapping, legacy_bdm) | ||
186 | 165 | |||
187 | 166 | + # We can't do this check earlier because we need bdms from all sources | ||
188 | 167 | + # to have been merged in order to get the root bdm. | ||
189 | 168 | + self._checks_for_create_and_rebuild(context, image_id, boot_meta, | ||
190 | 169 | + instance_type, metadata, injected_files, | ||
191 | 170 | + block_device_mapping.root_bdm()) | ||
192 | 171 | + | ||
193 | 172 | instance_group = self._get_requested_instance_group(context, | ||
194 | 173 | scheduler_hints, check_server_group_quota) | ||
195 | 174 | |||
196 | 175 | @@ -2333,8 +2386,9 @@ class API(base.Base): | ||
197 | 176 | self._check_auto_disk_config(image=image, **kwargs) | ||
198 | 177 | |||
199 | 178 | flavor = instance.get_flavor() | ||
200 | 179 | + root_bdm = self._get_root_bdm(context, instance) | ||
201 | 180 | self._checks_for_create_and_rebuild(context, image_id, image, | ||
202 | 181 | - flavor, metadata, files_to_inject) | ||
203 | 182 | + flavor, metadata, files_to_inject, root_bdm) | ||
204 | 183 | |||
205 | 184 | kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk( | ||
206 | 185 | context, None, None, image) | ||
207 | 186 | @@ -3201,15 +3255,18 @@ class API(base.Base): | ||
208 | 187 | uuids = [instance.uuid for instance in instances] | ||
209 | 188 | return self.db.instance_fault_get_by_instance_uuids(context, uuids) | ||
210 | 189 | |||
211 | 190 | - def is_volume_backed_instance(self, context, instance, bdms=None): | ||
212 | 191 | - if not instance.image_ref: | ||
213 | 192 | - return True | ||
214 | 193 | - | ||
215 | 194 | + def _get_root_bdm(self, context, instance, bdms=None): | ||
216 | 195 | if bdms is None: | ||
217 | 196 | bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( | ||
218 | 197 | context, instance.uuid) | ||
219 | 198 | |||
220 | 199 | - root_bdm = bdms.root_bdm() | ||
221 | 200 | + return bdms.root_bdm() | ||
222 | 201 | + | ||
223 | 202 | + def is_volume_backed_instance(self, context, instance, bdms=None): | ||
224 | 203 | + if not instance.image_ref: | ||
225 | 204 | + return True | ||
226 | 205 | + | ||
227 | 206 | + root_bdm = self._get_root_bdm(context, instance, bdms) | ||
228 | 207 | if not root_bdm: | ||
229 | 208 | return False | ||
230 | 209 | return root_bdm.is_volume | ||
231 | 210 | diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py | ||
232 | 211 | index bf36960..b18c1e5 100644 | ||
233 | 212 | --- a/nova/tests/unit/compute/test_compute.py | ||
234 | 213 | +++ b/nova/tests/unit/compute/test_compute.py | ||
235 | 214 | @@ -11356,6 +11356,8 @@ class ComputeInactiveImageTestCase(BaseTestCase): | ||
236 | 215 | return {'id': id, 'min_disk': None, 'min_ram': None, | ||
237 | 216 | 'name': 'fake_name', | ||
238 | 217 | 'status': 'deleted', | ||
239 | 218 | + 'min_ram': 0, | ||
240 | 219 | + 'min_disk': 0, | ||
241 | 220 | 'properties': {'kernel_id': 'fake_kernel_id', | ||
242 | 221 | 'ramdisk_id': 'fake_ramdisk_id', | ||
243 | 222 | 'something_else': 'meow'}} | ||
244 | 223 | @@ -11708,78 +11710,175 @@ class CheckRequestedImageTestCase(test.TestCase): | ||
245 | 224 | |||
246 | 225 | def test_no_image_specified(self): | ||
247 | 226 | self.compute_api._check_requested_image(self.context, None, None, | ||
248 | 227 | - self.instance_type) | ||
249 | 228 | + self.instance_type, None) | ||
250 | 229 | |||
251 | 230 | def test_image_status_must_be_active(self): | ||
252 | 231 | image = dict(id='123', status='foo') | ||
253 | 232 | |||
254 | 233 | self.assertRaises(exception.ImageNotActive, | ||
255 | 234 | self.compute_api._check_requested_image, self.context, | ||
256 | 235 | - image['id'], image, self.instance_type) | ||
257 | 236 | + image['id'], image, self.instance_type, None) | ||
258 | 237 | |||
259 | 238 | image['status'] = 'active' | ||
260 | 239 | self.compute_api._check_requested_image(self.context, image['id'], | ||
261 | 240 | - image, self.instance_type) | ||
262 | 241 | + image, self.instance_type, None) | ||
263 | 242 | |||
264 | 243 | def test_image_min_ram_check(self): | ||
265 | 244 | image = dict(id='123', status='active', min_ram='65') | ||
266 | 245 | |||
267 | 246 | self.assertRaises(exception.FlavorMemoryTooSmall, | ||
268 | 247 | self.compute_api._check_requested_image, self.context, | ||
269 | 248 | - image['id'], image, self.instance_type) | ||
270 | 249 | + image['id'], image, self.instance_type, None) | ||
271 | 250 | |||
272 | 251 | image['min_ram'] = '64' | ||
273 | 252 | self.compute_api._check_requested_image(self.context, image['id'], | ||
274 | 253 | - image, self.instance_type) | ||
275 | 254 | + image, self.instance_type, None) | ||
276 | 255 | |||
277 | 256 | def test_image_min_disk_check(self): | ||
278 | 257 | image = dict(id='123', status='active', min_disk='2') | ||
279 | 258 | |||
280 | 259 | self.assertRaises(exception.FlavorDiskTooSmall, | ||
281 | 260 | self.compute_api._check_requested_image, self.context, | ||
282 | 261 | - image['id'], image, self.instance_type) | ||
283 | 262 | + image['id'], image, self.instance_type, None) | ||
284 | 263 | |||
285 | 264 | image['min_disk'] = '1' | ||
286 | 265 | self.compute_api._check_requested_image(self.context, image['id'], | ||
287 | 266 | - image, self.instance_type) | ||
288 | 267 | + image, self.instance_type, None) | ||
289 | 268 | |||
290 | 269 | def test_image_too_large(self): | ||
291 | 270 | image = dict(id='123', status='active', size='1073741825') | ||
292 | 271 | |||
293 | 272 | self.assertRaises(exception.FlavorDiskTooSmall, | ||
294 | 273 | self.compute_api._check_requested_image, self.context, | ||
295 | 274 | - image['id'], image, self.instance_type) | ||
296 | 275 | + image['id'], image, self.instance_type, None) | ||
297 | 276 | |||
298 | 277 | image['size'] = '1073741824' | ||
299 | 278 | self.compute_api._check_requested_image(self.context, image['id'], | ||
300 | 279 | - image, self.instance_type) | ||
301 | 280 | + image, self.instance_type, None) | ||
302 | 281 | |||
303 | 282 | def test_root_gb_zero_disables_size_check(self): | ||
304 | 283 | self.instance_type['root_gb'] = 0 | ||
305 | 284 | image = dict(id='123', status='active', size='1073741825') | ||
306 | 285 | |||
307 | 286 | self.compute_api._check_requested_image(self.context, image['id'], | ||
308 | 287 | - image, self.instance_type) | ||
309 | 288 | + image, self.instance_type, None) | ||
310 | 289 | |||
311 | 290 | def test_root_gb_zero_disables_min_disk(self): | ||
312 | 291 | self.instance_type['root_gb'] = 0 | ||
313 | 292 | image = dict(id='123', status='active', min_disk='2') | ||
314 | 293 | |||
315 | 294 | self.compute_api._check_requested_image(self.context, image['id'], | ||
316 | 295 | - image, self.instance_type) | ||
317 | 296 | + image, self.instance_type, None) | ||
318 | 297 | |||
319 | 298 | def test_config_drive_option(self): | ||
320 | 299 | image = {'id': 1, 'status': 'active'} | ||
321 | 300 | image['properties'] = {'img_config_drive': 'optional'} | ||
322 | 301 | self.compute_api._check_requested_image(self.context, image['id'], | ||
323 | 302 | - image, self.instance_type) | ||
324 | 303 | + image, self.instance_type, None) | ||
325 | 304 | image['properties'] = {'img_config_drive': 'mandatory'} | ||
326 | 305 | self.compute_api._check_requested_image(self.context, image['id'], | ||
327 | 306 | - image, self.instance_type) | ||
328 | 307 | + image, self.instance_type, None) | ||
329 | 308 | image['properties'] = {'img_config_drive': 'bar'} | ||
330 | 309 | self.assertRaises(exception.InvalidImageConfigDrive, | ||
331 | 310 | self.compute_api._check_requested_image, | ||
332 | 311 | - self.context, image['id'], image, self.instance_type) | ||
333 | 312 | + self.context, image['id'], image, self.instance_type, | ||
334 | 313 | + None) | ||
335 | 314 | + | ||
336 | 315 | + def test_volume_blockdevicemapping(self): | ||
337 | 316 | + # We should allow a root volume which is larger than the flavor root | ||
338 | 317 | + # disk. | ||
339 | 318 | + # We should allow a root volume created from an image whose min_disk is | ||
340 | 319 | + # larger than the flavor root disk. | ||
341 | 320 | + image_uuid = str(uuid.uuid4()) | ||
342 | 321 | + image = dict(id=image_uuid, status='active', | ||
343 | 322 | + size=self.instance_type.root_gb * units.Gi, | ||
344 | 323 | + min_disk=self.instance_type.root_gb + 1) | ||
345 | 324 | + | ||
346 | 325 | + volume_uuid = str(uuid.uuid4()) | ||
347 | 326 | + root_bdm = block_device_obj.BlockDeviceMapping( | ||
348 | 327 | + source_type='volume', destination_type='volume', | ||
349 | 328 | + volume_id=volume_uuid, volume_size=self.instance_type.root_gb + 1) | ||
350 | 329 | + | ||
351 | 330 | + self.compute_api._check_requested_image(self.context, image['id'], | ||
352 | 331 | + image, self.instance_type, root_bdm) | ||
353 | 332 | + | ||
354 | 333 | + def test_volume_blockdevicemapping_min_disk(self): | ||
355 | 334 | + # A bdm object volume smaller than the image's min_disk should not be | ||
356 | 335 | + # allowed | ||
357 | 336 | + image_uuid = str(uuid.uuid4()) | ||
358 | 337 | + image = dict(id=image_uuid, status='active', | ||
359 | 338 | + size=self.instance_type.root_gb * units.Gi, | ||
360 | 339 | + min_disk=self.instance_type.root_gb + 1) | ||
361 | 340 | + | ||
362 | 341 | + volume_uuid = str(uuid.uuid4()) | ||
363 | 342 | + root_bdm = block_device_obj.BlockDeviceMapping( | ||
364 | 343 | + source_type='image', destination_type='volume', | ||
365 | 344 | + image_id=image_uuid, volume_id=volume_uuid, | ||
366 | 345 | + volume_size=self.instance_type.root_gb) | ||
367 | 346 | + | ||
368 | 347 | + self.assertRaises(exception.FlavorDiskTooSmall, | ||
369 | 348 | + self.compute_api._check_requested_image, | ||
370 | 349 | + self.context, image_uuid, image, self.instance_type, | ||
371 | 350 | + root_bdm) | ||
372 | 351 | + | ||
373 | 352 | + def test_volume_blockdevicemapping_min_disk_no_size(self): | ||
374 | 353 | + # We should allow a root volume whose size is not given | ||
375 | 354 | + image_uuid = str(uuid.uuid4()) | ||
376 | 355 | + image = dict(id=image_uuid, status='active', | ||
377 | 356 | + size=self.instance_type.root_gb * units.Gi, | ||
378 | 357 | + min_disk=self.instance_type.root_gb) | ||
379 | 358 | + | ||
380 | 359 | + volume_uuid = str(uuid.uuid4()) | ||
381 | 360 | + root_bdm = block_device_obj.BlockDeviceMapping( | ||
382 | 361 | + source_type='volume', destination_type='volume', | ||
383 | 362 | + volume_id=volume_uuid, volume_size=None) | ||
384 | 363 | + | ||
385 | 364 | + self.compute_api._check_requested_image(self.context, image['id'], | ||
386 | 365 | + image, self.instance_type, root_bdm) | ||
387 | 366 | + | ||
388 | 367 | + def test_image_blockdevicemapping(self): | ||
389 | 368 | + # Test that we can succeed when passing bdms, and the root bdm isn't a | ||
390 | 369 | + # volume | ||
391 | 370 | + image_uuid = str(uuid.uuid4()) | ||
392 | 371 | + image = dict(id=image_uuid, status='active', | ||
393 | 372 | + size=self.instance_type.root_gb * units.Gi, min_disk=0) | ||
394 | 373 | + | ||
395 | 374 | + root_bdm = block_device_obj.BlockDeviceMapping( | ||
396 | 375 | + source_type='image', destination_type='local', image_id=image_uuid) | ||
397 | 376 | + | ||
398 | 377 | + self.compute_api._check_requested_image(self.context, image['id'], | ||
399 | 378 | + image, self.instance_type, root_bdm) | ||
400 | 379 | + | ||
401 | 380 | + def test_image_blockdevicemapping_too_big(self): | ||
402 | 381 | + # We should do a size check against flavor if we were passed bdms but | ||
403 | 382 | + # the root bdm isn't a volume | ||
404 | 383 | + image_uuid = str(uuid.uuid4()) | ||
405 | 384 | + image = dict(id=image_uuid, status='active', | ||
406 | 385 | + size=(self.instance_type.root_gb + 1) * units.Gi, | ||
407 | 386 | + min_disk=0) | ||
408 | 387 | + | ||
409 | 388 | + root_bdm = block_device_obj.BlockDeviceMapping( | ||
410 | 389 | + source_type='image', destination_type='local', image_id=image_uuid) | ||
411 | 390 | + | ||
412 | 391 | + self.assertRaises(exception.FlavorDiskTooSmall, | ||
413 | 392 | + self.compute_api._check_requested_image, | ||
414 | 393 | + self.context, image['id'], | ||
415 | 394 | + image, self.instance_type, root_bdm) | ||
416 | 395 | + | ||
417 | 396 | + def test_image_blockdevicemapping_min_disk(self): | ||
418 | 397 | + # We should do a min_disk check against flavor if we were passed bdms | ||
419 | 398 | + # but the root bdm isn't a volume | ||
420 | 399 | + image_uuid = str(uuid.uuid4()) | ||
421 | 400 | + image = dict(id=image_uuid, status='active', | ||
422 | 401 | + size=0, min_disk=self.instance_type.root_gb + 1) | ||
423 | 402 | + | ||
424 | 403 | + root_bdm = block_device_obj.BlockDeviceMapping( | ||
425 | 404 | + source_type='image', destination_type='local', image_id=image_uuid) | ||
426 | 405 | + | ||
427 | 406 | + self.assertRaises(exception.FlavorDiskTooSmall, | ||
428 | 407 | + self.compute_api._check_requested_image, | ||
429 | 408 | + self.context, image['id'], | ||
430 | 409 | + image, self.instance_type, root_bdm) | ||
431 | 410 | |||
432 | 411 | |||
433 | 412 | class ComputeHooksTestCase(test.BaseHookTestCase): | ||
434 | 413 | diff --git a/nova/tests/unit/compute/test_compute_api.py b/nova/tests/unit/compute/test_compute_api.py | ||
435 | 414 | index 47848a9..5b4c2b3 100644 | ||
436 | 415 | --- a/nova/tests/unit/compute/test_compute_api.py | ||
437 | 416 | +++ b/nova/tests/unit/compute/test_compute_api.py | ||
438 | 417 | @@ -2257,15 +2257,16 @@ class _ComputeAPIUnitTestMixIn(object): | ||
439 | 418 | vm_state=vm_states.ACTIVE, cell_name='fake-cell', | ||
440 | 419 | launched_at=timeutils.utcnow(), | ||
441 | 420 | system_metadata=orig_system_metadata, | ||
442 | 421 | + image_ref='foo', | ||
443 | 422 | expected_attrs=['system_metadata']) | ||
444 | 423 | get_flavor.return_value = test_flavor.fake_flavor | ||
445 | 424 | flavor = instance.get_flavor() | ||
446 | 425 | - image_href = '' | ||
447 | 426 | + image_href = 'foo' | ||
448 | 427 | image = {"min_ram": 10, "min_disk": 1, | ||
449 | 428 | "properties": {'architecture': arch.X86_64}} | ||
450 | 429 | admin_pass = '' | ||
451 | 430 | files_to_inject = [] | ||
452 | 431 | - bdms = [] | ||
453 | 432 | + bdms = objects.BlockDeviceMappingList() | ||
454 | 433 | |||
455 | 434 | _get_image.return_value = (None, image) | ||
456 | 435 | bdm_get_by_instance_uuid.return_value = bdms | ||
457 | 436 | @@ -2284,7 +2285,7 @@ class _ComputeAPIUnitTestMixIn(object): | ||
458 | 437 | |||
459 | 438 | _check_auto_disk_config.assert_called_once_with(image=image) | ||
460 | 439 | _checks_for_create_and_rebuild.assert_called_once_with(self.context, | ||
461 | 440 | - None, image, flavor, {}, []) | ||
462 | 441 | + None, image, flavor, {}, [], None) | ||
463 | 442 | self.assertNotEqual(orig_system_metadata, instance.system_metadata) | ||
464 | 443 | |||
465 | 444 | @mock.patch.object(objects.Instance, 'save') | ||
466 | 445 | @@ -2309,7 +2310,7 @@ class _ComputeAPIUnitTestMixIn(object): | ||
467 | 446 | 'vm_mode': 'xen'}} | ||
468 | 447 | admin_pass = '' | ||
469 | 448 | files_to_inject = [] | ||
470 | 449 | - bdms = [] | ||
471 | 450 | + bdms = objects.BlockDeviceMappingList() | ||
472 | 451 | |||
473 | 452 | instance = fake_instance.fake_instance_obj(self.context, | ||
474 | 453 | vm_state=vm_states.ACTIVE, cell_name='fake-cell', | ||
475 | 454 | @@ -2342,7 +2343,7 @@ class _ComputeAPIUnitTestMixIn(object): | ||
476 | 455 | |||
477 | 456 | _check_auto_disk_config.assert_called_once_with(image=new_image) | ||
478 | 457 | _checks_for_create_and_rebuild.assert_called_once_with(self.context, | ||
479 | 458 | - None, new_image, flavor, {}, []) | ||
480 | 459 | + None, new_image, flavor, {}, [], None) | ||
481 | 460 | self.assertEqual(vm_mode.XEN, instance.vm_mode) | ||
482 | 461 | |||
483 | 462 | def _test_check_injected_file_quota_onset_file_limit_exceeded(self, | ||
484 | 463 | diff --git a/nova/tests/unit/compute/test_compute_cells.py b/nova/tests/unit/compute/test_compute_cells.py | ||
485 | 464 | index a769e1d..77d3dd7 100644 | ||
486 | 465 | --- a/nova/tests/unit/compute/test_compute_cells.py | ||
487 | 466 | +++ b/nova/tests/unit/compute/test_compute_cells.py | ||
488 | 467 | @@ -236,11 +236,13 @@ class CellsConductorAPIRPCRedirect(test.NoDBTestCase): | ||
489 | 468 | @mock.patch.object(compute_api.API, '_check_and_transform_bdm') | ||
490 | 469 | @mock.patch.object(compute_api.API, '_get_image') | ||
491 | 470 | @mock.patch.object(compute_api.API, '_validate_and_build_base_options') | ||
492 | 471 | - def test_build_instances(self, _validate, _get_image, _check_bdm, | ||
493 | 472 | + @mock.patch.object(compute_api.API, '_checks_for_create_and_rebuild') | ||
494 | 473 | + def test_build_instances(self, _checks_for_create_and_rebuild, | ||
495 | 474 | + _validate, _get_image, _check_bdm, | ||
496 | 475 | _provision, _record_action_start): | ||
497 | 476 | _get_image.return_value = (None, 'fake-image') | ||
498 | 477 | _validate.return_value = ({}, 1) | ||
499 | 478 | - _check_bdm.return_value = 'bdms' | ||
500 | 479 | + _check_bdm.return_value = objects.BlockDeviceMappingList() | ||
501 | 480 | _provision.return_value = 'instances' | ||
502 | 481 | |||
503 | 482 | self.compute_api.create(self.context, 'fake-flavor', 'fake-image') | ||
504 | 483 | @@ -309,7 +311,7 @@ class CellsConductorAPIRPCRedirect(test.NoDBTestCase): | ||
505 | 484 | "properties": {'architecture': 'x86_64'}} | ||
506 | 485 | admin_pass = '' | ||
507 | 486 | files_to_inject = [] | ||
508 | 487 | - bdms = [] | ||
509 | 488 | + bdms = objects.BlockDeviceMappingList() | ||
510 | 489 | |||
511 | 490 | _get_image.return_value = (None, image) | ||
512 | 491 | bdm_get_by_instance_uuid.return_value = bdms | ||
513 | 492 | -- | ||
514 | 493 | 2.1.4 | ||
515 | 494 | |||
516 | 0 | 495 | ||
517 | === modified file 'debian/patches/series' | |||
518 | --- debian/patches/series 2015-08-03 20:04:14 +0000 | |||
519 | +++ debian/patches/series 2015-09-08 23:22:49 +0000 | |||
520 | @@ -7,3 +7,4 @@ | |||
521 | 7 | #rate-limit-power-syncs.patch | 7 | #rate-limit-power-syncs.patch |
522 | 8 | skip-ubuntu-tests.patch | 8 | skip-ubuntu-tests.patch |
523 | 9 | skip-proxy-test.patch | 9 | skip-proxy-test.patch |
524 | 10 | not-check-disk-size.patch |