Merge ~newell-jensen/maas:lxd-pod-driver-discovery into maas:master

Proposed by Newell Jensen
Status: Merged
Approved by: Newell Jensen
Approved revision: 93c1d03c6041a1119ae1b3d8045f760696d9309b
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~newell-jensen/maas:lxd-pod-driver-discovery
Merge into: maas:master
Diff against target: 702 lines (+406/-81)
2 files modified
src/provisioningserver/drivers/pod/lxd.py (+170/-23)
src/provisioningserver/drivers/pod/tests/test_lxd.py (+236/-58)
Reviewer Review Type Date Requested Status
Lee Trager (community) Approve
MAAS Lander Approve
Review via email: mp+381791@code.launchpad.net

Commit message

Discover Pod storage pools and machines during the Pod adding/refresh process so that machines not known to MAAS are commissioned.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lxd-pod-driver-discovery lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/7356/console
COMMIT: 4d2cd8ddd424eaa39f60eed59be92e81b35f570d

review: Needs Fixing
Revision history for this message
Lee Trager (ltrager) wrote :

I think this branch also needs to configure an LXD profile for MAAS use if one isn't already created. Otherwise compose won't know which storage pool or parent interface to use when composing a machine.

Also a couple of questions below.

Revision history for this message
Newell Jensen (newell-jensen) wrote :

The LXD profile is going to be included with the branch that does compose and decompose, which this branch does not do. This branch is for adding discovered resources.

See inline for my responses on items that I don't agree or an explanation is needed (will change the others).

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lxd-pod-driver-discovery lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 208674ac978485f90b72e35b351ab9ba432e18bb

review: Approve
Revision history for this message
Lee Trager (ltrager) wrote :

Looks good, just a couple of things inline. I think we should ask stgraber about the vid but we can get that in a later branch.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lxd-pod-driver-discovery lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 93c1d03c6041a1119ae1b3d8045f760696d9309b

review: Approve
Revision history for this message
Lee Trager (ltrager) wrote :

