Add pool capacity pollsters

This adds code to retrieve pool capacity metrics
via the cinder API.

Change-Id: Ib84f33a91b6a69f56b8cff9da431720cb58f5d33
This commit is contained in:
Jaromir Wysoglad 2025-03-05 04:14:47 -05:00 committed by Takashi Kajinami
parent d778892271
commit 3922db4f3d
7 changed files with 272 additions and 9 deletions

View File

@ -5,7 +5,8 @@
required-projects:
- opendev.org/openstack/grenade
- opendev.org/openstack/ceilometer
- gnocchixyz/gnocchi
- name: gnocchixyz/gnocchi
override-checkout: stable/4.6
vars:
configure_swap_size: 8192
grenade_devstack_localrc:

View File

@ -134,6 +134,34 @@ BACKUP_LIST = [
'size': 1})
]
POOL_LIST = [
type('VolumePool', (object,),
{'name': 'localhost.localdomain@lvmdriver-1#lvmdriver-1',
'pool_name': 'lvmdriver-1',
'total_capacity_gb': 28.5,
'free_capacity_gb': 28.39,
'reserved_percentage': 0,
'location_info':
'LVMVolumeDriver:localhost.localdomain:stack-volumes:thin:0',
'QoS_support': False,
'provisioned_capacity_gb': 4.0,
'max_over_subscription_ratio': 20.0,
'thin_provisioning_support': True,
'thick_provisioning_support': False,
'total_volumes': 3,
'filter_function': None,
'goodness_function': None,
'multiattach': True,
'backend_state': 'up',
'allocated_capacity_gb': 4,
'cacheable': True,
'volume_backend_name': 'lvmdriver-1',
'storage_protocol': 'iSCSI',
'vendor_name': 'Open Source',
'driver_version': '3.0.0',
'timestamp': '2025-03-21T14:19:02.901750'})
]
class TestVolumeSizePollster(base.BaseTestCase):
def setUp(self):
@ -203,3 +231,94 @@ class TestVolumeBackupSizePollster(base.BaseTestCase):
self.assertEqual(1, volume_backup_size_samples[0].volume)
self.assertEqual('75a52125-85ff-4a8d-b2aa-580f3b22273f',
volume_backup_size_samples[0].resource_id)
class TestVolumeProviderPoolCapacityTotalPollster(base.BaseTestCase):
def setUp(self):
super(TestVolumeProviderPoolCapacityTotalPollster, self).setUp()
conf = service.prepare_service([], [])
self.manager = manager.AgentManager(0, conf)
self.pollster = cinder.VolumeProviderPoolCapacityTotal(conf)
def test_volume_provider_pool_capacity_total_pollster(self):
volume_pool_size_total_samples = list(
self.pollster.get_samples(self.manager, {}, resources=POOL_LIST))
self.assertEqual(1, len(volume_pool_size_total_samples))
self.assertEqual('volume.provider.pool.capacity.total',
volume_pool_size_total_samples[0].name)
self.assertEqual(28.5, volume_pool_size_total_samples[0].volume)
self.assertEqual('localhost.localdomain@lvmdriver-1#lvmdriver-1',
volume_pool_size_total_samples[0].resource_id)
class TestVolumeProviderPoolCapacityFreePollster(base.BaseTestCase):
def setUp(self):
super(TestVolumeProviderPoolCapacityFreePollster, self).setUp()
conf = service.prepare_service([], [])
self.manager = manager.AgentManager(0, conf)
self.pollster = cinder.VolumeProviderPoolCapacityFree(conf)
def test_volume_provider_pool_capacity_free_pollster(self):
volume_pool_size_free_samples = list(
self.pollster.get_samples(self.manager, {}, resources=POOL_LIST))
self.assertEqual(1, len(volume_pool_size_free_samples))
self.assertEqual('volume.provider.pool.capacity.free',
volume_pool_size_free_samples[0].name)
self.assertEqual(28.39, volume_pool_size_free_samples[0].volume)
self.assertEqual('localhost.localdomain@lvmdriver-1#lvmdriver-1',
volume_pool_size_free_samples[0].resource_id)
class TestVolumeProviderPoolCapacityProvisionedPollster(base.BaseTestCase):
def setUp(self):
super(TestVolumeProviderPoolCapacityProvisionedPollster, self).setUp()
conf = service.prepare_service([], [])
self.manager = manager.AgentManager(0, conf)
self.pollster = cinder.VolumeProviderPoolCapacityProvisioned(conf)
def test_volume_provider_pool_capacity_provisioned_pollster(self):
volume_pool_size_provisioned_samples = list(
self.pollster.get_samples(self.manager, {}, resources=POOL_LIST))
self.assertEqual(1, len(volume_pool_size_provisioned_samples))
self.assertEqual('volume.provider.pool.capacity.provisioned',
volume_pool_size_provisioned_samples[0].name)
self.assertEqual(4.0, volume_pool_size_provisioned_samples[0].volume)
self.assertEqual('localhost.localdomain@lvmdriver-1#lvmdriver-1',
volume_pool_size_provisioned_samples[0].resource_id)
class TestVolumeProviderPoolCapacityVirtualFreePollster(base.BaseTestCase):
def setUp(self):
super(TestVolumeProviderPoolCapacityVirtualFreePollster, self).setUp()
conf = service.prepare_service([], [])
self.manager = manager.AgentManager(0, conf)
self.pollster = cinder.VolumeProviderPoolCapacityVirtualFree(conf)
def test_volume_provider_pool_capacity_virtual_free_pollster(self):
volume_pool_size_virtual_free_samples = list(
self.pollster.get_samples(self.manager, {}, resources=POOL_LIST))
self.assertEqual(1, len(volume_pool_size_virtual_free_samples))
self.assertEqual('volume.provider.pool.capacity.virtual_free',
volume_pool_size_virtual_free_samples[0].name)
self.assertEqual(566.0,
volume_pool_size_virtual_free_samples[0].volume)
self.assertEqual('localhost.localdomain@lvmdriver-1#lvmdriver-1',
volume_pool_size_virtual_free_samples[0].resource_id)
class TestVolumeProviderPoolCapacityAllocatedPollster(base.BaseTestCase):
def setUp(self):
super(TestVolumeProviderPoolCapacityAllocatedPollster, self).setUp()
conf = service.prepare_service([], [])
self.manager = manager.AgentManager(0, conf)
self.pollster = cinder.VolumeProviderPoolCapacityAllocated(conf)
def test_volume_provider_pool_capacity_allocated_pollster(self):
volume_pool_size_allocated_samples = list(
self.pollster.get_samples(self.manager, {}, resources=POOL_LIST))
self.assertEqual(1, len(volume_pool_size_allocated_samples))
self.assertEqual('volume.provider.pool.capacity.allocated',
volume_pool_size_allocated_samples[0].name)
self.assertEqual(4, volume_pool_size_allocated_samples[0].volume)
self.assertEqual('localhost.localdomain@lvmdriver-1#lvmdriver-1',
volume_pool_size_allocated_samples[0].resource_id)

