Merge lp:~cerberus/nova/lp809909 into lp:~hudson-openstack/nova/trunk

Proposed by Matt Dietz
Status: Merged
Merged at revision: 1344
Proposed branch: lp:~cerberus/nova/lp809909
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 294 lines (+134/-50)
6 files modified
nova/compute/manager.py (+15/-9)
nova/tests/test_compute.py (+3/-3)
nova/tests/test_xenapi.py (+90/-21)
nova/virt/driver.py (+3/-2)
nova/virt/xenapi/vmops.py (+18/-12)
nova/virt/xenapi_conn.py (+5/-3)
To merge this branch: bzr merge lp:~cerberus/nova/lp809909
Reviewer Review Type Date Requested Status
Jason Kölker (community) Disapprove
Johannes Erdfelt (community) Needs Fixing
Review via email: mp+68602@code.launchpad.net

Description of the change

Fixes lp809909

Migrate of instance with no local storage fails with exception

Simply checks to see if the instance has any local storage, and if not, skips over the resize VDI step.

To post a comment you must log in.
Revision history for this message
Johannes Erdfelt (johannes.erdfelt) wrote :

Thinking about this more, what would happen if we switched from m1.large (as an example) to m1.tiny? Would the same error occur because 0 is an invalid value? I'd guess we would need to delete the VDI in that case instead of resizing.

Also, in the hypothetical case of two flavors with the same local_gb, a resize of the VDI isn't necessary, right? Should we just skip the call to VDI.resize_online then?

Revision history for this message
Matt Dietz (cerberus) wrote :

To the first point, you can't resize down (currently) so this wouldn't be an issue.

Regarding the second, I think a call to resize online with the same disk size effectively becomes a NOP, but you're right, we shouldn't be calling it at all. So much for taking the easy way out.

Revision history for this message
Johannes Erdfelt (johannes.erdfelt) wrote :

If it's a NOP, then that's probably not a big deal.

One other concern, which might be moot, would be going from m1.tiny to a larger instance. I originally thought that m1.tiny didn't create a VDI since it wouldn't be necessary with 0GB of local storage, but my logs show it tried to resize a VDI.

Assuming there is no VDI, then we would need to create one first?

The code is a little confusing and I'm not 100% sure what is happening right now.

Revision history for this message
Matt Dietz (cerberus) wrote :

I just thought of the same case, and TBH, I don't know what it expects to be able to do. I would guess we would have to create a disk in that scenario. When I originally built this feature, I was thinking only in terms of Rackspace style instances, which all have attached disk.

I'll go ahead and file a bug that attempts to reconcile these other cases as necessary.

lp:~cerberus/nova/lp809909 updated
1288. By Matt Dietz

CHanges based on feedback

1289. By Matt Dietz

Renamed the virt driver resize methods to migration for marginally more understandable code

Revision history for this message
Johannes Erdfelt (johannes.erdfelt) wrote :

You might need to merge with trunk, the preview diff looks like it is showing a conflict.

lp:~cerberus/nova/lp809909 updated
1290. By Matt Dietz

Resolved conflict with trunk

Revision history for this message
Johannes Erdfelt (johannes.erdfelt) wrote :

The code tracks a resize_instance boolean all the way through calling the resize_instance() method, but that method doesn't use the resize_instance boolean at all. Is that intended for future use?

Revision history for this message
Matt Dietz (cerberus) wrote :

Johannes: it's used in the xenapi virt driver

Passed from compute/manage
43 + self.driver.finish_migration(instance_ref, disk_info, network_info,
44 + resize_instance)

to xenapi_conn
234 + if resize_instance:
235 + self.resize_instance(instance, vdi_uuid, resize_instance)

lp:~cerberus/nova/lp809909 updated
1291. By Matt Dietz

Fixed the virt driver base

1292. By Matt Dietz

Merge from trunk and conflict resolution

1293. By Matt Dietz

Removed superfluous parameter

Revision history for this message
Johannes Erdfelt (johannes.erdfelt) wrote :

After your recent changes, now resize_instance() is called with an extra unexpected parameter.

review: Needs Fixing
Revision history for this message
Jason Kölker (jason-koelker) wrote :

Since cerberus is living it up pimp style at OSCON, I'm going to -steal his karma- shepard the change lp:~jason-koelker/nova/lp809909

