Report ZFS pool capacity and usage statistics

When LXD is using a ZFS backend, correctly report disk stats
based on the usage of the ZFS pool rather than the usage of
the filesystem at /var/lib/lxd.

Change-Id: I2f2b843156cdc47c2f3324d69c384f943a0e1499
Closes-Bug: 1680869
This commit is contained in:
James Page 2017-04-07 17:03:02 +01:00
parent a91cdd62ce
commit 02ce341738
4 changed files with 115 additions and 2 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@
# Packages
*.egg
*.eggs
*.egg-info
dist
build

View File

@ -3,4 +3,5 @@
[Filters]
zfs: CommandFilter, zfs, root
zpool: CommandFilter, zpool, root
btrfs: CommandFilter, btrfs, root

View File

@ -1040,8 +1040,84 @@ class LXDDriverTest(test.NoDBTestCase):
'\n'),
meminfo,
]
lxd_config = {
'environment': {
'storage': 'dir',
},
'config': {}
}
lxd_driver = driver.LXDDriver(None)
lxd_driver.client = mock.MagicMock()
lxd_driver.client.host_info = lxd_config
value = lxd_driver.get_available_resource(None)
# This is funky, but json strings make for fragile tests.
value['cpu_info'] = json.loads(value['cpu_info'])
self.assertEqual(expected, value)
@mock.patch('socket.gethostname', mock.Mock(return_value='fake_hostname'))
@mock.patch('nova.virt.lxd.driver.open')
@mock.patch.object(driver.utils, 'execute')
def test_get_available_resource_zfs(self, execute, open):
expected = {
'cpu_info': {
"features": "fake flag goes here",
"model": "Fake CPU",
"topology": {"sockets": "10", "threads": "4", "cores": "5"},
"arch": "x86_64", "vendor": "FakeVendor"
},
'hypervisor_hostname': 'fake_hostname',
'hypervisor_type': 'lxd',
'hypervisor_version': '011',
'local_gb': 2222,
'local_gb_used': 200,
'memory_mb': 10000,
'memory_mb_used': 8000,
'numa_topology': None,
'supported_instances': [
('i686', 'lxd', 'exe'),
('x86_64', 'lxd', 'exe'),
('i686', 'lxc', 'exe'),
('x86_64', 'lxc', 'exe')],
'vcpus': 200,
'vcpus_used': 0}
execute.side_effect = [
('Model name: Fake CPU\n'
'Vendor ID: FakeVendor\n'
'Socket(s): 10\n'
'Core(s) per socket: 5\n'
'Thread(s) per core: 4\n\n',
None),
('2.17T\n', None),
('200.4G\n', None),
('1.8T\n', None)
]
meminfo = mock.MagicMock()
meminfo.__enter__.return_value = six.moves.cStringIO(
'MemTotal: 10240000 kB\n'
'MemFree: 2000000 kB\n'
'Buffers: 24000 kB\n'
'Cached: 24000 kB\n')
open.side_effect = [
six.moves.cStringIO('flags: fake flag goes here\n'
'processor: 2\n'
'\n'),
meminfo,
]
lxd_config = {
'environment': {
'storage': 'zfs',
},
'config': {
'storage.zfs_pool_name': 'lxd',
}
}
lxd_driver = driver.LXDDriver(None)
lxd_driver.client = mock.MagicMock()
lxd_driver.client.host_info = lxd_config
value = lxd_driver.get_available_resource(None)
# This is funky, but json strings make for fragile tests.
value['cpu_info'] = json.loads(value['cpu_info'])

View File

@ -62,6 +62,7 @@ import psutil
from oslo_concurrency import lockutils
from nova.compute import task_states
from oslo_utils import excutils
from oslo_utils import strutils
from nova.virt import firewall
_ = i18n._
@ -160,6 +161,28 @@ def _get_fs_info(path):
'used': used}
def _get_zpool_info(pool):
"""Get free/used/total disk space in a zfs pool."""
def _get_zpool_attribute(attribute):
value, err = utils.execute('zpool', 'list',
'-o', attribute,
'-H', pool,
run_as_root=True)
if err:
msg = _('Unable to parse zpool output.')
raise exception.NovaException(msg)
value = strutils.string_to_bytes('{}B'.format(value.strip()),
return_int=True)
return value
total = _get_zpool_attribute('size')
used = _get_zpool_attribute('alloc')
available = _get_zpool_attribute('free')
return {'total': total,
'available': available,
'used': used}
def _get_power_state(lxd_state):
"""Take a lxd state code and translate it to nova power state."""
state_map = [
@ -858,7 +881,19 @@ class LXDDriver(driver.ComputeDriver):
int(cpu_topology['threads']))
local_memory_info = _get_ram_usage()
local_disk_info = _get_fs_info(CONF.lxd.root_dir)
lxd_config = self.client.host_info
# NOTE(jamespage): ZFS storage report is very LXD 2.0.x
# centric and will need to be updated
# to support LXD storage pools
storage_driver = lxd_config['environment']['storage']
if storage_driver == 'zfs':
local_disk_info = _get_zpool_info(
lxd_config['config']['storage.zfs_pool_name']
)
else:
local_disk_info = _get_fs_info(CONF.lxd.root_dir)
data = {
'vcpus': vcpus,