View File

@ -12,6 +12,7 @@
# under the License.
"""Common code for working with volumes
"""
import math
from ceilometer.polling import plugin_base
from ceilometer import sample
@ -117,3 +118,119 @@ class VolumeBackupSize(_Base):
resource_id=backup.id,
resource_metadata=self.extract_metadata(backup),
)
class VolumeProviderPoolCapacityTotal(_Base):
@property
def default_discovery(self):
return 'volume_pools'
FIELDS = ['pool_name']
def get_samples(self, manager, cache, resources):
for pool in resources:
yield sample.Sample(
name='volume.provider.pool.capacity.total',
type=sample.TYPE_GAUGE,
unit='GB',
volume=pool.total_capacity_gb,
user_id=None,
project_id=None,
resource_id=pool.name,
resource_metadata=self.extract_metadata(pool),
)
class VolumeProviderPoolCapacityFree(_Base):
@property
def default_discovery(self):
return 'volume_pools'
FIELDS = ['pool_name']
def get_samples(self, manager, cache, resources):
for pool in resources:
yield sample.Sample(
name='volume.provider.pool.capacity.free',
type=sample.TYPE_GAUGE,
unit='GB',
volume=pool.free_capacity_gb,
user_id=None,
project_id=None,
resource_id=pool.name,
resource_metadata=self.extract_metadata(pool),
)
class VolumeProviderPoolCapacityProvisioned(_Base):
@property
def default_discovery(self):
return 'volume_pools'
FIELDS = ['pool_name']
def get_samples(self, manager, cache, resources):
for pool in resources:
yield sample.Sample(
name='volume.provider.pool.capacity.provisioned',
type=sample.TYPE_GAUGE,
unit='GB',
volume=pool.provisioned_capacity_gb,
user_id=None,
project_id=None,
resource_id=pool.name,
resource_metadata=self.extract_metadata(pool),
)
class VolumeProviderPoolCapacityVirtualFree(_Base):
@property
def default_discovery(self):
return 'volume_pools'
FIELDS = ['pool_name']
def get_samples(self, manager, cache, resources):
for pool in resources:
reserved_size = math.floor(
(pool.reserved_percentage / 100) * pool.total_capacity_gb
)
max_over_subscription_ratio = 1.0
if pool.thin_provisioning_support:
max_over_subscription_ratio = pool.max_over_subscription_ratio
value = (
max_over_subscription_ratio *
(pool.total_capacity_gb - reserved_size) -
pool.provisioned_capacity_gb
)
yield sample.Sample(
name='volume.provider.pool.capacity.virtual_free',
type=sample.TYPE_GAUGE,
unit='GB',
volume=value,
user_id=None,
project_id=None,
resource_id=pool.name,
resource_metadata=self.extract_metadata(pool),
)
class VolumeProviderPoolCapacityAllocated(_Base):
@property
def default_discovery(self):
return 'volume_pools'
FIELDS = ['pool_name']
def get_samples(self, manager, cache, resources):
for pool in resources:
yield sample.Sample(
name='volume.provider.pool.capacity.allocated',
type=sample.TYPE_GAUGE,
unit='GB',
volume=pool.allocated_capacity_gb,
user_id=None,
project_id=None,
resource_id=pool.name,
resource_metadata=self.extract_metadata(pool),
)

