|
1 commit 42cae28241cd0c213201d036bfbe13fb118e4bee |
|
2 Author: Cyril Roelandt <[email protected]> |
|
3 Date: Mon Aug 18 17:45:35 2014 +0000 |
|
4 |
|
5 libvirt: Make sure volumes are well detected during block migration |
|
6 |
|
7 Current implementation of live migration in libvirt incorrectly includes |
|
8 block devices on shared storage (e.g., NFS) when computing destination |
|
9 storage requirements. Since these volumes are already on shared storage |
|
10 they do not need to be migrated. As a result, migration fails if the |
|
11 amount of free space on the shared drive is less than the size of the |
|
12 volume to be migrated. The problem is addressed by adding a |
|
13 block_device_info parameter to check_can_live_migrate_source() to allow |
|
14 volumes to be filtered correctly when computing migration space |
|
15 requirements. |
|
16 |
|
17 This only fixes the issue on libvirt: it is unclear whether other |
|
18 implementations suffer from the same issue. |
|
19 |
|
20 Thanks to Florent Flament for spotting and fixing an issue while trying out |
|
21 this patch. |
|
22 |
|
23 Co-Authored-By: Florent Flament <[email protected]> |
|
24 Change-Id: Iac7d2cd2a70800fd89864463ca45c030c47411b0 |
|
25 Closes-Bug: #1356552 |
|
26 (cherry picked from commit 671aa9f8b7ca5274696f83bde0d4822ee431b837) |
|
27 |
|
28 --- nova-2014.2.2/nova/compute/manager.py.~3~ 2015-12-01 05:07:52.781465660 -0800 |
|
29 +++ nova-2014.2.2/nova/compute/manager.py 2015-12-01 05:07:52.795381628 -0800 |
|
30 @@ -4891,8 +4891,11 @@ class ComputeManager(manager.Manager): |
|
31 is_volume_backed = self.compute_api.is_volume_backed_instance(ctxt, |
|
32 instance) |
|
33 dest_check_data['is_volume_backed'] = is_volume_backed |
|
34 + block_device_info = self._get_instance_block_device_info( |
|
35 + ctxt, instance, refresh_conn_info=True) |
|
36 return self.driver.check_can_live_migrate_source(ctxt, instance, |
|
37 - dest_check_data) |
|
38 + dest_check_data, |
|
39 + block_device_info) |
|
40 |
|
41 @object_compat |
|
42 @wrap_exception() |
|
43 --- nova-2014.2.2/nova/tests/compute/test_compute_mgr.py.~2~ 2015-12-01 05:07:52.782691092 -0800 |
|
44 +++ nova-2014.2.2/nova/tests/compute/test_compute_mgr.py 2015-12-01 05:07:52.796520248 -0800 |
|
45 @@ -1302,13 +1302,19 @@ class ComputeManagerUnitTestCase(test.No |
|
46 |
|
47 self.mox.StubOutWithMock(self.compute.compute_api, |
|
48 'is_volume_backed_instance') |
|
49 + self.mox.StubOutWithMock(self.compute, |
|
50 + '_get_instance_block_device_info') |
|
51 self.mox.StubOutWithMock(self.compute.driver, |
|
52 'check_can_live_migrate_source') |
|
53 |
|
54 self.compute.compute_api.is_volume_backed_instance( |
|
55 self.context, instance).AndReturn(is_volume_backed) |
|
56 + self.compute._get_instance_block_device_info( |
|
57 + self.context, instance, refresh_conn_info=True |
|
58 + ).AndReturn({'block_device_mapping': 'fake'}) |
|
59 self.compute.driver.check_can_live_migrate_source( |
|
60 - self.context, instance, expected_dest_check_data) |
|
61 + self.context, instance, expected_dest_check_data, |
|
62 + {'block_device_mapping': 'fake'}) |
|
63 |
|
64 self.mox.ReplayAll() |
|
65 |
|
66 diff --git a/nova/tests/virt/libvirt/test_driver.py b/nova/tests/virt/libvirt/test_driver.py |
|
67 index 613943b..c686b88 100644 |
|
68 --- a/nova/tests/virt/libvirt/test_driver.py |
|
69 +++ b/nova/tests/virt/libvirt/test_driver.py |
|
70 @@ -5323,7 +5323,7 @@ class LibvirtConnTestCase(test.TestCase): |
|
71 self.mox.StubOutWithMock(conn, "_assert_dest_node_has_enough_disk") |
|
72 conn._assert_dest_node_has_enough_disk( |
|
73 self.context, instance, dest_check_data['disk_available_mb'], |
|
74 - False) |
|
75 + False, None) |
|
76 |
|
77 self.mox.ReplayAll() |
|
78 ret = conn.check_can_live_migrate_source(self.context, instance, |
|
79 @@ -5386,8 +5386,9 @@ class LibvirtConnTestCase(test.TestCase): |
|
80 disk_available_mb=0) |
|
81 |
|
82 self.mox.StubOutWithMock(conn, "get_instance_disk_info") |
|
83 - conn.get_instance_disk_info(instance["name"]).AndReturn( |
|
84 - '[{"virt_disk_size":2}]') |
|
85 + conn.get_instance_disk_info(instance["name"], |
|
86 + block_device_info=None).AndReturn( |
|
87 + '[{"virt_disk_size":2}]') |
|
88 |
|
89 self.mox.ReplayAll() |
|
90 self.assertRaises(exception.MigrationError, |
|
91 diff --git a/nova/virt/driver.py b/nova/virt/driver.py |
|
92 index fd483e5..20f4dd1 100644 |
|
93 --- a/nova/virt/driver.py |
|
94 +++ b/nova/virt/driver.py |
|
95 @@ -808,7 +808,7 @@ class ComputeDriver(object): |
|
96 raise NotImplementedError() |
|
97 |
|
98 def check_can_live_migrate_source(self, context, instance, |
|
99 - dest_check_data): |
|
100 + dest_check_data, block_device_info=None): |
|
101 """Check if it is possible to execute live migration. |
|
102 |
|
103 This checks if the live migration can succeed, based on the |
|
104 @@ -817,6 +817,7 @@ class ComputeDriver(object): |
|
105 :param context: security context |
|
106 :param instance: nova.db.sqlalchemy.models.Instance |
|
107 :param dest_check_data: result of check_can_live_migrate_destination |
|
108 + :param block_device_info: result of _get_instance_block_device_info |
|
109 :returns: a dict containing migration info (hypervisor-dependent) |
|
110 """ |
|
111 raise NotImplementedError() |
|
112 diff --git a/nova/virt/fake.py b/nova/virt/fake.py |
|
113 index 049c519..fe9ff1c 100644 |
|
114 --- a/nova/virt/fake.py |
|
115 +++ b/nova/virt/fake.py |
|
116 @@ -426,7 +426,7 @@ class FakeDriver(driver.ComputeDriver): |
|
117 return {} |
|
118 |
|
119 def check_can_live_migrate_source(self, ctxt, instance_ref, |
|
120 - dest_check_data): |
|
121 + dest_check_data, block_device_info=None): |
|
122 return |
|
123 |
|
124 def finish_migration(self, context, migration, instance, disk_info, |
|
125 diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py |
|
126 index 485aa23..fa42130 100644 |
|
127 --- a/nova/virt/hyperv/driver.py |
|
128 +++ b/nova/virt/hyperv/driver.py |
|
129 @@ -169,7 +169,7 @@ class HyperVDriver(driver.ComputeDriver): |
|
130 context, dest_check_data) |
|
131 |
|
132 def check_can_live_migrate_source(self, context, instance, |
|
133 - dest_check_data): |
|
134 + dest_check_data, block_device_info=None): |
|
135 return self._livemigrationops.check_can_live_migrate_source( |
|
136 context, instance, dest_check_data) |
|
137 |
|
138 diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py |
|
139 index cec2013..d420f15 100644 |
|
140 --- a/nova/virt/libvirt/driver.py |
|
141 +++ b/nova/virt/libvirt/driver.py |
|
142 @@ -5028,7 +5028,8 @@ class LibvirtDriver(driver.ComputeDriver): |
|
143 self._cleanup_shared_storage_test_file(filename) |
|
144 |
|
145 def check_can_live_migrate_source(self, context, instance, |
|
146 - dest_check_data): |
|
147 + dest_check_data, |
|
148 + block_device_info=None): |
|
149 """Check if it is possible to execute live migration. |
|
150 |
|
151 This checks if the live migration can succeed, based on the |
|
152 @@ -5037,6 +5038,7 @@ class LibvirtDriver(driver.ComputeDriver): |
|
153 :param context: security context |
|
154 :param instance: nova.db.sqlalchemy.models.Instance |
|
155 :param dest_check_data: result of check_can_live_migrate_destination |
|
156 + :param block_device_info: result of _get_instance_block_device_info |
|
157 :returns: a dict containing migration info |
|
158 """ |
|
159 # Checking shared storage connectivity |
|
160 @@ -5058,7 +5060,8 @@ class LibvirtDriver(driver.ComputeDriver): |
|
161 raise exception.InvalidLocalStorage(reason=reason, path=source) |
|
162 self._assert_dest_node_has_enough_disk(context, instance, |
|
163 dest_check_data['disk_available_mb'], |
|
164 - dest_check_data['disk_over_commit']) |
|
165 + dest_check_data['disk_over_commit'], |
|
166 + block_device_info) |
|
167 |
|
168 elif not (dest_check_data['is_shared_block_storage'] or |
|
169 dest_check_data['is_shared_instance_path']): |
|
170 @@ -5106,7 +5109,8 @@ class LibvirtDriver(driver.ComputeDriver): |
|
171 return False |
|
172 |
|
173 def _assert_dest_node_has_enough_disk(self, context, instance, |
|
174 - available_mb, disk_over_commit): |
|
175 + available_mb, disk_over_commit, |
|
176 + block_device_info=None): |
|
177 """Checks if destination has enough disk for block migration.""" |
|
178 # Libvirt supports qcow2 disk format,which is usually compressed |
|
179 # on compute nodes. |
|
180 @@ -5122,7 +5126,8 @@ class LibvirtDriver(driver.ComputeDriver): |
|
181 if available_mb: |
|
182 available = available_mb * units.Mi |
|
183 |
|
184 - ret = self.get_instance_disk_info(instance['name']) |
|
185 + ret = self.get_instance_disk_info(instance['name'], |
|
186 + block_device_info=block_device_info) |
|
187 disk_infos = jsonutils.loads(ret) |
|
188 |
|
189 necessary = 0 |
|
190 --- nova-2014.2.2/nova/virt/xenapi/driver.py.~2~ 2015-12-01 05:16:19.562306358 -0800 |
|
191 +++ nova-2014.2.2/nova/virt/xenapi/driver.py 2015-12-01 05:16:19.614403555 -0800 |
|
192 @@ -501,7 +501,7 @@ class XenAPIDriver(driver.ComputeDriver) |
|
193 pass |
|
194 |
|
195 def check_can_live_migrate_source(self, context, instance, |
|
196 - dest_check_data): |
|
197 + dest_check_data, block_device_info=None): |
|
198 """Check if it is possible to execute live migration. |
|
199 |
|
200 This checks if the live migration can succeed, based on the |
|
201 @@ -511,6 +511,7 @@ class XenAPIDriver(driver.ComputeDriver) |
|
202 :param instance: nova.db.sqlalchemy.models.Instance |
|
203 :param dest_check_data: result of check_can_live_migrate_destination |
|
204 includes the block_migration flag |
|
205 + :param block_device_info: result of _get_instance_block_device_info |
|
206 """ |
|
207 return self._vmops.check_can_live_migrate_source(context, instance, |
|
208 dest_check_data) |