Merge ~newell-jensen/maas:virsh-storage-pools into maas:master
- Git
- lp:~newell-jensen/maas
- virsh-storage-pools
- Merge into master
Proposed by
Newell Jensen
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Newell Jensen | ||||
Approved revision: | aaa4698a5d5bac7bdc8bcd4a4cecdff1d631f3b3 | ||||
Merge reported by: | MAAS Lander | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~newell-jensen/maas:virsh-storage-pools | ||||
Merge into: | maas:master | ||||
Diff against target: |
377 lines (+83/-12) 7 files modified
src/maasserver/api/tests/test_enlistment.py (+7/-0) src/maasserver/api/tests/test_machine.py (+4/-0) src/maasserver/forms/tests/test_pods.py (+9/-0) src/maasserver/rpc/tests/test_nodes.py (+1/-0) src/maasserver/websockets/handlers/tests/test_machine.py (+3/-0) src/provisioningserver/drivers/pod/tests/test_virsh.py (+21/-1) src/provisioningserver/drivers/pod/virsh.py (+38/-11) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
MAAS Lander | Needs Fixing | ||
Review via email: mp+342365@code.launchpad.net |
Commit message
LP: #1742708 -- Add ability to define default storage pool for Virsh pods.
Description of the change
To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b virsh-storage-pools lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
- aaa4698... by Newell Jensen
-
Fix failing tests.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/api/tests/test_enlistment.py b/src/maasserver/api/tests/test_enlistment.py |
2 | index 2f03848..5fc5563 100644 |
3 | --- a/src/maasserver/api/tests/test_enlistment.py |
4 | +++ b/src/maasserver/api/tests/test_enlistment.py |
5 | @@ -480,6 +480,7 @@ class SimpleUserLoggedInEnlistmentAPITest(APITestCase.ForUser): |
6 | def test_POST_simple_user_can_set_power_type_and_parameters(self): |
7 | new_power_address = factory.make_ip_address() # XXX: URLs don't work. |
8 | new_power_id = factory.make_name('power_id') |
9 | + new_default_storage_pool = factory.make_name('default_pool') |
10 | response = self.client.post( |
11 | reverse('machines_handler'), { |
12 | 'architecture': make_usable_architecture(self), |
13 | @@ -488,6 +489,7 @@ class SimpleUserLoggedInEnlistmentAPITest(APITestCase.ForUser): |
14 | { |
15 | "power_address": new_power_address, |
16 | "power_id": new_power_id, |
17 | + "default_storage_pool": new_default_storage_pool, |
18 | }), |
19 | 'mac_addresses': ['AA:BB:CC:DD:EE:FF'], |
20 | }) |
21 | @@ -500,6 +502,7 @@ class SimpleUserLoggedInEnlistmentAPITest(APITestCase.ForUser): |
22 | 'power_pass': '', |
23 | 'power_id': new_power_id, |
24 | 'power_address': new_power_address, |
25 | + 'default_storage_pool': new_default_storage_pool, |
26 | }, machine.power_parameters) |
27 | |
28 | def test_POST_returns_limited_fields(self): |
29 | @@ -614,6 +617,7 @@ class AdminLoggedInEnlistmentAPITest(APITestCase.ForAdmin): |
30 | new_power_id = factory.make_name('power_id') |
31 | new_power_address = factory.make_ipv4_address() |
32 | new_power_pass = factory.make_name('power_pass') |
33 | + new_default_storage_pool = factory.make_name('default_pool') |
34 | response = self.client.post( |
35 | reverse('machines_handler'), { |
36 | 'architecture': make_usable_architecture(self), |
37 | @@ -621,6 +625,8 @@ class AdminLoggedInEnlistmentAPITest(APITestCase.ForAdmin): |
38 | 'power_parameters_power_id': new_power_id, |
39 | 'power_parameters_power_pass': new_power_pass, |
40 | 'power_parameters_power_address': new_power_address, |
41 | + 'power_parameters_default_storage_pool': ( |
42 | + new_default_storage_pool), |
43 | 'mac_addresses': ['AA:BB:CC:DD:EE:FF'], |
44 | }) |
45 | |
46 | @@ -633,6 +639,7 @@ class AdminLoggedInEnlistmentAPITest(APITestCase.ForAdmin): |
47 | 'power_id': new_power_id, |
48 | 'power_pass': new_power_pass, |
49 | 'power_address': new_power_address, |
50 | + 'default_storage_pool': new_default_storage_pool, |
51 | }, |
52 | reload_object(machine).power_parameters) |
53 | |
54 | diff --git a/src/maasserver/api/tests/test_machine.py b/src/maasserver/api/tests/test_machine.py |
55 | index 65fd929..b143f22 100644 |
56 | --- a/src/maasserver/api/tests/test_machine.py |
57 | +++ b/src/maasserver/api/tests/test_machine.py |
58 | @@ -1280,12 +1280,15 @@ class TestMachineAPI(APITestCase.ForUser): |
59 | new_power_id = factory.make_name('power_id') |
60 | new_power_pass = factory.make_name('power_pass') |
61 | new_power_address = factory.make_ipv4_address() |
62 | + new_default_storage_pool = factory.make_name('default_pool') |
63 | response = self.client.put( |
64 | self.get_machine_uri(machine), |
65 | { |
66 | 'power_parameters_power_id': new_power_id, |
67 | 'power_parameters_power_pass': new_power_pass, |
68 | 'power_parameters_power_address': new_power_address, |
69 | + 'power_parameters_default_storage_pool': ( |
70 | + new_default_storage_pool), |
71 | } |
72 | ) |
73 | |
74 | @@ -1295,6 +1298,7 @@ class TestMachineAPI(APITestCase.ForUser): |
75 | 'power_id': new_power_id, |
76 | 'power_pass': new_power_pass, |
77 | 'power_address': new_power_address, |
78 | + 'default_storage_pool': new_default_storage_pool, |
79 | }, |
80 | reload_object(machine).power_parameters) |
81 | |
82 | diff --git a/src/maasserver/forms/tests/test_pods.py b/src/maasserver/forms/tests/test_pods.py |
83 | index 3808839..6eb2805 100644 |
84 | --- a/src/maasserver/forms/tests/test_pods.py |
85 | +++ b/src/maasserver/forms/tests/test_pods.py |
86 | @@ -94,6 +94,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
87 | pod_ip_adddress = factory.make_ipv4_address() |
88 | pod_power_address = 'qemu+ssh://user@%s/system' % pod_ip_adddress |
89 | pod_password = factory.make_name('password') |
90 | + pod_default_storage_pool = factory.make_name('default_pool') |
91 | pod_tags = [ |
92 | factory.make_name("tag") |
93 | for _ in range(3) |
94 | @@ -104,6 +105,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
95 | 'type': pod_type, |
96 | 'power_address': pod_power_address, |
97 | 'power_pass': pod_password, |
98 | + 'default_storage_pool': pod_default_storage_pool, |
99 | 'ip_address': pod_ip_adddress, |
100 | 'tags': ",".join(pod_tags), |
101 | 'cpu_over_commit_ratio': pod_cpu_over_commit_ratio, |
102 | @@ -168,6 +170,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
103 | power_parameters=Equals({ |
104 | 'power_address': pod_info['power_address'], |
105 | 'power_pass': pod_info['power_pass'], |
106 | + 'default_storage_pool': pod_info['default_storage_pool'], |
107 | }), |
108 | ip_address=MatchesStructure(ip=Equals(pod_info['ip_address'])), |
109 | )) |
110 | @@ -208,6 +211,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
111 | power_parameters=Equals({ |
112 | 'power_address': pod_info['power_address'], |
113 | 'power_pass': pod_info['power_pass'], |
114 | + 'default_storage_pool': pod_info['default_storage_pool'], |
115 | }), |
116 | ip_address=MatchesStructure(ip=Equals(pod_info['ip_address'])), |
117 | )) |
118 | @@ -239,6 +243,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
119 | power_parameters=Equals({ |
120 | 'power_address': pod_info['power_address'], |
121 | 'power_pass': pod_info['power_pass'], |
122 | + 'default_storage_pool': pod_info['default_storage_pool'], |
123 | }), |
124 | ip_address=MatchesStructure(ip=Equals(pod_info['ip_address'])), |
125 | )) |
126 | @@ -288,6 +293,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
127 | power_parameters=Equals({ |
128 | 'power_address': pod_info['power_address'], |
129 | 'power_pass': pod_info['power_pass'], |
130 | + 'default_storage_pool': pod_info['default_storage_pool'], |
131 | }), |
132 | ip_address=MatchesStructure(ip=Equals(pod_info['ip_address'])), |
133 | )) |
134 | @@ -339,6 +345,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
135 | power_parameters={ |
136 | 'power_address': pod_info['power_address'], |
137 | 'power_pass': pod_info['power_pass'], |
138 | + 'default_storage_pool': pod_info['default_storage_pool'], |
139 | }) |
140 | request = MagicMock() |
141 | request.user = factory.make_User() |
142 | @@ -375,6 +382,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
143 | power_parameters=Equals({ |
144 | 'power_address': pod_info['power_address'], |
145 | 'power_pass': pod_info['power_pass'], |
146 | + 'default_storage_pool': pod_info['default_storage_pool'], |
147 | }), |
148 | ip_address=MatchesStructure(ip=Equals(pod_info['ip_address'])), |
149 | )) |
150 | @@ -425,6 +433,7 @@ class TestPodForm(MAASTransactionServerTestCase): |
151 | power_parameters=Equals({ |
152 | 'power_address': pod_info['power_address'], |
153 | 'power_pass': pod_info['power_pass'], |
154 | + 'default_storage_pool': pod_info['default_storage_pool'], |
155 | }), |
156 | ip_address=MatchesStructure(ip=Equals(pod_info['ip_address'])), |
157 | )) |
158 | diff --git a/src/maasserver/rpc/tests/test_nodes.py b/src/maasserver/rpc/tests/test_nodes.py |
159 | index c8034b1..f8f99ff 100644 |
160 | --- a/src/maasserver/rpc/tests/test_nodes.py |
161 | +++ b/src/maasserver/rpc/tests/test_nodes.py |
162 | @@ -240,6 +240,7 @@ class TestCreateNode(MAASTransactionServerTestCase): |
163 | 'power_address': factory.make_ip_address(), # XXX: URLs break. |
164 | 'power_pass': factory.make_name('power_pass'), |
165 | 'power_id': factory.make_name('power_id'), |
166 | + 'default_storage_pool': factory.make_name('default_pool'), |
167 | } |
168 | |
169 | node = create_node( |
170 | diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py |
171 | index 27bc195..b027541 100644 |
172 | --- a/src/maasserver/websockets/handlers/tests/test_machine.py |
173 | +++ b/src/maasserver/websockets/handlers/tests/test_machine.py |
174 | @@ -1853,6 +1853,7 @@ class TestMachineHandler(MAASServerTestCase): |
175 | power_id = factory.make_name('power_id') |
176 | power_pass = factory.make_name('power_pass') |
177 | power_address = factory.make_ipv4_address() |
178 | + default_storage_pool = factory.make_name('default_pool') |
179 | node_data["hostname"] = new_hostname |
180 | node_data["architecture"] = new_architecture |
181 | node_data["zone"] = { |
182 | @@ -1863,6 +1864,7 @@ class TestMachineHandler(MAASServerTestCase): |
183 | 'power_id': power_id, |
184 | 'power_pass': power_pass, |
185 | 'power_address': power_address, |
186 | + 'default_storage_pool': default_storage_pool, |
187 | } |
188 | updated_node = handler.update(node_data) |
189 | self.expectThat(updated_node["hostname"], Equals(new_hostname)) |
190 | @@ -1873,6 +1875,7 @@ class TestMachineHandler(MAASServerTestCase): |
191 | 'power_id': power_id, |
192 | 'power_pass': power_pass, |
193 | 'power_address': power_address, |
194 | + 'default_storage_pool': default_storage_pool, |
195 | })) |
196 | |
197 | def test_update_adds_tags_to_node(self): |
198 | diff --git a/src/provisioningserver/drivers/pod/tests/test_virsh.py b/src/provisioningserver/drivers/pod/tests/test_virsh.py |
199 | index d73ebc3..44cd8d7 100644 |
200 | --- a/src/provisioningserver/drivers/pod/tests/test_virsh.py |
201 | +++ b/src/provisioningserver/drivers/pod/tests/test_virsh.py |
202 | @@ -1416,12 +1416,29 @@ class TestVirshPodDriver(MAASTestCase): |
203 | yield driver.discover(system_id, context) |
204 | |
205 | @inlineCallbacks |
206 | + def test_discover_errors_on_incorrect_default_storage_pool(self): |
207 | + driver = VirshPodDriver() |
208 | + system_id = factory.make_name('system_id') |
209 | + context = { |
210 | + 'power_address': factory.make_name('power_address'), |
211 | + 'power_pass': factory.make_name('power_pass'), |
212 | + 'default_storage_pool': factory.make_name('non-existent-pool') |
213 | + } |
214 | + mock_login = self.patch(virsh.VirshSSH, 'login') |
215 | + mock_login.return_value = True |
216 | + mock_run = self.patch(virsh.VirshSSH, 'run') |
217 | + mock_run.return_value = SAMPLE_POOLLIST |
218 | + with ExpectedException(virsh.VirshError): |
219 | + yield driver.discover(system_id, context) |
220 | + |
221 | + @inlineCallbacks |
222 | def test_discover(self): |
223 | driver = VirshPodDriver() |
224 | system_id = factory.make_name('system_id') |
225 | context = { |
226 | 'power_address': factory.make_name('power_address'), |
227 | - 'power_pass': factory.make_name('power_pass') |
228 | + 'power_pass': factory.make_name('power_pass'), |
229 | + 'default_storage_pool': 'default' |
230 | } |
231 | machines = [ |
232 | factory.make_name('machine') |
233 | @@ -1429,6 +1446,8 @@ class TestVirshPodDriver(MAASTestCase): |
234 | ] |
235 | mock_login = self.patch(virsh.VirshSSH, 'login') |
236 | mock_login.return_value = True |
237 | + mock_run = self.patch(virsh.VirshSSH, 'run') |
238 | + mock_run.return_value = SAMPLE_POOLLIST |
239 | mock_get_pod_resources = self.patch( |
240 | virsh.VirshSSH, 'get_pod_resources') |
241 | mock_get_pod_hints = self.patch( |
242 | @@ -1451,6 +1470,7 @@ class TestVirshPodDriver(MAASTestCase): |
243 | call(machines[1]), |
244 | call(machines[2]))) |
245 | self.expectThat(['virtual'], Equals(discovered_pod.tags)) |
246 | + self.expectThat(driver.default_storage_pool, Equals('default')) |
247 | |
248 | @inlineCallbacks |
249 | def test_compose(self): |
250 | diff --git a/src/provisioningserver/drivers/pod/virsh.py b/src/provisioningserver/drivers/pod/virsh.py |
251 | index a32d935..afdca25 100644 |
252 | --- a/src/provisioningserver/drivers/pod/virsh.py |
253 | +++ b/src/provisioningserver/drivers/pod/virsh.py |
254 | @@ -295,11 +295,16 @@ class VirshSSH(pexpect.spawn): |
255 | machines = machines.strip().splitlines() |
256 | return [m for m in machines if m.startswith(self.dom_prefix)] |
257 | |
258 | - def list_pools(self): |
259 | - """Lists all pools in the pod.""" |
260 | + def list_pools(self, default_pool=None): |
261 | + """Lists all pools in the pod. |
262 | + |
263 | + If default_pool is set, only return this pool. |
264 | + """ |
265 | keys = ['Name'] |
266 | output = self.run(['pool-list']) |
267 | pools = self.get_column_values(output, keys) |
268 | + if default_pool: |
269 | + return [p[0] for p in pools if p[0] == default_pool] |
270 | return [p[0] for p in pools] |
271 | |
272 | def list_machine_block_devices(self, machine): |
273 | @@ -372,10 +377,10 @@ class VirshSSH(pexpect.spawn): |
274 | # Memory in MiB. |
275 | return int(KiB / 1024) |
276 | |
277 | - def get_pod_pool_size_map(self, key): |
278 | + def get_pod_pool_size_map(self, key, default_pool=None): |
279 | """Return the mapping for a size calculation based on key.""" |
280 | pools = {} |
281 | - for pool in self.list_pools(): |
282 | + for pool in self.list_pools(default_pool): |
283 | output = self.run(['pool-info', pool]).strip() |
284 | if output is None: |
285 | # Skip if cannot get more information. |
286 | @@ -445,6 +450,11 @@ class VirshSSH(pexpect.spawn): |
287 | # name, that MAAS understands. |
288 | return ARCH_FIX.get(arch, arch) |
289 | |
290 | + def check_default_storage_pool(self, default_pool): |
291 | + """Check that default_pool exists.""" |
292 | + if not len(self.list_pools(default_pool)): |
293 | + raise VirshError("Storage pool '%s' doesn't exist." % default_pool) |
294 | + |
295 | def get_pod_resources(self): |
296 | """Get the pod resources.""" |
297 | discovered_pod = DiscoveredPod( |
298 | @@ -596,17 +606,17 @@ class VirshSSH(pexpect.spawn): |
299 | return False |
300 | return True |
301 | |
302 | - def get_usable_pool(self, size): |
303 | + def get_usable_pool(self, size, default_pool=None): |
304 | """Return the pool that as enough space for `size`.""" |
305 | - pools = self.get_pod_pool_size_map("Available") |
306 | + pools = self.get_pod_pool_size_map("Available", default_pool) |
307 | for pool, available in pools.items(): |
308 | if size <= available: |
309 | return pool |
310 | return None |
311 | |
312 | - def create_local_volume(self, size): |
313 | + def create_local_volume(self, size, default_pool=None): |
314 | """Create a local volume with `size`.""" |
315 | - usable_pool = self.get_usable_pool(size) |
316 | + usable_pool = self.get_usable_pool(size, default_pool) |
317 | if usable_pool is None: |
318 | return None |
319 | volume = str(uuid.uuid4()) |
320 | @@ -718,7 +728,7 @@ class VirshSSH(pexpect.spawn): |
321 | idx = (idx // 26) - 1 |
322 | return "vd" + name |
323 | |
324 | - def create_domain(self, request): |
325 | + def create_domain(self, request, default_pool=None): |
326 | """Create a domain based on the `request` with hostname. |
327 | |
328 | For now this just uses `get_best_network` to connect the interfaces |
329 | @@ -730,7 +740,7 @@ class VirshSSH(pexpect.spawn): |
330 | created_disks = [] |
331 | for idx, disk in enumerate(request.block_devices): |
332 | try: |
333 | - disk_info = self.create_local_volume(disk.size) |
334 | + disk_info = self.create_local_volume(disk.size, default_pool) |
335 | except Exception: |
336 | self.cleanup_disks(created_disks) |
337 | raise |
338 | @@ -802,7 +812,11 @@ class VirshPodDriver(PodDriver): |
339 | make_setting_field( |
340 | 'power_id', "Virsh VM ID", scope=SETTING_SCOPE.NODE, |
341 | required=True), |
342 | + make_setting_field( |
343 | + 'default_storage_pool', "Default storage pool (optional)", |
344 | + required=False), |
345 | ] |
346 | + default_storage_pool = None |
347 | ip_extractor = make_ip_extractor( |
348 | 'power_address', IP_EXTRACTOR_PATTERNS.URL) |
349 | |
350 | @@ -903,6 +917,18 @@ class VirshPodDriver(PodDriver): |
351 | """ |
352 | conn = yield self.get_virsh_connection(context) |
353 | |
354 | + # Check and set default storage pool. |
355 | + self.default_storage_pool = context.get('default_storage_pool') |
356 | + if self.default_storage_pool: |
357 | + try: |
358 | + yield deferToThread( |
359 | + conn.check_default_storage_pool, self.default_storage_pool) |
360 | + except VirshError: |
361 | + # Set the default_storage_pool to None since |
362 | + # one wasn't found and raise the error. |
363 | + self.default_storage_pool = None |
364 | + raise |
365 | + |
366 | # Discover pod resources. |
367 | discovered_pod = yield deferToThread(conn.get_pod_resources) |
368 | |
369 | @@ -930,7 +956,8 @@ class VirshPodDriver(PodDriver): |
370 | def compose(self, system_id, context, request): |
371 | """Compose machine.""" |
372 | conn = yield self.get_virsh_connection(context) |
373 | - created_machine = yield deferToThread(conn.create_domain, request) |
374 | + created_machine = yield deferToThread( |
375 | + conn.create_domain, request, self.default_storage_pool) |
376 | hints = yield deferToThread(conn.get_pod_hints) |
377 | return created_machine, hints |
378 |
UNIT TESTS
-b virsh-storage-pools lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED maas-ci- jenkins. internal: 8080/job/ maas/job/ branch- tester/ 2180/console 9660df60b3fecc1 6ce1cc958c
LOG: http://
COMMIT: 03cc1edfb2ad561