Merge ~ack/maas:1915715-lxd-multidisk-size-fix into maas:master

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: d4bd357580d93256e8c03c095c9c4d1e7983eb12
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:1915715-lxd-multidisk-size-fix
Merge into: maas:master
Diff against target: 260 lines (+146/-68)
2 files modified
src/provisioningserver/drivers/pod/lxd.py (+70/-67)
src/provisioningserver/drivers/pod/tests/test_lxd.py (+76/-1)
Reviewer Review Type Date Requested Status
Björn Tillenius Approve
MAAS Lander Approve
Review via email: mp+398124@code.launchpad.net

Commit message

LP #1915715 - LXD: consider tags/size for disks in get_discovered_machine

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

UNIT TESTS
-b 1915715-lxd-multidisk-size-fix lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 0009786e76e0fbbf72bf550f1099a05d00d56abd

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

UNIT TESTS
-b 1915715-lxd-multidisk-size-fix lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: d4bd357580d93256e8c03c095c9c4d1e7983eb12

review: Approve
Revision history for this message
Björn Tillenius (bjornt) wrote :

+1

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

LANDING
-b 1915715-lxd-multidisk-size-fix lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/9254/consoleText

There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.

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 16929e3..665873a 100644
3--- a/src/provisioningserver/drivers/pod/lxd.py
4+++ b/src/provisioningserver/drivers/pod/lxd.py
5@@ -78,6 +78,7 @@ LXD_BYTE_SUFFIXES = {
6 def convert_lxd_byte_suffixes(value, divisor=None):
7 """Takes the value and converts to a proper integer
8 using LXD_BYTE_SUFFIXES."""
9+ value = str(value)
10 result = re.match(
11 r"(?P<size>[0-9]+)(?P<unit>%s)" % "|".join(LXD_BYTE_SUFFIXES.keys()),
12 value,
13@@ -615,64 +616,44 @@ class LXDPodDriver(PodDriver):
14 )
15 power_state = "unknown"
16
17- expanded_config = machine.expanded_config
18- expanded_devices = machine.expanded_devices
19+ def _get_discovered_block_device(name, device, requested_device=None):
20+ tags = requested_device.tags if requested_device else []
21+ # When LXD creates a QEMU disk the serial is always
22+ # lxd_{device name}. The device_name is defined by
23+ # the LXD profile or when adding a device. This is
24+ # commonly "root" for the first disk. The model and
25+ # serial must be correctly defined here otherwise
26+ # MAAS will delete the disk created during composition
27+ # which results in losing the storage pool link. Without
28+ # the storage pool link MAAS can't determine how much
29+ # of the storage pool has been used.
30+ serial = f"lxd_{name}"
31+ source = device.get("source")
32+ if source:
33+ pool = client.storage_pools.get(device["pool"])
34+ volume = pool.volumes.get("custom", source)
35+ size = volume.config.get("size")
36+ else:
37+ size = device.get("size")
38+ # Default disk size is 10GB in LXD
39+ size = convert_lxd_byte_suffixes(size or "10GB")
40+ return DiscoveredMachineBlockDevice(
41+ model="QEMU HARDDISK",
42+ serial=serial,
43+ id_path=f"/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_{serial}",
44+ size=size,
45+ tags=tags,
46+ storage_pool=device.get("pool"),
47+ )
48
49- # Discover block devices.
50- block_devices = []
51- for device in expanded_devices:
52- # Block device.
53- # When request is provided map the tags from the request block
54- # devices to the discovered block devices. This ensures that
55- # composed machine has the requested tags on the block device.
56-
57- tags = []
58- if (
59- request is not None
60- and expanded_devices[device]["type"] == "disk"
61- ):
62- tags = request.block_devices[0].tags
63-
64- device_info = expanded_devices[device]
65- if device_info["type"] == "disk":
66- # When LXD creates a QEMU disk the serial is always
67- # lxd_{device name}. The device_name is defined by
68- # the LXD profile or when adding a device. This is
69- # commonly "root" for the first disk. The model and
70- # serial must be correctly defined here otherwise
71- # MAAS will delete the disk created during composition
72- # which results in losing the storage pool link. Without
73- # the storage pool link MAAS can't determine how much
74- # of the storage pool has been used.
75- serial = f"lxd_{device}"
76- # Default disk size is 10GB.
77- size = convert_lxd_byte_suffixes(
78- device_info.get("size", "10GB")
79- )
80- storage_pool = device_info.get("pool")
81- block_devices.append(
82- DiscoveredMachineBlockDevice(
83- model="QEMU HARDDISK",
84- serial=serial,
85- id_path=f"/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_{serial}",
86- size=size,
87- tags=tags,
88- storage_pool=storage_pool,
89- )
90- )
91+ expanded_config = machine.expanded_config
92+ iface_to_mac = {
93+ key.split(".")[1]: value
94+ for key, value in expanded_config.items()
95+ if key.endswith("hwaddr")
96+ }
97
98- # Discover interfaces.
99- interfaces = []
100- boot = True
101- config_mac_address = {}
102- for configuration in expanded_config:
103- if configuration.endswith("hwaddr"):
104- mac = expanded_config[configuration]
105- name = configuration.split(".")[1]
106- config_mac_address[name] = mac
107- for name, device in expanded_devices.items():
108- if device["type"] != "nic":
109- continue
110+ def _get_discovered_interface(name, device, boot):
111 if "network" in device:
112 # Try finding the nictype from the networks.
113 # XXX: This should work for "bridge" networks,
114@@ -691,18 +672,40 @@ class LXDPodDriver(PodDriver):
115 )
116 mac = device.get("hwaddr")
117 if mac is None:
118- mac = config_mac_address.get(name)
119-
120- interfaces.append(
121- DiscoveredMachineInterface(
122- mac_address=mac,
123- vid=int(device.get("vlan", get_vid_from_ifname(name))),
124- boot=boot,
125- attach_type=attach_type,
126- attach_name=attach_name,
127- )
128+ mac = iface_to_mac.get(name)
129+ return DiscoveredMachineInterface(
130+ mac_address=mac,
131+ vid=int(device.get("vlan", get_vid_from_ifname(name))),
132+ boot=boot,
133+ attach_type=attach_type,
134+ attach_name=attach_name,
135 )
136- boot = False
137+
138+ extra_block_devices = 0
139+ block_devices = []
140+ interfaces = []
141+ for name, device in machine.expanded_devices.items():
142+ if device["type"] == "disk":
143+ requested_device = None
144+ if request:
145+ # for composed VMs, the root disk is always the first
146+ # one. Adjust the index so that it matches the requested
147+ # device
148+ if name == "root":
149+ index = 0
150+ else:
151+ extra_block_devices += 1
152+ index = extra_block_devices
153+ requested_device = request.block_devices[index]
154+ block_devices.append(
155+ _get_discovered_block_device(
156+ name, device, requested_device=requested_device
157+ )
158+ )
159+ elif device["type"] == "nic":
160+ interfaces.append(
161+ _get_discovered_interface(name, device, not interfaces)
162+ )
163
164 # LXD uses different suffixes to store memory so make
165 # sure we convert to MiB, which is what MAAS uses.
166diff --git a/src/provisioningserver/drivers/pod/tests/test_lxd.py b/src/provisioningserver/drivers/pod/tests/test_lxd.py
167index 3045029..d6e69ec 100644
168--- a/src/provisioningserver/drivers/pod/tests/test_lxd.py
169+++ b/src/provisioningserver/drivers/pod/tests/test_lxd.py
170@@ -44,7 +44,8 @@ from provisioningserver.utils.network import generate_mac_address
171 def make_requested_machine(num_disks=1, **kwargs):
172 block_devices = [
173 RequestedMachineBlockDevice(
174- size=random.randint(1024 ** 3, 4 * 1024 ** 3)
175+ size=random.randint(1024 ** 3, 4 * 1024 ** 3),
176+ tags=[factory.make_name("tag")],
177 )
178 for _ in range(num_disks)
179 ]
180@@ -749,6 +750,80 @@ class TestLXDPodDriver(MAASTestCase):
181 self.assertTrue(discovered_machine.hugepages_backed)
182 self.assertEqual(discovered_machine.pinned_cores, [0, 1, 2])
183
184+ def test_get_discovered_machine_with_request(self):
185+ request = make_requested_machine(num_disks=2)
186+ driver = lxd_module.LXDPodDriver()
187+ Client = self.patch(driver, "_get_client")
188+ client = Client.return_value
189+ mock_profile = Mock()
190+ mock_profile.name = random.choice(["maas", "default"])
191+ profile_devices = {
192+ "eth0": {
193+ "name": "eth0",
194+ "nictype": "bridged",
195+ "parent": "lxdbr0",
196+ "type": "nic",
197+ },
198+ }
199+ mock_profile.devices = profile_devices
200+ client.profiles.get.return_value = mock_profile
201+ mock_storage_pools = Mock()
202+ client.storage_pools.all.return_value = mock_storage_pools
203+ mock_get_usable_storage_pool = self.patch(
204+ driver, "_get_usable_storage_pool"
205+ )
206+ # a volume is created for the second disk
207+ volume = Mock()
208+ volume.name = factory.make_name("vol")
209+ volume.config = {"size": request.block_devices[1].size}
210+ usable_pool = Mock()
211+ usable_pool.name = factory.make_name("pool")
212+ usable_pool.volumes.create.return_value = volume
213+ usable_pool.volumes.get.return_value = volume
214+ mock_get_usable_storage_pool.return_value = usable_pool
215+ client.storage_pools.get.return_value = usable_pool
216+ mock_machine = Mock(architecture="x86_64")
217+ expanded_config = {
218+ "limits.cpu": "2",
219+ "limits.memory": "1024",
220+ "volatile.eth0.hwaddr": "00:16:3e:78:be:04",
221+ }
222+ expanded_devices = {
223+ "root": {
224+ "path": "/",
225+ "type": "disk",
226+ "pool": usable_pool.name,
227+ "size": str(request.block_devices[0].size),
228+ "boot.priority": "0",
229+ },
230+ "disk1": {
231+ "path": "",
232+ "type": "disk",
233+ "pool": usable_pool.name,
234+ "source": volume.name,
235+ },
236+ "eth0": {
237+ "boot.priority": "1",
238+ "name": "eth0",
239+ "nictype": "bridged",
240+ "parent": "lxdbr0",
241+ "type": "nic",
242+ },
243+ }
244+ mock_machine.expanded_config = expanded_config
245+ mock_machine.expanded_devices = expanded_devices
246+ client.virtual_machines.create.return_value = mock_machine
247+ discovered_machine = driver._get_discovered_machine(
248+ client, mock_machine, [usable_pool], request
249+ )
250+ # invert sort as the root device shows up last because of name ordering
251+ discovered_devices = sorted(
252+ discovered_machine.block_devices, reverse=True
253+ )
254+ for idx, device in enumerate(discovered_devices):
255+ self.assertEqual(device.size, request.block_devices[idx].size)
256+ self.assertEqual(device.tags, request.block_devices[idx].tags)
257+
258 def test_get_hugepages_info_int_value_as_bool(self):
259 driver = lxd_module.LXDPodDriver()
260 Client = self.patch(lxd_module, "Client")

Subscribers

People subscribed via source and target branches