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
1=== modified file 'src/provisioningserver/drivers/pod/tests/test_virsh.py'
2--- src/provisioningserver/drivers/pod/tests/test_virsh.py 2017-05-19 03:37:56 +0000
3+++ src/provisioningserver/drivers/pod/tests/test_virsh.py 2017-06-19 10:17:32 +0000
4@@ -105,6 +105,17 @@
5 Available: 173.84 GiB
6 """)
7
8+SAMPLE_POOLINFO_TB = dedent("""
9+ Name: default
10+ UUID: 59edc0cb-4635-449a-80e2-2c8a59afa327
11+ State: running
12+ Persistent: yes
13+ Autostart: yes
14+ Capacity: 2.21 TiB
15+ Allocation: 880.64 GiB
16+ Available: 1.35 TiB
17+ """)
18+
19 SAMPLE_IFLIST = dedent("""
20 Interface Type Source Model MAC
21 -------------------------------------------------------
22@@ -506,6 +517,18 @@
23 for pool in pools_mock.return_value
24 }, expected)
25
26+ def test_get_pod_pool_size_map_terabytes(self):
27+ conn = self.configure_virshssh(SAMPLE_POOLINFO_TB)
28+ pools_mock = self.patch(virsh.VirshSSH, 'list_pools')
29+ pools_mock.return_value = [
30+ factory.make_name('pool') for _ in range(3)]
31+ expected = conn.get_pod_pool_size_map('Capacity')
32+ capacity = int(2.21 * 2**40)
33+ self.assertEqual({
34+ pool: capacity
35+ for pool in pools_mock.return_value
36+ }, expected)
37+
38 def test_get_pod_local_storage(self):
39 conn = self.configure_virshssh(SAMPLE_POOLINFO)
40 pools_mock = self.patch(virsh.VirshSSH, 'list_pools')
41
42=== modified file 'src/provisioningserver/drivers/pod/virsh.py'
43--- src/provisioningserver/drivers/pod/virsh.py 2017-05-19 03:57:03 +0000
44+++ src/provisioningserver/drivers/pod/virsh.py 2017-06-19 10:17:32 +0000
45@@ -273,7 +273,13 @@
46 data = data.strip().splitlines()
47 for d in data:
48 if key == d.split(':')[0].strip():
49- return d.split(':')[1].split()[0]
50+ return d.split(':')[1].strip()
51+
52+ def get_key_value_unitless(self, data, key):
53+ """Return value based off of key with unit (if any) stripped off."""
54+ value = self.get_key_value(data, key)
55+ if value:
56+ return value.split()[0]
57
58 def list_machines(self):
59 """Lists all VMs by name."""
60@@ -336,7 +342,7 @@
61 if output is None:
62 maaslog.error("Failed to get pod CPU speed")
63 return None
64- return int(self.get_key_value(output, "CPU frequency"))
65+ return int(self.get_key_value_unitless(output, "CPU frequency"))
66
67 def get_pod_memory(self):
68 """Gets the total memory of the pod."""
69@@ -344,7 +350,7 @@
70 if output is None:
71 maaslog.error("Failed to get pod memory")
72 return None
73- KiB = int(self.get_key_value(output, "Memory size"))
74+ KiB = int(self.get_key_value_unitless(output, "Memory size"))
75 # Memory in MiB.
76 return int(KiB / 1024)
77
78@@ -354,10 +360,34 @@
79 if output is None:
80 maaslog.error("%s: Failed to get machine memory", machine)
81 return None
82- KiB = int(self.get_key_value(output, "Max memory"))
83+ KiB = int(self.get_key_value_unitless(output, "Max memory"))
84 # Memory in MiB.
85 return int(KiB / 1024)
86
87+ def _convert_to_bytes(self, value):
88+ """Converts values with KiB, MiB, GiB, TiB units to bytes."""
89+ capacity_def = value.split(' ', 1)
90+ if len(capacity_def) == 1:
91+ # No unit specified, default to bytes.
92+ capacity_value = int(capacity_def[0])
93+ multiplier = 1
94+ else:
95+ capacity_value, capacity_unit = capacity_def
96+ capacity_value = float(capacity_value)
97+ if capacity_unit == 'TiB':
98+ multiplier = 2**40
99+ elif capacity_unit == 'GiB':
100+ multiplier = 2**30
101+ elif capacity_unit == 'MiB':
102+ multiplier = 2**20
103+ elif capacity_unit == 'KiB':
104+ multiplier = 2**10
105+ else:
106+ maaslog.warn("Unknown capacity unit %s", capacity_unit)
107+ multiplier = 1
108+ # Convert value to bytes.
109+ return int(capacity_value * multiplier)
110+
111 def get_pod_pool_size_map(self, key):
112 """Return the mapping for a size calculation based on key."""
113 pools = {}
114@@ -366,10 +396,8 @@
115 if output is None:
116 # Skip if cannot get more information.
117 continue
118- capacity = float(
119- self.get_key_value(output, key))
120- # Convert GiB to bytes.
121- pools[pool] = int(capacity * 2**30)
122+ pools[pool] = self._convert_to_bytes(
123+ self.get_key_value(output, "Capacity"))
124 return pools
125
126 def get_pod_local_storage(self):
127@@ -390,10 +418,10 @@
128 maaslog.error(
129 "Failed to get available pod local storage")
130 return None
131- local_storage += float(self.get_key_value(
132- output, "Available"))
133- # Local storage in bytes. GiB to bytes.
134- return int(local_storage * 2**30)
135+ local_storage += self._convert_to_bytes(
136+ self.get_key_value(output, "Available"))
137+ # Local storage in bytes.
138+ return local_storage
139
140 def get_machine_local_storage(self, machine, device):
141 """Gets the VM local storage for device."""

Subscribers

People subscribed via source and target branches