Merge lp:~danilo/maas/virsh-storage-units-2.2 into lp:maas/2.2

Proposed by Данило Шеган
Status: Merged
Approved by: Данило Шеган
Approved revision: no longer in the source branch.
Merged at revision: 6068
Proposed branch: lp:~danilo/maas/virsh-storage-units-2.2
Merge into: lp:maas/2.2
Diff against target: 235 lines (+123/-12)
4 files modified
src/provisioningserver/drivers/pod/tests/test_virsh.py (+23/-0)
src/provisioningserver/drivers/pod/virsh.py (+17/-12)
src/provisioningserver/utils/__init__.py (+45/-0)
src/provisioningserver/utils/tests/test_utils.py (+38/-0)
To merge this branch: bzr merge lp:~danilo/maas/virsh-storage-units-2.2
Reviewer Review Type Date Requested Status
Данило Шеган (community) Approve
Review via email: mp+326065@code.launchpad.net

Commit message

Backport r6095 from trunk.

Consider storage size units when figuring out virsh pod storage pool sizes.

To post a comment you must log in.
Revision history for this message
Данило Шеган (danilo) wrote :

Self-approving a backport.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/provisioningserver/drivers/pod/tests/test_virsh.py'
--- src/provisioningserver/drivers/pod/tests/test_virsh.py 2017-05-19 03:37:56 +0000
+++ src/provisioningserver/drivers/pod/tests/test_virsh.py 2017-06-21 11:02:31 +0000
@@ -105,6 +105,17 @@
105 Available: 173.84 GiB105 Available: 173.84 GiB
106 """)106 """)
107107
108SAMPLE_POOLINFO_TB = dedent("""
109 Name: default
110 UUID: 59edc0cb-4635-449a-80e2-2c8a59afa327
111 State: running
112 Persistent: yes
113 Autostart: yes
114 Capacity: 2.21 TiB
115 Allocation: 880.64 GiB
116 Available: 1.35 TiB
117 """)
118
108SAMPLE_IFLIST = dedent("""119SAMPLE_IFLIST = dedent("""
109 Interface Type Source Model MAC120 Interface Type Source Model MAC
110 -------------------------------------------------------121 -------------------------------------------------------
@@ -506,6 +517,18 @@
506 for pool in pools_mock.return_value517 for pool in pools_mock.return_value
507 }, expected)518 }, expected)
508519
520 def test_get_pod_pool_size_map_terabytes(self):
521 conn = self.configure_virshssh(SAMPLE_POOLINFO_TB)
522 pools_mock = self.patch(virsh.VirshSSH, 'list_pools')
523 pools_mock.return_value = [
524 factory.make_name('pool') for _ in range(3)]
525 expected = conn.get_pod_pool_size_map('Capacity')
526 capacity = int(2.21 * 2**40)
527 self.assertEqual({
528 pool: capacity
529 for pool in pools_mock.return_value
530 }, expected)
531
509 def test_get_pod_local_storage(self):532 def test_get_pod_local_storage(self):
510 conn = self.configure_virshssh(SAMPLE_POOLINFO)533 conn = self.configure_virshssh(SAMPLE_POOLINFO)
511 pools_mock = self.patch(virsh.VirshSSH, 'list_pools')534 pools_mock = self.patch(virsh.VirshSSH, 'list_pools')
512535
=== modified file 'src/provisioningserver/drivers/pod/virsh.py'
--- src/provisioningserver/drivers/pod/virsh.py 2017-05-19 03:57:03 +0000
+++ src/provisioningserver/drivers/pod/virsh.py 2017-06-21 11:02:31 +0000
@@ -37,6 +37,7 @@
37 create_node,37 create_node,
38)38)
39from provisioningserver.utils import (39from provisioningserver.utils import (
40 convert_size_to_bytes,
40 shell,41 shell,
41 typed,42 typed,
42)43)
@@ -273,7 +274,13 @@
273 data = data.strip().splitlines()274 data = data.strip().splitlines()
274 for d in data:275 for d in data:
275 if key == d.split(':')[0].strip():276 if key == d.split(':')[0].strip():
276 return d.split(':')[1].split()[0]277 return d.split(':')[1].strip()
278
279 def get_key_value_unitless(self, data, key):
280 """Return value based off of key with unit (if any) stripped off."""
281 value = self.get_key_value(data, key)
282 if value:
283 return value.split()[0]
277284
278 def list_machines(self):285 def list_machines(self):
279 """Lists all VMs by name."""286 """Lists all VMs by name."""
@@ -336,7 +343,7 @@
336 if output is None:343 if output is None:
337 maaslog.error("Failed to get pod CPU speed")344 maaslog.error("Failed to get pod CPU speed")
338 return None345 return None
339 return int(self.get_key_value(output, "CPU frequency"))346 return int(self.get_key_value_unitless(output, "CPU frequency"))
340347
341 def get_pod_memory(self):348 def get_pod_memory(self):
342 """Gets the total memory of the pod."""349 """Gets the total memory of the pod."""
@@ -344,7 +351,7 @@
344 if output is None:351 if output is None:
345 maaslog.error("Failed to get pod memory")352 maaslog.error("Failed to get pod memory")
346 return None353 return None
347 KiB = int(self.get_key_value(output, "Memory size"))354 KiB = int(self.get_key_value_unitless(output, "Memory size"))
348 # Memory in MiB.355 # Memory in MiB.
349 return int(KiB / 1024)356 return int(KiB / 1024)
350357
@@ -354,7 +361,7 @@
354 if output is None:361 if output is None:
355 maaslog.error("%s: Failed to get machine memory", machine)362 maaslog.error("%s: Failed to get machine memory", machine)
356 return None363 return None
357 KiB = int(self.get_key_value(output, "Max memory"))364 KiB = int(self.get_key_value_unitless(output, "Max memory"))
358 # Memory in MiB.365 # Memory in MiB.
359 return int(KiB / 1024)366 return int(KiB / 1024)
360367
@@ -366,10 +373,8 @@
366 if output is None:373 if output is None:
367 # Skip if cannot get more information.374 # Skip if cannot get more information.
368 continue375 continue
369 capacity = float(376 pools[pool] = convert_size_to_bytes(
370 self.get_key_value(output, key))377 self.get_key_value(output, "Capacity"))
371 # Convert GiB to bytes.
372 pools[pool] = int(capacity * 2**30)
373 return pools378 return pools
374379
375 def get_pod_local_storage(self):380 def get_pod_local_storage(self):
@@ -390,10 +395,10 @@
390 maaslog.error(395 maaslog.error(
391 "Failed to get available pod local storage")396 "Failed to get available pod local storage")
392 return None397 return None
393 local_storage += float(self.get_key_value(398 local_storage += convert_size_to_bytes(
394 output, "Available"))399 self.get_key_value(output, "Available"))
395 # Local storage in bytes. GiB to bytes.400 # Local storage in bytes.
396 return int(local_storage * 2**30)401 return local_storage
397402
398 def get_machine_local_storage(self, machine, device):403 def get_machine_local_storage(self, machine, device):
399 """Gets the VM local storage for device."""404 """Gets the VM local storage for device."""
400405
=== modified file 'src/provisioningserver/utils/__init__.py'
--- src/provisioningserver/utils/__init__.py 2017-04-05 14:57:42 +0000
+++ src/provisioningserver/utils/__init__.py 2017-06-21 11:02:31 +0000
@@ -246,3 +246,48 @@
246 return issubclass(test, query_tuple)246 return issubclass(test, query_tuple)
247 except TypeError:247 except TypeError:
248 return False248 return False
249
250
251# Capacity units supported by convert_size_to_bytes() function.
252CAPACITY_UNITS = {
253 "KiB": 2 ** 10,
254 "MiB": 2 ** 20,
255 "GiB": 2 ** 30,
256 "TiB": 2 ** 40,
257 "PiB": 2 ** 50,
258 "EiB": 2 ** 60,
259 "ZiB": 2 ** 70,
260 "YiB": 2 ** 80,
261}
262
263
264class UnknownCapacityUnitError(Exception):
265 """Unknown capacity unit used."""
266
267
268def convert_size_to_bytes(value):
269 """
270 Converts storage size values with units (GiB, TiB...) to bytes.
271
272 :param value: A string containing a number and unit separated by at least
273 one space character. If unit is not specified, defaults to bytes.
274 :return: An integer indicating the number of bytes for the given value in
275 any other size unit.
276 :raises UnknownCapacityUnitError: unsupported capacity unit.
277 """
278 # Split value on the first space.
279 capacity_def = value.split(" ", 1)
280 if len(capacity_def) == 1:
281 # No unit specified, default to bytes.
282 return int(capacity_def[0])
283
284 capacity_value, capacity_unit = capacity_def
285 capacity_value = float(capacity_value)
286 capacity_unit = capacity_unit.strip()
287 if capacity_unit in CAPACITY_UNITS:
288 multiplier = CAPACITY_UNITS[capacity_unit]
289 else:
290 raise UnknownCapacityUnitError(
291 "Unknown capacity unit '%s'" % capacity_unit)
292 # Convert value to bytes.
293 return int(capacity_value * multiplier)
249294
=== modified file 'src/provisioningserver/utils/tests/test_utils.py'
--- src/provisioningserver/utils/tests/test_utils.py 2017-03-31 20:27:52 +0000
+++ src/provisioningserver/utils/tests/test_utils.py 2017-06-21 11:02:31 +0000
@@ -21,6 +21,7 @@
21from provisioningserver.utils import (21from provisioningserver.utils import (
22 CircularDependency,22 CircularDependency,
23 classify,23 classify,
24 convert_size_to_bytes,
24 flatten,25 flatten,
25 is_instance_or_subclass,26 is_instance_or_subclass,
26 locate_config,27 locate_config,
@@ -30,6 +31,7 @@
30 ShellTemplate,31 ShellTemplate,
31 sorttop,32 sorttop,
32 sudo,33 sudo,
34 UnknownCapacityUnitError,
33)35)
34from testtools.matchers import (36from testtools.matchers import (
35 DirExists,37 DirExists,
@@ -369,3 +371,39 @@
369 self.assertThat(371 self.assertThat(
370 is_instance_or_subclass(372 is_instance_or_subclass(
371 self.bar, *[Baz, [Bar, [Foo]]]), Equals(True))373 self.bar, *[Baz, [Bar, [Foo]]]), Equals(True))
374
375
376class TestConvertSizeToBytes(MAASTestCase):
377 """Tests for `convert_size_to_bytes`."""
378
379 scenarios = (
380 ("bytes", {"value": "24111", "expected": 24111}),
381 ("KiB", {"value": "2.21 KiB", "expected": int(2.21 * 2**10)}),
382 ("MiB", {"value": "2.21 MiB", "expected": int(2.21 * 2**20)}),
383 ("GiB", {"value": "2.21 GiB", "expected": int(2.21 * 2**30)}),
384 ("TiB", {"value": "2.21 TiB", "expected": int(2.21 * 2**40)}),
385 ("PiB", {"value": "2.21 PiB", "expected": int(2.21 * 2**50)}),
386 ("EiB", {"value": "2.21 EiB", "expected": int(2.21 * 2**60)}),
387 ("ZiB", {"value": "2.21 ZiB", "expected": int(2.21 * 2**70)}),
388 ("YiB", {"value": "2.21 YiB", "expected": int(2.21 * 2**80)}),
389 ("whitespace", {"value": "2.21 GiB", "expected": int(2.21 * 2**30)}),
390 ("zero", {"value": "0 TiB", "expected": 0}),
391 )
392
393 def test__convert_size_to_bytes(self):
394 self.assertEqual(self.expected, convert_size_to_bytes(self.value))
395
396
397class TestConvertSizeToBytesErrors(MAASTestCase):
398 """Error handling tests for `convert_size_to_bytes`."""
399
400 def test__unknown_capacity_unit(self):
401 error = self.assertRaises(
402 UnknownCapacityUnitError, convert_size_to_bytes, "200 superbytes")
403 self.assertEqual("Unknown capacity unit 'superbytes'", str(error))
404
405 def test__empty_string(self):
406 self.assertRaises(ValueError, convert_size_to_bytes, "")
407
408 def test__empty_value(self):
409 self.assertRaises(ValueError, convert_size_to_bytes, " KiB")

Subscribers

People subscribed via source and target branches