View File

@ -61,3 +61,10 @@ class VolumeBackupsDiscovery(_BaseDiscovery):
"""Discover volume resources to monitor."""
return self.client.backups.list(search_opts={'all_tenants': True})
class VolumePoolsDiscovery(_BaseDiscovery):
def discover(self, manager, param=None):
"""Discover volume resources to monitor."""
return self.client.pools.list(detailed=True)

View File

@ -443,22 +443,24 @@ The following meters are collected for OpenStack Block Storage:
| | | | | | on host |
+--------------------+-------+--------+----------+----------+-----------------+
| volume.provider.po\| Gauge | GB | hostname\| Notifica\| Total volume |
| ol.capacity.total | | | #pool | tion | capacity in pool|
| ol.capacity.total | | | #pool | tion, Po\| capacity in pool|
| | | | | llster | |
+--------------------+-------+--------+----------+----------+-----------------+
| volume.provider.po\| Gauge | GB | hostname\| Notifica\| Free volume |
| ol.capacity.free | | | #pool | tion | capacity in pool|
| ol.capacity.free | | | #pool | tion, Po\| capacity in pool|
| | | | | llster | |
+--------------------+-------+--------+----------+----------+-----------------+
| volume.provider.po\| Gauge | GB | hostname\| Notifica\| Assigned volume |
| ol.capacity.alloca\| | | #pool | tion | capacity in pool|
| ted | | | | | by Cinder |
| ol.capacity.alloca\| | | #pool | tion, Po\| capacity in pool|
| ted | | | | llster | by Cinder |
+--------------------+-------+--------+----------+----------+-----------------+
| volume.provider.po\| Gauge | GB | hostname\| Notifica\| Assigned volume |
| ol.capacity.provis\| | | #pool | tion | capacity in pool|
| ioned | | | | | |
| ol.capacity.provis\| | | #pool | tion, Po\| capacity in pool|
| ioned | | | | llster | |
+--------------------+-------+--------+----------+----------+-----------------+
| volume.provider.po\| Gauge | GB | hostname\| Notifica\| Virtual free |
| ol.capacity.virtua\| | | #pool | tion | volume capacity |
| l_free | | | | | in pool |
| ol.capacity.virtua\| | | #pool | tion, Po\| volume capacity |
| l_free | | | | llster | in pool |
+--------------------+-------+--------+----------+----------+-----------------+
OpenStack File Share

View File

@ -0,0 +1,11 @@
---
features:
- |
Added the following meters to the central agent to capture these metrics
for each storage pool by API.
- `volume.provider.pool.capacity.total`
- `volume.provider.pool.capacity.free`
- `volume.provider.pool.capacity.provisioned`
- `volume.provider.pool.capacity.virtual_free`
- `volume.provider.pool.capacity.allocated`

View File

@ -60,6 +60,7 @@ ceilometer.discover.central =
fip_services = ceilometer.network.services.discovery:FloatingIPDiscovery
images = ceilometer.image.discovery:ImagesDiscovery
volumes = ceilometer.volume.discovery:VolumeDiscovery
volume_pools = ceilometer.volume.discovery:VolumePoolsDiscovery
volume_snapshots = ceilometer.volume.discovery:VolumeSnapshotsDiscovery
volume_backups = ceilometer.volume.discovery:VolumeBackupsDiscovery
@ -137,6 +138,11 @@ ceilometer.poll.central =
volume.size = ceilometer.volume.cinder:VolumeSizePollster
volume.snapshot.size = ceilometer.volume.cinder:VolumeSnapshotSize
volume.backup.size = ceilometer.volume.cinder:VolumeBackupSize
volume.provider.pool.capacity.total = ceilometer.volume.cinder:VolumeProviderPoolCapacityTotal
volume.provider.pool.capacity.free = ceilometer.volume.cinder:VolumeProviderPoolCapacityFree
volume.provider.pool.capacity.provisioned = ceilometer.volume.cinder:VolumeProviderPoolCapacityProvisioned
volume.provider.pool.capacity.virtual_free = ceilometer.volume.cinder:VolumeProviderPoolCapacityVirtualFree
volume.provider.pool.capacity.allocated = ceilometer.volume.cinder:VolumeProviderPoolCapacityAllocated
ceilometer.compute.virt =
libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector