Merge ~ack/maas:1940977-3.0 into maas:3.0

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: 78b494cd0e32eb61e10fafe85bd5c68ee33949b8
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:1940977-3.0
Merge into: maas:3.0
Diff against target: 255 lines (+58/-38)
2 files modified
src/provisioningserver/drivers/pod/lxd.py (+18/-9)
src/provisioningserver/drivers/pod/tests/test_lxd.py (+40/-29)
Reviewer Review Type Date Requested Status
Adam Collard (community) Approve
MAAS Lander unittests Pending
Review via email: mp+407670@code.launchpad.net

Commit message

LP:1940977 check for all required LXD extensions

This also adds check for the custom_block_volumes one, which is needed to
compose VMs with multiple disks.

To post a comment you must log in.
Revision history for this message
Adam Collard (adam-collard) :
review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

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 4095e85..f754d42 100644
3--- a/src/provisioningserver/drivers/pod/lxd.py
4+++ b/src/provisioningserver/drivers/pod/lxd.py
5@@ -76,6 +76,12 @@ LXD_BYTE_SUFFIXES = {
6 "EiB": 1024 ** 6,
7 }
8
9+LXD_REQUIRED_EXTENSIONS = frozenset(
10+ ("projects", "virtual-machines", "custom_block_volumes")
11+)
12+
13+LXD_MIN_VERSION = "4.16"
14+
15
16 def convert_lxd_byte_suffixes(value, divisor=None):
17 """Takes the value and converts to a proper integer
18@@ -237,10 +243,7 @@ class LXDPodDriver(PodDriver):
19 def discover_projects(self, pod_id: int, context: dict):
20 """Discover the list of projects in a pod."""
21 client = self._get_client(pod_id, context)
22- if not client.has_api_extension("projects"):
23- raise LXDPodError(
24- "Please upgrade your LXD host to 3.6+ for projects support."
25- )
26+ self._check_required_extensions(client)
27 return [
28 {"name": project.name, "description": project.description}
29 for project in client.projects.all()
30@@ -251,11 +254,7 @@ class LXDPodDriver(PodDriver):
31 """Discover all Pod host resources."""
32 # Connect to the Pod and make sure it is valid.
33 client = self._get_client(pod_id, context)
34- if not client.has_api_extension("virtual-machines"):
35- raise LXDPodError(
36- "Please upgrade your LXD host to 3.19+ for virtual machine support."
37- )
38-
39+ self._check_required_extensions(client)
40 self._ensure_project(client)
41
42 # get MACs for host interfaces. "unknown" interfaces are considered too
43@@ -402,6 +401,16 @@ class LXDPodDriver(PodDriver):
44 # Hints are updated on the region for LXDPodDriver.
45 return DiscoveredPodHints()
46
47+ def _check_required_extensions(self, client):
48+ """Raise an error if the LXD server doesn't support all required features."""
49+ all_extensions = set(client.host_info["api_extensions"])
50+ missing_extensions = sorted(LXD_REQUIRED_EXTENSIONS - all_extensions)
51+ if missing_extensions:
52+ raise LXDPodError(
53+ f"Please upgrade your LXD host to {LXD_MIN_VERSION} or higher "
54+ f"to support the following extensions: {','.join(missing_extensions)}"
55+ )
56+
57 def _get_machine_disks(
58 self, requested_disks, storage_pools, default_storage_pool
59 ):
60diff --git a/src/provisioningserver/drivers/pod/tests/test_lxd.py b/src/provisioningserver/drivers/pod/tests/test_lxd.py
61index 0b41f13..afb260a 100644
62--- a/src/provisioningserver/drivers/pod/tests/test_lxd.py
63+++ b/src/provisioningserver/drivers/pod/tests/test_lxd.py
64@@ -149,7 +149,9 @@ class TestLXDPodDriver(MAASTestCase):
65 context = self.make_parameters_context()
66 Client = self.patch(lxd_module, "Client")
67 client = Client.return_value
68- client.has_api_extension.return_value = True
69+ client.host_info = {
70+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
71+ }
72 client.trusted = False
73 driver = lxd_module.LXDPodDriver()
74 endpoint = driver.get_url(context)
75@@ -173,7 +175,9 @@ class TestLXDPodDriver(MAASTestCase):
76 context.pop("project")
77 Client = self.patch(lxd_module, "Client")
78 client = Client.return_value
79- client.has_api_extension.return_value = True
80+ client.host_info = {
81+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
82+ }
83 client.trusted = False
84 driver = lxd_module.LXDPodDriver()
85 endpoint = driver.get_url(context)
86@@ -196,7 +200,9 @@ class TestLXDPodDriver(MAASTestCase):
87 context = self.make_parameters_context()
88 Client = self.patch(lxd_module, "Client")
89 client = Client.return_value
90- client.has_api_extension.return_value = True
91+ client.host_info = {
92+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
93+ }
94 client.trusted = False
95 driver = lxd_module.LXDPodDriver()
96 endpoint = driver.get_url(context)
97@@ -314,18 +320,22 @@ class TestLXDPodDriver(MAASTestCase):
98 yield driver.power_query(pod_id, context)
99
100 @inlineCallbacks
101- def test_discover_requires_client_to_have_vm_support(self):
102+ def test_discover_checks_required_extensions(self):
103 context = self.make_parameters_context()
104 driver = lxd_module.LXDPodDriver()
105 Client = self.patch(lxd_module, "Client")
106 client = Client.return_value
107- client.has_api_extension.return_value = False
108- error_msg = "Please upgrade your LXD host to *."
109+ client.host_info = {
110+ "api_extensions": sorted(
111+ lxd_module.LXD_REQUIRED_EXTENSIONS - {"projects"}
112+ ),
113+ }
114+ error_msg = (
115+ "Please upgrade your LXD host to 4.16 or higher "
116+ "to support the following extensions: projects"
117+ )
118 with ExpectedException(lxd_module.LXDPodError, error_msg):
119 yield driver.discover(None, context)
120- self.assertThat(
121- client.has_api_extension, MockCalledOnceWith("virtual-machines")
122- )
123
124 @inlineCallbacks
125 def test_discover(self):
126@@ -333,15 +343,15 @@ class TestLXDPodDriver(MAASTestCase):
127 driver = lxd_module.LXDPodDriver()
128 Client = self.patch(lxd_module, "Client")
129 client = Client.return_value
130- client.has_api_extension.return_value = True
131 name = factory.make_name("hostname")
132 client.host_info = {
133+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
134 "environment": {
135 "architectures": ["x86_64", "i686"],
136 "kernel_architecture": "x86_64",
137 "server_name": name,
138 "server_version": "1.2.3",
139- }
140+ },
141 }
142 mac_address = factory.make_mac_address()
143 lxd_net1 = Mock(type="physical")
144@@ -381,15 +391,15 @@ class TestLXDPodDriver(MAASTestCase):
145 driver = lxd_module.LXDPodDriver()
146 Client = self.patch(lxd_module, "Client")
147 client = Client.return_value
148- client.has_api_extension.return_value = True
149 name = factory.make_name("hostname")
150 client.host_info = {
151+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
152 "environment": {
153 "architectures": ["x86_64", "i686"],
154 "kernel_architecture": "x86_64",
155 "server_name": name,
156 "server_version": "1.2.3",
157- }
158+ },
159 }
160 mac_address = factory.make_mac_address()
161 lxd_network = Mock(type="unknown")
162@@ -405,14 +415,14 @@ class TestLXDPodDriver(MAASTestCase):
163 Client = self.patch(lxd_module, "Client")
164 client = Client.return_value
165 client.project = project_name
166- client.has_api_extension.return_value = True
167 client.host_info = {
168+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
169 "environment": {
170 "architectures": ["x86_64", "i686"],
171 "kernel_architecture": "x86_64",
172 "server_name": factory.make_name("hostname"),
173 "server_version": "1.2.3",
174- }
175+ },
176 }
177 client.projects.exists.return_value = True
178 driver = lxd_module.LXDPodDriver()
179@@ -426,16 +436,16 @@ class TestLXDPodDriver(MAASTestCase):
180 project_name = context["project"]
181 Client = self.patch(lxd_module, "Client")
182 client = Client.return_value
183- client.project = project_name
184- client.has_api_extension.return_value = True
185 client.host_info = {
186+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
187 "environment": {
188 "architectures": ["x86_64", "i686"],
189 "kernel_architecture": "x86_64",
190 "server_name": factory.make_name("hostname"),
191 "server_version": "1.2.3",
192- }
193+ },
194 }
195+ client.project = project_name
196 client.projects.exists.return_value = False
197 driver = lxd_module.LXDPodDriver()
198 yield driver.discover(None, context)
199@@ -451,18 +461,22 @@ class TestLXDPodDriver(MAASTestCase):
200 )
201
202 @inlineCallbacks
203- def test_discover_projects_requires_projects_support(self):
204+ def test_discover_projects_checks_required_extensions(self):
205 context = self.make_parameters_context()
206 driver = lxd_module.LXDPodDriver()
207 Client = self.patch(lxd_module, "Client")
208 client = Client.return_value
209- client.has_api_extension.return_value = False
210- error_msg = "Please upgrade your LXD host to *."
211+ client.host_info = {
212+ "api_extensions": sorted(
213+ lxd_module.LXD_REQUIRED_EXTENSIONS - {"virtual-machines"}
214+ ),
215+ }
216+ error_msg = (
217+ "Please upgrade your LXD host to 4.16 or higher "
218+ "to support the following extensions: virtual-machines"
219+ )
220 with ExpectedException(lxd_module.LXDPodError, error_msg):
221 yield driver.discover_projects(None, context)
222- self.assertThat(
223- client.has_api_extension, MockCalledOnceWith("projects")
224- )
225
226 @inlineCallbacks
227 def test_discover_projects(self):
228@@ -470,15 +484,15 @@ class TestLXDPodDriver(MAASTestCase):
229 driver = lxd_module.LXDPodDriver()
230 Client = self.patch(lxd_module, "Client")
231 client = Client.return_value
232- client.has_api_extension.return_value = True
233 name = factory.make_name("hostname")
234 client.host_info = {
235+ "api_extensions": sorted(lxd_module.LXD_REQUIRED_EXTENSIONS),
236 "environment": {
237 "architectures": ["x86_64", "i686"],
238 "kernel_architecture": "x86_64",
239 "server_name": name,
240 "server_version": "1.2.3",
241- }
242+ },
243 }
244 proj1 = Mock()
245 proj1.name = "proj1"
246@@ -923,9 +937,6 @@ class TestLXDPodDriver(MAASTestCase):
247 client.resources = {
248 factory.make_name("rkey"): factory.make_name("rvalue")
249 }
250- client.host_info = {
251- factory.make_name("hkey"): factory.make_name("hvalue")
252- }
253
254 def mock_iface(name, mac):
255 iface = Mock()

Subscribers

People subscribed via source and target branches