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

Proposed by Данило Шеган
Status: Rejected
Rejected by: MAAS Lander
Proposed branch: lp:~danilo/maas/virsh-storage-units
Merge into: lp:maas/2.2
Diff against target: 141 lines (+63/-12)
2 files modified
src/provisioningserver/drivers/pod/tests/test_virsh.py (+23/-0)
src/provisioningserver/drivers/pod/virsh.py (+40/-12)
To merge this branch: bzr merge lp:~danilo/maas/virsh-storage-units
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Needs Fixing
Review via email: mp+325913@code.launchpad.net

Commit message

Consider size units for storage when evaluating virsh pod storage.

Description of the change

This changes .get_key_value() on the Virsh driver to not strip the unit, and introduces a "_unitless" variant that still does that. This highlights a few potential future problems as well (memory size in KiB, CPU speed in Mhz, if those units change, we'll have similar problems).

I wonder why did we not end up using libvirt instead which would provide us stable units, but I imagine they might not be py3 bindings available or something along those lines.

Testing instructions:

I have not tested this yet (I only have one system where I have >1TiB LVM volume), so it seems easier to test this with a small storage partition instead (eg. MiB to ensure that still works), but generally you need to:

 - set up a libvirt on a system and add a storage pool of >1TiB
 - ensure "virsh pool-info" lists the storage pool details in TiB
 - add a pod to MAAS and look at available storage: it should still be correct

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

This looks fine, but I'm not sure why we aren't using "virsh pool-dumpxml", which would report the units consistently in the first place. For example, I see the following:

...
  <capacity unit='bytes'>61966548992</capacity>
  <allocation unit='bytes'>35070156800</allocation>
  <available unit='bytes'>26896392192</available>
...

I'm going to approve anyway, since it seems like that work would be well beyond the scope of this branch.

review: Approve
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Actually, upon closer inspection, I think this needs fixing, because _convert_to_bytes() doesn't have unit tests that cover the entire function.

Also, I think _convert_to_bytes() should be placed in a more common location; it's more generally useful than a private function inside the virsh driver. I would find a home for it somewhere in src/provisioningserver/utils, and add unit tests for it to ensure it handles each unit correctly.

Finally, consider raising an exception for an unknown unit rather than just logging and carrying on.

review: Needs Fixing
Revision history for this message
Mike Pontillo (mpontillo) wrote :

While we're at it, we could also future-proof ourselves, such as:

CAPACITY_UNITS = {
    "KiB": 2 ** 10,
    "MiB": 2 ** 20,
    "GiB": 2 ** 30,
    "TiB": 2 ** 40,
    "PiB": 2 ** 50,
    "EiB": 2 ** 60,
    "ZiB": 2 ** 70,
    "YiB": 2 ** 80,
}

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

Transitioned to Git.

lp:maas/2.2 has now moved from Bzr to Git.
Please propose your branches with Launchpad using Git.

git clone -b 2.2 https://git.launchpad.net/maas

Unmerged revisions

6066. By Данило Шеган

Fix test data so it adds up.

6065. By Данило Шеган

Interpret units for virsh calls.

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-19 10:17:32 +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-19 10:17:32 +0000
@@ -273,7 +273,13 @@
273 data = data.strip().splitlines()273 data = data.strip().splitlines()
274 for d in data:274 for d in data:
275 if key == d.split(':')[0].strip():275 if key == d.split(':')[0].strip():
276 return d.split(':')[1].split()[0]276 return d.split(':')[1].strip()
277
278 def get_key_value_unitless(self, data, key):
279 """Return value based off of key with unit (if any) stripped off."""
280 value = self.get_key_value(data, key)
281 if value:
282 return value.split()[0]
277283
278 def list_machines(self):284 def list_machines(self):
279 """Lists all VMs by name."""285 """Lists all VMs by name."""
@@ -336,7 +342,7 @@
336 if output is None:342 if output is None:
337 maaslog.error("Failed to get pod CPU speed")343 maaslog.error("Failed to get pod CPU speed")
338 return None344 return None
339 return int(self.get_key_value(output, "CPU frequency"))345 return int(self.get_key_value_unitless(output, "CPU frequency"))
340346
341 def get_pod_memory(self):347 def get_pod_memory(self):
342 """Gets the total memory of the pod."""348 """Gets the total memory of the pod."""
@@ -344,7 +350,7 @@
344 if output is None:350 if output is None:
345 maaslog.error("Failed to get pod memory")351 maaslog.error("Failed to get pod memory")
346 return None352 return None
347 KiB = int(self.get_key_value(output, "Memory size"))353 KiB = int(self.get_key_value_unitless(output, "Memory size"))
348 # Memory in MiB.354 # Memory in MiB.
349 return int(KiB / 1024)355 return int(KiB / 1024)
350356
@@ -354,10 +360,34 @@
354 if output is None:360 if output is None:
355 maaslog.error("%s: Failed to get machine memory", machine)361 maaslog.error("%s: Failed to get machine memory", machine)
356 return None362 return None
357 KiB = int(self.get_key_value(output, "Max memory"))363 KiB = int(self.get_key_value_unitless(output, "Max memory"))
358 # Memory in MiB.364 # Memory in MiB.
359 return int(KiB / 1024)365 return int(KiB / 1024)
360366
367 def _convert_to_bytes(self, value):
368 """Converts values with KiB, MiB, GiB, TiB units to bytes."""
369 capacity_def = value.split(' ', 1)
370 if len(capacity_def) == 1:
371 # No unit specified, default to bytes.
372 capacity_value = int(capacity_def[0])
373 multiplier = 1
374 else:
375 capacity_value, capacity_unit = capacity_def
376 capacity_value = float(capacity_value)
377 if capacity_unit == 'TiB':
378 multiplier = 2**40
379 elif capacity_unit == 'GiB':
380 multiplier = 2**30
381 elif capacity_unit == 'MiB':
382 multiplier = 2**20
383 elif capacity_unit == 'KiB':
384 multiplier = 2**10
385 else:
386 maaslog.warn("Unknown capacity unit %s", capacity_unit)
387 multiplier = 1
388 # Convert value to bytes.
389 return int(capacity_value * multiplier)
390
361 def get_pod_pool_size_map(self, key):391 def get_pod_pool_size_map(self, key):
362 """Return the mapping for a size calculation based on key."""392 """Return the mapping for a size calculation based on key."""
363 pools = {}393 pools = {}
@@ -366,10 +396,8 @@
366 if output is None:396 if output is None:
367 # Skip if cannot get more information.397 # Skip if cannot get more information.
368 continue398 continue
369 capacity = float(399 pools[pool] = self._convert_to_bytes(
370 self.get_key_value(output, key))400 self.get_key_value(output, "Capacity"))
371 # Convert GiB to bytes.
372 pools[pool] = int(capacity * 2**30)
373 return pools401 return pools
374402
375 def get_pod_local_storage(self):403 def get_pod_local_storage(self):
@@ -390,10 +418,10 @@
390 maaslog.error(418 maaslog.error(
391 "Failed to get available pod local storage")419 "Failed to get available pod local storage")
392 return None420 return None
393 local_storage += float(self.get_key_value(421 local_storage += self._convert_to_bytes(
394 output, "Available"))422 self.get_key_value(output, "Available"))
395 # Local storage in bytes. GiB to bytes.423 # Local storage in bytes.
396 return int(local_storage * 2**30)424 return local_storage
397425
398 def get_machine_local_storage(self, machine, device):426 def get_machine_local_storage(self, machine, device):
399 """Gets the VM local storage for device."""427 """Gets the VM local storage for device."""

Subscribers

People subscribed via source and target branches