Revision history for this message
Jason Kölker (jason-koelker) wrote :
review: Disapprove

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/compute/manager.py'
2--- nova/compute/manager.py 2011-07-26 19:42:56 +0000
3+++ nova/compute/manager.py 2011-07-27 18:22:33 +0000
4@@ -747,7 +747,7 @@
5 local_gb=instance_type['local_gb'],
6 instance_type_id=instance_type['id']))
7
8- self.driver.revert_resize(instance_ref)
9+ self.driver.revert_migration(instance_ref)
10 self.db.migration_update(context, migration_id,
11 {'status': 'reverted'})
12 usage_info = utils.usage_from_instance(instance_ref)
13@@ -845,20 +845,26 @@
14
15 """
16 migration_ref = self.db.migration_get(context, migration_id)
17+
18+ resize_instance = False
19 instance_ref = self.db.instance_get_by_uuid(context,
20 migration_ref.instance_uuid)
21- instance_type = self.db.instance_type_get_by_flavor_id(context,
22- migration_ref['new_flavor_id'])
23- self.db.instance_update(context, instance_ref.uuid,
24- dict(instance_type_id=instance_type['id'],
25- memory_mb=instance_type['memory_mb'],
26- vcpus=instance_type['vcpus'],
27- local_gb=instance_type['local_gb']))
28+ if migration_ref['old_flavor_id'] != migration_ref['new_flavor_id']:
29+ instance_type = self.db.instance_type_get_by_flavor_id(context,
30+ migration_ref['new_flavor_id'])
31+ self.db.instance_update(context, instance_ref.uuid,
32+ dict(instance_type_id=instance_type['id'],
33+ memory_mb=instance_type['memory_mb'],
34+ vcpus=instance_type['vcpus'],
35+ local_gb=instance_type['local_gb']))
36+ resize_instance = True
37
38 instance_ref = self.db.instance_get_by_uuid(context,
39 instance_ref.uuid)
40+
41 network_info = self._get_instance_nw_info(context, instance_ref)
42- self.driver.finish_resize(instance_ref, disk_info, network_info)
43+ self.driver.finish_migration(instance_ref, disk_info, network_info,
44+ resize_instance)
45
46 self.db.migration_update(context, migration_id,
47 {'status': 'finished', })
48
49=== modified file 'nova/tests/test_compute.py'
50--- nova/tests/test_compute.py 2011-07-22 20:11:39 +0000
51+++ nova/tests/test_compute.py 2011-07-27 18:22:33 +0000
52@@ -420,7 +420,7 @@
53 def fake(*args, **kwargs):
54 pass
55
56- self.stubs.Set(self.compute.driver, 'finish_resize', fake)
57+ self.stubs.Set(self.compute.driver, 'finish_migration', fake)
58 self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake)
59 context = self.context.elevated()
60 instance_id = self._create_instance()
61@@ -531,8 +531,8 @@
62 def fake(*args, **kwargs):
63 pass
64
65- self.stubs.Set(self.compute.driver, 'finish_resize', fake)
66- self.stubs.Set(self.compute.driver, 'revert_resize', fake)
67+ self.stubs.Set(self.compute.driver, 'finish_migration', fake)
68+ self.stubs.Set(self.compute.driver, 'revert_migration', fake)
69 self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake)
70
71 self.compute.run_instance(self.context, instance_id)
72
73=== modified file 'nova/tests/test_xenapi.py'
74--- nova/tests/test_xenapi.py 2011-07-27 17:07:52 +0000
75+++ nova/tests/test_xenapi.py 2011-07-27 18:22:33 +0000
76@@ -783,27 +783,96 @@
77 conn = xenapi_conn.get_connection(False)
78 conn.migrate_disk_and_power_off(instance, '127.0.0.1')
79
80- def test_finish_resize(self):
81- instance = db.instance_create(self.context, self.values)
82- stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
83- stubs.stubout_loopingcall_start(self.stubs)
84- conn = xenapi_conn.get_connection(False)
85- network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
86- {'broadcast': '192.168.0.255',
87- 'dns': ['192.168.0.1'],
88- 'gateway': '192.168.0.1',
89- 'gateway6': 'dead:beef::1',
90- 'ip6s': [{'enabled': '1',
91- 'ip': 'dead:beef::dcad:beff:feef:0',
92- 'netmask': '64'}],
93- 'ips': [{'enabled': '1',
94- 'ip': '192.168.0.100',
95- 'netmask': '255.255.255.0'}],
96- 'label': 'fake',
97- 'mac': 'DE:AD:BE:EF:00:00',
98- 'rxtx_cap': 3})]
99- conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'),
100- network_info)
101+ def test_finish_migrate(self):
102+ instance = db.instance_create(self.context, self.values)
103+ self.called = False
104+
105+ def fake_vdi_resize(*args, **kwargs):
106+ self.called = True
107+
108+ self.stubs.Set(stubs.FakeSessionForMigrationTests,
109+ "VDI_resize_online", fake_vdi_resize)
110+ stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
111+ stubs.stubout_loopingcall_start(self.stubs)
112+ conn = xenapi_conn.get_connection(False)
113+ network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
114+ {'broadcast': '192.168.0.255',
115+ 'dns': ['192.168.0.1'],
116+ 'gateway': '192.168.0.1',
117+ 'gateway6': 'dead:beef::1',
118+ 'ip6s': [{'enabled': '1',
119+ 'ip': 'dead:beef::dcad:beff:feef:0',
120+ 'netmask': '64'}],
121+ 'ips': [{'enabled': '1',
122+ 'ip': '192.168.0.100',
123+ 'netmask': '255.255.255.0'}],
124+ 'label': 'fake',
125+ 'mac': 'DE:AD:BE:EF:00:00',
126+ 'rxtx_cap': 3})]
127+ conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'),
128+ network_info, resize_instance=True)
129+ self.assertEqual(self.called, True)
130+
131+ def test_finish_migrate_no_local_storage(self):
132+ tiny_type_id = \
133+ instance_types.get_instance_type_by_name('m1.tiny')['id']
134+ self.values.update({'instance_type_id': tiny_type_id, 'local_gb': 0})
135+ instance = db.instance_create(self.context, self.values)
136+
137+ def fake_vdi_resize(*args, **kwargs):
138+ raise Exception("This shouldn't be called")
139+
140+ self.stubs.Set(stubs.FakeSessionForMigrationTests,
141+ "VDI_resize_online", fake_vdi_resize)
142+ stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
143+ stubs.stubout_loopingcall_start(self.stubs)
144+ conn = xenapi_conn.get_connection(False)
145+ network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
146+ {'broadcast': '192.168.0.255',
147+ 'dns': ['192.168.0.1'],
148+ 'gateway': '192.168.0.1',
149+ 'gateway6': 'dead:beef::1',
150+ 'ip6s': [{'enabled': '1',
151+ 'ip': 'dead:beef::dcad:beff:feef:0',
152+ 'netmask': '64'}],
153+ 'ips': [{'enabled': '1',
154+ 'ip': '192.168.0.100',
155+ 'netmask': '255.255.255.0'}],
156+ 'label': 'fake',
157+ 'mac': 'DE:AD:BE:EF:00:00',
158+ 'rxtx_cap': 3})]
159+ conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'),
160+ network_info, resize_instance=True)
161+
162+ def test_finish_migrate_no_resize_vdi(self):
163+ instance = db.instance_create(self.context, self.values)
164+
165+ def fake_vdi_resize(*args, **kwargs):
166+ raise Exception("This shouldn't be called")
167+
168+ self.stubs.Set(stubs.FakeSessionForMigrationTests,
169+ "VDI_resize_online", fake_vdi_resize)
170+ stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
171+ stubs.stubout_loopingcall_start(self.stubs)
172+ conn = xenapi_conn.get_connection(False)
173+ network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
174+ {'broadcast': '192.168.0.255',
175+ 'dns': ['192.168.0.1'],
176+ 'gateway': '192.168.0.1',
177+ 'gateway6': 'dead:beef::1',
178+ 'ip6s': [{'enabled': '1',
179+ 'ip': 'dead:beef::dcad:beff:feef:0',
180+ 'netmask': '64'}],
181+ 'ips': [{'enabled': '1',
182+ 'ip': '192.168.0.100',
183+ 'netmask': '255.255.255.0'}],
184+ 'label': 'fake',
185+ 'mac': 'DE:AD:BE:EF:00:00',
186+ 'rxtx_cap': 3})]
187+
188+ # Resize instance would be determined by the compute call
189+ conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'),
190+ network_info, resize_instance=False)
191
192
193 class XenAPIDetermineDiskImageTestCase(test.TestCase):
194
195=== modified file 'nova/virt/driver.py'
196--- nova/virt/driver.py 2011-07-22 20:11:39 +0000
197+++ nova/virt/driver.py 2011-07-27 18:22:33 +0000
198@@ -122,11 +122,12 @@
199 """Create snapshot from a running VM instance."""
200 raise NotImplementedError()
201
202- def finish_resize(self, instance, disk_info):
203+ def finish_migration(self, instance, disk_info, network_info,
204+ resize_instance):
205 """Completes a resize, turning on the migrated instance"""
206 raise NotImplementedError()
207
208- def revert_resize(self, instance):
209+ def revert_migration(self, instance):
210 """Reverts a resize, powering back on the instance"""
211 raise NotImplementedError()
212
213
214=== modified file 'nova/virt/xenapi/vmops.py'
215--- nova/virt/xenapi/vmops.py 2011-07-27 17:07:52 +0000
216+++ nova/virt/xenapi/vmops.py 2011-07-27 18:22:33 +0000
217@@ -110,17 +110,19 @@
218 instance_infos.append(instance_info)
219 return instance_infos
220
221- def revert_resize(self, instance):
222+ def revert_migration(self, instance):
223 vm_ref = VMHelper.lookup(self._session, instance.name)
224 self._start(instance, vm_ref)
225
226- def finish_resize(self, instance, disk_info, network_info):
227+ def finish_migration(self, instance, disk_info, network_info,
228+ resize_instance):
229 vdi_uuid = self.link_disks(instance, disk_info['base_copy'],
230 disk_info['cow'])
231 vm_ref = self._create_vm(instance,
232 [dict(vdi_type='os', vdi_uuid=vdi_uuid)],
233 network_info)
234- self.resize_instance(instance, vdi_uuid)
235+ if resize_instance:
236+ self.resize_instance(instance, vdi_uuid, resize_instance)
237 self._spawn(instance, vm_ref)
238
239 def _start(self, instance, vm_ref=None):
240@@ -568,18 +570,22 @@
241 return new_cow_uuid
242
243 def resize_instance(self, instance, vdi_uuid):
244- """Resize a running instance by changing it's RAM and disk size."""
245+ """Resize a running instance by changing its RAM and disk size."""
246 #TODO(mdietz): this will need to be adjusted for swap later
247 #The new disk size must be in bytes
248
249- new_disk_size = str(instance.local_gb * 1024 * 1024 * 1024)
250- instance_name = instance.name
251- instance_local_gb = instance.local_gb
252- LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance %(instance_name)s."
253- " Expanding to %(instance_local_gb)d GB") % locals())
254- vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
255- self._session.call_xenapi('VDI.resize_online', vdi_ref, new_disk_size)
256- LOG.debug(_("Resize instance %s complete") % (instance.name))
257+ new_disk_size = instance.local_gb * 1024 * 1024 * 1024
258+ if new_disk_size > 0:
259+ instance_name = instance.name
260+ instance_local_gb = instance.local_gb
261+ LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance"
262+ "%(instance_name)s. Expanding to %(instance_local_gb)d"
263+ " GB") % locals())
264+ vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
265+ # for an instance with no local storage
266+ self._session.call_xenapi('VDI.resize_online', vdi_ref,
267+ str(new_disk_size))
268+ LOG.debug(_("Resize instance %s complete") % (instance.name))
269
270 def reboot(self, instance):
271 """Reboot VM instance."""
272
273=== modified file 'nova/virt/xenapi_conn.py'
274--- nova/virt/xenapi_conn.py 2011-07-27 17:07:52 +0000
275+++ nova/virt/xenapi_conn.py 2011-07-27 18:22:33 +0000
276@@ -191,13 +191,15 @@
277 """Create VM instance"""
278 self._vmops.spawn(instance, network_info)
279
280- def revert_resize(self, instance):
281+ def revert_migration(self, instance):
282 """Reverts a resize, powering back on the instance"""
283 self._vmops.revert_resize(instance)
284
285- def finish_resize(self, instance, disk_info, network_info):
286+ def finish_migration(self, instance, disk_info, network_info,
287+ resize_instance=False):
288 """Completes a resize, turning on the migrated instance"""
289- self._vmops.finish_resize(instance, disk_info, network_info)
290+ self._vmops.finish_migration(instance, disk_info, network_info,
291+ resize_instance)
292
293 def snapshot(self, instance, image_id):
294 """ Create snapshot from a running VM instance """