Thanks for the fixes! LGTM!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/provisioningserver/drivers/pod/lxd.py b/src/provisioningserver/drivers/pod/lxd.py
2index 28c441f..70a1cbb 100644
3--- a/src/provisioningserver/drivers/pod/lxd.py
4+++ b/src/provisioningserver/drivers/pod/lxd.py
5@@ -20,20 +20,32 @@ from provisioningserver.drivers import (
6 )
7 from provisioningserver.drivers.pod import (
8 Capabilities,
9+ DiscoveredMachine,
10+ DiscoveredMachineBlockDevice,
11+ DiscoveredMachineInterface,
12 DiscoveredPod,
13+ DiscoveredPodStoragePool,
14 PodDriver,
15 )
16+from provisioningserver.logger import get_maas_logger
17 from provisioningserver.maas_certificates import (
18 MAAS_CERTIFICATE,
19 MAAS_PRIVATE_KEY,
20 )
21 from provisioningserver.utils import kernel_to_debian_architecture, typed
22+from provisioningserver.utils.ipaddr import get_vid_from_ifname
23+from provisioningserver.utils.twisted import asynchronous
24
25-# LXD Status Codes
26+maaslog = get_maas_logger("drivers.pod.lxd")
27+
28+# LXD status codes
29 LXD_VM_POWER_STATE = {101: "on", 102: "off", 103: "on", 110: "off"}
30
31+# LXD vm disk path
32+LXD_VM_ID_PATH = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_lxd_"
33+
34
35-class LXDError(Exception):
36+class LXDPodError(Exception):
37 """Failure communicating to LXD. """
38
39
40@@ -86,8 +98,8 @@ class LXDPodDriver(PodDriver):
41
42 @typed
43 @inlineCallbacks
44- def get_client(self, system_id: str, context: dict):
45- """Connect and return PyLXD client."""
46+ def get_client(self, pod_id: str, context: dict):
47+ """Connect pylxd client."""
48 endpoint = self.get_url(context)
49 password = context.get("password")
50 try:
51@@ -101,62 +113,165 @@ class LXDPodDriver(PodDriver):
52 if password:
53 yield deferToThread(client.authenticate, password)
54 else:
55- raise LXDError(
56- f"{system_id}: Certificate is not trusted and no password was given."
57+ raise LXDPodError(
58+ f"Pod {pod_id}: Certificate is not trusted and no password was given."
59 )
60- return client
61 except ClientConnectionFailed:
62- raise LXDError(
63- f"{system_id}: Failed to connect to the LXD REST API."
64+ raise LXDPodError(
65+ f"Pod {pod_id}: Failed to connect to the LXD REST API."
66 )
67+ return client
68
69 @typed
70 @inlineCallbacks
71- def get_machine(self, system_id: str, context: dict):
72+ def get_machine(self, pod_id: str, context: dict):
73 """Retrieve LXD VM."""
74- client = yield self.get_client(system_id, context)
75+ client = yield self.get_client(pod_id, context)
76 instance_name = context.get("instance_name")
77 try:
78 machine = yield deferToThread(
79 client.virtual_machines.get, instance_name
80 )
81 except NotFound:
82- raise LXDError(f"{system_id}: LXD VM {instance_name} not found.")
83+ raise LXDPodError(
84+ f"Pod {pod_id}: LXD VM {instance_name} not found."
85+ )
86 return machine
87
88+ def get_discovered_machine(
89+ self, machine, storage_pools, request=None, cpu_speed=0
90+ ):
91+ """Get the discovered machine."""
92+ # Check the power state first.
93+ state = machine.status_code
94+ try:
95+ power_state = LXD_VM_POWER_STATE[state]
96+ except KeyError:
97+ maaslog.error(
98+ f"{machine.name}: Unknown power status code: {state}"
99+ )
100+ power_state = "unknown"
101+
102+ expanded_config = machine.expanded_config
103+ expanded_devices = machine.expanded_devices
104+
105+ # Discover block devices.
106+ block_devices = []
107+ for idx, device in enumerate(expanded_devices):
108+ # Block device.
109+ # When request is provided map the tags from the request block
110+ # devices to the discovered block devices. This ensures that
111+ # composed machine has the requested tags on the block device.
112+ tags = []
113+ if request is not None:
114+ tags = request.block_devices[idx].tags
115+
116+ device_info = expanded_devices[device]
117+ if device_info["type"] == "disk":
118+ # Default disk size is 10GB.
119+ size = device_info.get("size", 10 * 1000 ** 3)
120+ pool = device_info.get("pool")
121+ block_devices.append(
122+ DiscoveredMachineBlockDevice(
123+ model=None,
124+ serial=None,
125+ id_path=LXD_VM_ID_PATH + device,
126+ size=size,
127+ tags=tags,
128+ storage_pool=pool,
129+ )
130+ )
131+
132+ # Discover interfaces.
133+ interfaces = []
134+ boot = True
135+ for configuration in expanded_config:
136+ if configuration.endswith("hwaddr"):
137+ mac = expanded_config[configuration]
138+ name = configuration.split(".")[1]
139+ nictype = expanded_devices[name]["nictype"]
140+ interfaces.append(
141+ DiscoveredMachineInterface(
142+ mac_address=mac,
143+ vid=get_vid_from_ifname(name),
144+ boot=boot,
145+ attach_type=nictype,
146+ attach_name=name,
147+ )
148+ )
149+ boot = False
150+
151+ return DiscoveredMachine(
152+ hostname=machine.name,
153+ architecture=kernel_to_debian_architecture(machine.architecture),
154+ # 1 core and 1GiB of memory (we need it in MiB) is default for
155+ # LXD if not specified.
156+ cores=int(expanded_config.get("limits.cpu", 1)),
157+ memory=int(expanded_config.get("limits.memory", 1024)),
158+ cpu_speed=cpu_speed,
159+ interfaces=interfaces,
160+ block_devices=block_devices,
161+ power_state=power_state,
162+ power_parameters={"instance_name": machine.name},
163+ tags=[],
164+ )
165+
166+ def get_discovered_pod_storage_pool(self, storage_pool):
167+ """Get the Pod storage pool."""
168+ storage_pool_config = storage_pool.config
169+ # Sometimes the config is empty, use get() method on the dictionary in case.
170+ storage_pool_path = storage_pool_config.get("source")
171+ storage_pool_resources = storage_pool.resources.get()
172+ total_storage = storage_pool_resources.space["total"]
173+
174+ return DiscoveredPodStoragePool(
175+ # No ID's with LXD so we are just using the name as the ID.
176+ id=storage_pool.name,
177+ name=storage_pool.name,
178+ path=storage_pool_path,
179+ type=storage_pool.driver,
180+ storage=total_storage,
181+ )
182+
183 @typed
184+ @asynchronous
185 @inlineCallbacks
186- def power_on(self, system_id: str, context: dict):
187+ def power_on(self, pod_id: str, context: dict):
188 """Power on LXD VM."""
189- machine = yield deferToThread(self.get_machine, system_id, context)
190+ machine = yield self.get_machine(pod_id, context)
191 if LXD_VM_POWER_STATE[machine.status_code] == "off":
192 yield deferToThread(machine.start)
193
194 @typed
195+ @asynchronous
196 @inlineCallbacks
197- def power_off(self, system_id: str, context: dict):
198+ def power_off(self, pod_id: str, context: dict):
199 """Power off LXD VM."""
200- machine = yield deferToThread(self.get_machine, system_id, context)
201+ machine = yield self.get_machine(pod_id, context)
202 if LXD_VM_POWER_STATE[machine.status_code] == "on":
203 yield deferToThread(machine.stop)
204
205 @typed
206+ @asynchronous
207 @inlineCallbacks
208- def power_query(self, system_id: str, context: dict):
209+ def power_query(self, pod_id: str, context: dict):
210 """Power query LXD VM."""
211- machine = yield deferToThread(self.get_machine, system_id, context)
212+ machine = yield self.get_machine(pod_id, context)
213 state = machine.status_code
214 try:
215 return LXD_VM_POWER_STATE[state]
216 except KeyError:
217- raise LXDError(f"{system_id}: Unknown power status code: {state}")
218+ raise LXDPodError(
219+ f"Pod {pod_id}: Unknown power status code: {state}"
220+ )
221
222 @inlineCallbacks
223 def discover(self, pod_id, context):
224 """Discover all Pod host resources."""
225+ # Connect to the Pod and make sure it is valid.
226 client = yield self.get_client(pod_id, context)
227 if not client.has_api_extension("virtual-machines"):
228- raise LXDError(
229+ raise LXDPodError(
230 "Please upgrade your LXD host to 3.19+ for virtual machine support."
231 )
232 resources = yield deferToThread(lambda: client.resources)
233@@ -168,7 +283,7 @@ class LXDPodDriver(PodDriver):
234
235 # After the region creates the Pod object it will sync LXD commissioning
236 # data for all hardware information.
237- return DiscoveredPod(
238+ discovered_pod = DiscoveredPod(
239 architectures=[
240 kernel_to_debian_architecture(arch)
241 for arch in client.host_info["environment"]["architectures"]
242@@ -183,14 +298,46 @@ class LXDPodDriver(PodDriver):
243 ],
244 )
245
246+ # Check that we have at least one storage pool. If not, create it.
247+ pools = yield deferToThread(client.storage_pools.all)
248+ if not len(pools):
249+ yield deferToThread(
250+ client.storage_pools.create, {"name": "maas", "driver": "dir"}
251+ )
252+
253+ # Discover Storage Pools.
254+ pools = []
255+ storage_pools = yield deferToThread(client.storage_pools.all)
256+ for storage_pool in storage_pools:
257+ discovered_storage_pool = self.get_discovered_pod_storage_pool(
258+ storage_pool
259+ )
260+ pools.append(discovered_storage_pool)
261+ discovered_pod.storage_pools = pools
262+
263+ # Discover VMs.
264+ machines = []
265+ virtual_machines = yield deferToThread(client.virtual_machines.all)
266+ for virtual_machine in virtual_machines:
267+ discovered_machine = self.get_discovered_machine(
268+ virtual_machine,
269+ storage_pools=discovered_pod.storage_pools,
270+ cpu_speed=discovered_pod.cpu_speed,
271+ )
272+ machines.append(discovered_machine)
273+ discovered_pod.machines = machines
274+
275+ # Return the DiscoveredPod.
276+ return discovered_pod
277+
278 @inlineCallbacks
279- def compose(self, system_id, context, request):
280+ def compose(self, pod_id, context, request):
281 """Compose a virtual machine."""
282 # abstract method, will update in subsequent branch.
283 pass
284
285 @inlineCallbacks
286- def decompose(self, system_id, context):
287+ def decompose(self, pod_id, context):
288 """Decompose a virtual machine machine."""
289 # abstract method, will update in subsequent branch.
290 pass
291diff --git a/src/provisioningserver/drivers/pod/tests/test_lxd.py b/src/provisioningserver/drivers/pod/tests/test_lxd.py
292index 904c7af..7c81873 100644
293--- a/src/provisioningserver/drivers/pod/tests/test_lxd.py
294+++ b/src/provisioningserver/drivers/pod/tests/test_lxd.py
295@@ -8,23 +8,20 @@ __all__ = []
296 from os.path import join
297 from unittest.mock import Mock
298
299-from testtools.matchers import Equals
300+from testtools.matchers import Equals, MatchesStructure
301 from testtools.testcase import ExpectedException
302 from twisted.internet.defer import inlineCallbacks
303
304-# XXX - Remove provisioningserver.drivers.power from testing in this file
305-# and rename lxd_pod_module.
306 from maastesting.factory import factory
307 from maastesting.matchers import MockCalledOnceWith
308 from maastesting.testcase import MAASTestCase, MAASTwistedRunTest
309 from provisioningserver.drivers.pod import Capabilities
310 from provisioningserver.drivers.pod import lxd as lxd_pod_module
311-from provisioningserver.drivers.pod.lxd import LXDPodDriver
312-from provisioningserver.drivers.power import lxd as lxd_module
313 from provisioningserver.maas_certificates import (
314 MAAS_CERTIFICATE,
315 MAAS_PRIVATE_KEY,
316 )
317+from provisioningserver.utils import kernel_to_debian_architecture
318
319
320 class TestLXDPodDriver(MAASTestCase):
321@@ -32,7 +29,7 @@ class TestLXDPodDriver(MAASTestCase):
322 run_tests_with = MAASTwistedRunTest.make_factory(timeout=5)
323
324 def test_missing_packages(self):
325- driver = LXDPodDriver()
326+ driver = lxd_pod_module.LXDPodDriver()
327 missing = driver.detect_missing_packages()
328 self.assertItemsEqual([], missing)
329
330@@ -56,7 +53,7 @@ class TestLXDPodDriver(MAASTestCase):
331 )
332
333 def test_get_url(self):
334- driver = lxd_module.LXDPowerDriver()
335+ driver = lxd_pod_module.LXDPodDriver()
336 context = {"power_address": factory.make_hostname()}
337
338 # Test ip adds protocol and port
339@@ -86,15 +83,13 @@ class TestLXDPodDriver(MAASTestCase):
340 @inlineCallbacks
341 def test__get_client(self):
342 context = self.make_parameters_context()
343- Client = self.patch(lxd_module, "Client")
344+ Client = self.patch(lxd_pod_module, "Client")
345 client = Client.return_value
346 client.has_api_extension.return_value = True
347 client.trusted = False
348- driver = lxd_module.LXDPowerDriver()
349+ driver = lxd_pod_module.LXDPodDriver()
350 endpoint = driver.get_url(context)
351- returned_client = yield driver.get_client(
352- factory.make_name("system_id"), context
353- )
354+ returned_client = yield driver.get_client(None, context)
355 self.assertThat(
356 Client,
357 MockCalledOnceWith(
358@@ -112,111 +107,109 @@ class TestLXDPodDriver(MAASTestCase):
359 def test_get_client_raises_error_when_not_trusted_and_no_password(self):
360 context = self.make_parameters_context()
361 context["password"] = None
362- system_id = factory.make_name("system_id")
363- Client = self.patch(lxd_module, "Client")
364+ pod_id = factory.make_name("pod_id")
365+ Client = self.patch(lxd_pod_module, "Client")
366 client = Client.return_value
367- client.has_api_extension.return_value = True
368 client.trusted = False
369- driver = lxd_module.LXDPowerDriver()
370- error_msg = f"{system_id}: Certificate is not trusted and no password was given."
371- with ExpectedException(lxd_module.LXDError, error_msg):
372- yield driver.get_machine(system_id, context)
373- self.assertThat(
374- client.has_api_extension, MockCalledOnceWith("virtual-machines")
375- )
376+ driver = lxd_pod_module.LXDPodDriver()
377+ error_msg = f"Pod {pod_id}: Certificate is not trusted and no password was given."
378+ with ExpectedException(lxd_pod_module.LXDPodError, error_msg):
379+ yield driver.get_client(pod_id, context)
380
381 @inlineCallbacks
382 def test_get_client_raises_error_when_cannot_connect(self):
383 context = self.make_parameters_context()
384- system_id = factory.make_name("system_id")
385- Client = self.patch(lxd_module, "Client")
386- Client.side_effect = lxd_module.ClientConnectionFailed()
387- driver = lxd_module.LXDPowerDriver()
388- error_msg = f"{system_id}: Failed to connect to the LXD REST API."
389- with ExpectedException(lxd_module.LXDError, error_msg):
390- yield driver.get_client(system_id, context)
391+ pod_id = factory.make_name("pod_id")
392+ Client = self.patch(lxd_pod_module, "Client")
393+ Client.side_effect = lxd_pod_module.ClientConnectionFailed()
394+ driver = lxd_pod_module.LXDPodDriver()
395+ error_msg = f"Pod {pod_id}: Failed to connect to the LXD REST API."
396+ with ExpectedException(lxd_pod_module.LXDPodError, error_msg):
397+ yield driver.get_client(pod_id, context)
398
399 @inlineCallbacks
400 def test__get_machine(self):
401 context = self.make_parameters_context()
402- system_id = factory.make_name("system_id")
403- driver = lxd_module.LXDPowerDriver()
404+ driver = lxd_pod_module.LXDPodDriver()
405 Client = self.patch(driver, "get_client")
406 client = Client.return_value
407 mock_machine = Mock()
408 client.virtual_machines.get.return_value = mock_machine
409- returned_machine = yield driver.get_machine(system_id, context)
410- self.assertThat(Client, MockCalledOnceWith(system_id, context))
411+ returned_machine = yield driver.get_machine(None, context)
412+ self.assertThat(Client, MockCalledOnceWith(None, context))
413 self.assertEquals(mock_machine, returned_machine)
414
415 @inlineCallbacks
416 def test_get_machine_raises_error_when_machine_not_found(self):
417 context = self.make_parameters_context()
418- system_id = factory.make_name("system_id")
419+ pod_id = factory.make_name("pod_id")
420 instance_name = context.get("instance_name")
421- driver = lxd_module.LXDPowerDriver()
422+ driver = lxd_pod_module.LXDPodDriver()
423 Client = self.patch(driver, "get_client")
424 client = Client.return_value
425- client.virtual_machines.get.side_effect = lxd_module.NotFound("Error")
426- error_msg = f"{system_id}: LXD VM {instance_name} not found."
427- with ExpectedException(lxd_module.LXDError, error_msg):
428- yield driver.get_machine(system_id, context)
429+ client.virtual_machines.get.side_effect = lxd_pod_module.NotFound(
430+ "Error"
431+ )
432+ error_msg = f"Pod {pod_id}: LXD VM {instance_name} not found."
433+ with ExpectedException(lxd_pod_module.LXDPodError, error_msg):
434+ yield driver.get_machine(pod_id, context)
435
436 @inlineCallbacks
437 def test__power_on(self):
438 context = self.make_parameters_context()
439- system_id = factory.make_name("system_id")
440- driver = lxd_module.LXDPowerDriver()
441+ driver = lxd_pod_module.LXDPodDriver()
442 mock_machine = self.patch(driver, "get_machine").return_value
443 mock_machine.status_code = 110
444- yield driver.power_on(system_id, context)
445+ yield driver.power_on(None, context)
446 self.assertThat(mock_machine.start, MockCalledOnceWith())
447
448 @inlineCallbacks
449 def test__power_off(self):
450 context = self.make_parameters_context()
451- system_id = factory.make_name("system_id")
452- driver = lxd_module.LXDPowerDriver()
453+ driver = lxd_pod_module.LXDPodDriver()
454 mock_machine = self.patch(driver, "get_machine").return_value
455 mock_machine.status_code = 103
456- yield driver.power_off(system_id, context)
457+ yield driver.power_off(None, context)
458 self.assertThat(mock_machine.stop, MockCalledOnceWith())
459
460 @inlineCallbacks
461 def test__power_query(self):
462 context = self.make_parameters_context()
463- system_id = factory.make_name("system_id")
464- driver = lxd_module.LXDPowerDriver()
465+ driver = lxd_pod_module.LXDPodDriver()
466 mock_machine = self.patch(driver, "get_machine").return_value
467 mock_machine.status_code = 103
468- state = yield driver.power_query(system_id, context)
469+ state = yield driver.power_query(None, context)
470 self.assertThat(state, Equals("on"))
471
472 @inlineCallbacks
473- def test__power_query_raises_error_on_unknown_state(self):
474+ def test_power_query_raises_error_on_unknown_state(self):
475 context = self.make_parameters_context()
476- system_id = factory.make_name("system_id")
477- driver = lxd_module.LXDPowerDriver()
478+ pod_id = factory.make_name("pod_id")
479+ driver = lxd_pod_module.LXDPodDriver()
480 mock_machine = self.patch(driver, "get_machine").return_value
481 mock_machine.status_code = 106
482- error_msg = f"{system_id}: Unknown power status code: {mock_machine.status_code}"
483- with ExpectedException(lxd_module.LXDError, error_msg):
484- yield driver.power_query(system_id, context)
485+ error_msg = f"Pod {pod_id}: Unknown power status code: {mock_machine.status_code}"
486+ with ExpectedException(lxd_pod_module.LXDPodError, error_msg):
487+ yield driver.power_query(pod_id, context)
488
489 @inlineCallbacks
490- def test__discover_requires_client_to_have_vm_support(self):
491+ def test_discover_requires_client_to_have_vm_support(self):
492 context = self.make_parameters_context()
493- driver = LXDPodDriver()
494+ driver = lxd_pod_module.LXDPodDriver()
495 Client = self.patch(lxd_pod_module, "Client")
496 client = Client.return_value
497 client.has_api_extension.return_value = False
498- with ExpectedException(lxd_pod_module.LXDError):
499+ error_msg = "Please upgrade your LXD host to *."
500+ with ExpectedException(lxd_pod_module.LXDPodError, error_msg):
501 yield driver.discover(None, context)
502+ self.assertThat(
503+ client.has_api_extension, MockCalledOnceWith("virtual-machines")
504+ )
505
506 @inlineCallbacks
507 def test__discover(self):
508 context = self.make_parameters_context()
509- driver = LXDPodDriver()
510+ driver = lxd_pod_module.LXDPodDriver()
511 Client = self.patch(lxd_pod_module, "Client")
512 client = Client.return_value
513 client.has_api_extension.return_value = True
514@@ -260,3 +253,188 @@ class TestLXDPodDriver(MAASTestCase):
515 self.assertItemsEqual([], discovered_pod.machines)
516 self.assertItemsEqual([], discovered_pod.tags)
517 self.assertItemsEqual([], discovered_pod.storage_pools)
518+
519+ @inlineCallbacks
520+ def test__get_discovered_pod_storage_pool(self):
521+ driver = lxd_pod_module.LXDPodDriver()
522+ mock_storage_pool = Mock()
523+ mock_storage_pool.name = factory.make_name("pool")
524+ mock_storage_pool.driver = "dir"
525+ mock_storage_pool.config = {
526+ "size": "61203283968",
527+ "source": "/home/chb/mnt/l2/disks/default.img",
528+ "volume.size": "0",
529+ "zfs.pool_name": "default",
530+ }
531+ mock_resources = Mock()
532+ mock_resources.space = {"used": 207111192576, "total": 306027577344}
533+ mock_storage_pool.resources.get.return_value = mock_resources
534+ discovered_pod_storage_pool = yield driver.get_discovered_pod_storage_pool(
535+ mock_storage_pool
536+ )
537+
538+ self.assertEquals(
539+ mock_storage_pool.name, discovered_pod_storage_pool.id
540+ )
541+ self.assertEquals(
542+ mock_storage_pool.name, discovered_pod_storage_pool.name
543+ )
544+ self.assertEquals(
545+ mock_storage_pool.config["source"],
546+ discovered_pod_storage_pool.path,
547+ )
548+ self.assertEquals(
549+ mock_storage_pool.driver, discovered_pod_storage_pool.type
550+ )
551+ self.assertEquals(
552+ mock_resources.space["total"], discovered_pod_storage_pool.storage
553+ )
554+
555+ @inlineCallbacks
556+ def test__get_discovered_machine(self):
557+ driver = lxd_pod_module.LXDPodDriver()
558+ mock_machine = Mock()
559+ mock_machine.name = factory.make_name("machine")
560+ mock_machine.architecture = "x86_64"
561+ expanded_config = {
562+ "limits.cpu": "2",
563+ "limits.memory": "1024",
564+ "volatile.eth0.hwaddr": "00:16:3e:78:be:04",
565+ "volatile.eth1.hwaddr": "00:16:3e:f9:fc:cb",
566+ }
567+ expanded_devices = {
568+ "eth0": {
569+ "name": "eth0",
570+ "nictype": "bridged",
571+ "parent": "lxdbr0",
572+ "type": "nic",
573+ },
574+ "eth1": {
575+ "name": "eth1",
576+ "nictype": "bridged",
577+ "parent": "virbr1",
578+ "type": "nic",
579+ },
580+ "root": {"path": "/", "pool": "default", "type": "disk"},
581+ }
582+ mock_machine.expanded_config = expanded_config
583+ mock_machine.expanded_devices = expanded_devices
584+ mock_machine.status_code = 102
585+ mock_storage_pool = Mock()
586+ mock_storage_pool.name = "default"
587+ mock_storage_pool_resources = Mock()
588+ mock_storage_pool_resources.space = {
589+ "used": 207111192576,
590+ "total": 306027577344,
591+ }
592+ mock_storage_pool.resources.get.return_value = (
593+ mock_storage_pool_resources
594+ )
595+ mock_machine.storage_pools.get.return_value = mock_storage_pool
596+ discovered_machine = yield driver.get_discovered_machine(
597+ mock_machine, [mock_storage_pool], cpu_speed=2500
598+ )
599+
600+ self.assertEquals(mock_machine.name, discovered_machine.hostname)
601+
602+ self.assertEquals(
603+ kernel_to_debian_architecture(mock_machine.architecture),
604+ discovered_machine.architecture,
605+ )
606+ self.assertEquals(
607+ lxd_pod_module.LXD_VM_POWER_STATE[mock_machine.status_code],
608+ discovered_machine.power_state,
609+ )
610+ self.assertEquals(2, discovered_machine.cores)
611+ self.assertEquals(2500, discovered_machine.cpu_speed)
612+ self.assertEquals(1024, discovered_machine.memory)
613+ self.assertEquals(
614+ mock_machine.name,
615+ discovered_machine.power_parameters["instance_name"],
616+ )
617+ self.assertThat(
618+ discovered_machine.block_devices[0],
619+ MatchesStructure.byEquality(
620+ model=None,
621+ serial=None,
622+ id_path=lxd_pod_module.LXD_VM_ID_PATH + "root",
623+ size=10 * 1000 ** 3,
624+ block_size=512,
625+ tags=[],
626+ type="physical",
627+ storage_pool=expanded_devices["root"]["pool"],
628+ iscsi_target=None,
629+ ),
630+ )
631+ self.assertThat(
632+ discovered_machine.interfaces[0],
633+ MatchesStructure.byEquality(
634+ mac_address=expanded_config["volatile.eth0.hwaddr"],
635+ vid=0,
636+ tags=[],
637+ boot=True,
638+ attach_type=expanded_devices["eth0"]["nictype"],
639+ attach_name="eth0",
640+ ),
641+ )
642+ self.assertThat(
643+ discovered_machine.interfaces[1],
644+ MatchesStructure.byEquality(
645+ mac_address=expanded_config["volatile.eth1.hwaddr"],
646+ vid=0,
647+ tags=[],
648+ boot=False,
649+ attach_type=expanded_devices["eth1"]["nictype"],
650+ attach_name="eth1",
651+ ),
652+ )
653+ self.assertItemsEqual([], discovered_machine.tags)
654+
655+ @inlineCallbacks
656+ def test_get_discovered_machine_sets_power_state_to_unknown_for_unknown(
657+ self
658+ ):
659+ driver = lxd_pod_module.LXDPodDriver()
660+ mock_machine = Mock()
661+ mock_machine.name = factory.make_name("machine")
662+ mock_machine.architecture = "x86_64"
663+ expanded_config = {
664+ "limits.cpu": "2",
665+ "limits.memory": "1024",
666+ "volatile.eth0.hwaddr": "00:16:3e:78:be:04",
667+ "volatile.eth1.hwaddr": "00:16:3e:f9:fc:cb",
668+ }
669+ expanded_devices = {
670+ "eth0": {
671+ "name": "eth0",
672+ "nictype": "bridged",
673+ "parent": "lxdbr0",
674+ "type": "nic",
675+ },
676+ "eth1": {
677+ "name": "eth1",
678+ "nictype": "bridged",
679+ "parent": "virbr1",
680+ "type": "nic",
681+ },
682+ "root": {"path": "/", "pool": "default", "type": "disk"},
683+ }
684+ mock_machine.expanded_config = expanded_config
685+ mock_machine.expanded_devices = expanded_devices
686+ mock_machine.status_code = 100
687+ mock_storage_pool = Mock()
688+ mock_storage_pool.name = "default"
689+ mock_storage_pool_resources = Mock()
690+ mock_storage_pool_resources.space = {
691+ "used": 207111192576,
692+ "total": 306027577344,
693+ }
694+ mock_storage_pool.resources.get.return_value = (
695+ mock_storage_pool_resources
696+ )
697+ mock_machine.storage_pools.get.return_value = mock_storage_pool
698+ discovered_machine = yield driver.get_discovered_machine(
699+ mock_machine, [mock_storage_pool], cpu_speed=2500
700+ )
701+
702+ self.assertEquals("unknown", discovered_machine.power_state)

Subscribers

People subscribed via source and target branches