Do not drop firewalls with unknown states

Currently, Ceilometer checks the state of Neutron FWaaS firewalls and
does not generate samples if they have unknown status, leaving a
warning in the Ceilometer logs.

There are two major issues with this:

* It makes visibility of resources with unknown/invalid state worse,
  since samples are not being generated for affected resources at all.
* Downstream rating/billing services such as CloudKitty prefer to know
  everything about as many resources as possible, so business logic can
  be implemented around that information. Sometimes resources should be
  billed even if they are in unknown/error state, because they still
  consume resources.

This patch changes the network.services.firewall pollster to always
sample all found firewalls. For resources with unknown status or
status None, the sample volume is set to -1.

This lines FWaaS polling up with the changes previously made for
floating IPs and VPNaaS [1]. FWaaS was not done as at the time
FWaaS polling was deprecated.

[1]: https://review.opendev.org/c/openstack/ceilometer/+/942022

Change-Id: I79d31127267facab30a18a137144a525fa10379c
Signed-off-by: Callum Dickinson <callum.dickinson@catalystcloud.nz>
This commit is contained in:
Callum Dickinson
2026-01-08 07:12:28 +13:00
parent d2e91ba450
commit 261157efdb
3 changed files with 62 additions and 31 deletions

View File

@@ -42,12 +42,12 @@ class FirewallPollster(base.BaseServicesPollster):
LOG.debug("Firewall : %s", fw)
status = self.get_status_id(fw['status'])
if status == -1:
# unknown status, skip this sample
LOG.warning("Unknown status %(stat)s received on fw %(id)s,"
"skipping sample",
{'stat': fw['status'], 'id': fw['id']})
continue
LOG.warning(
"Unknown status %(status)s for firewall %(name)s "
"(%(id)s), setting volume to -1",
{"status": fw['status'],
"name": fw['name'],
"id": fw['id']})
yield sample.Sample(
name='network.services.firewall',
type=sample.TYPE_GAUGE,

View File

@@ -43,77 +43,96 @@ class TestFirewallPollster(_BaseTestFWPollster):
def setUp(self):
super().setUp()
self.pollster = fwaas.FirewallPollster(self.CONF)
fake_fw = self.fake_get_fw_service()
self.fake_fw = self.fake_get_fw_service()
self.useFixture(fixtures.MockPatch('ceilometer.neutron_client.Client.'
'firewall_get_all',
return_value=fake_fw))
return_value=self.fake_fw))
@staticmethod
def fake_get_fw_service():
return [{'status': 'ACTIVE',
'name': 'myfw',
'name': 'myfw1',
'description': '',
'admin_state_up': True,
'id': 'fdde3d818-fdcb-fg4b-de7f-6750dc8a9d7a',
'firewall_policy_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a',
'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa'},
{'status': 'INACTIVE',
'name': 'myfw',
'name': 'myfw2',
'description': '',
'admin_state_up': True,
'id': 'fdde3d818-fdcb-fg4b-de7f-6750dc8a9d7a',
'firewall_policy_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a',
'id': 'e0d707dc-6194-4471-8286-0635bf65a055',
'firewall_policy_id': 'e0d707dc-6194-4471-8286-0635bf65a055',
'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa'},
{'status': 'PENDING_CREATE',
'name': 'myfw',
'name': 'myfw3',
'description': '',
'admin_state_up': True,
'id': 'fdde3d818-fdcb-fg4b-de7f-6750dc8a9d7a',
'id': 'e538d353-31e9-4581-a511-0a487ff71d0d',
'firewall_policy_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a',
'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa'},
{'status': 'error',
'name': 'myfw',
{'status': 'ERROR',
'name': 'myfw4',
'description': '',
'admin_state_up': True,
'id': 'fdde3d818-fdcb-fg4b-de7f-6750dc8a9d7a',
'firewall_policy_id': 'bbe3d818-bdcb-4e4b-b47f-5650dc8a9d7a',
'id': '06f698c4-dc63-43c4-a2d9-7b978e80f09a',
'firewall_policy_id': 'bef98f97-789f-418e-82ad-3e5d69618916',
'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa'},
{'status': 'UNKNOWN',
'name': 'myfw5',
'description': '',
'admin_state_up': True,
'id': 'c65a1bec-ab59-44ce-b784-1c725f427998',
'firewall_policy_id': 'd45b975e-738f-42c3-a4b3-760d3a58ab51',
'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa'},
{'status': None,
'name': 'myfw6',
'description': '',
'admin_state_up': True,
'id': 'ab5d19ff-32a8-49e5-aa2b-d008157359d9',
'firewall_policy_id': '79b9c933-2a7c-4f93-bbf9-d165f0326581',
'tenant_id': 'a4eb9f4938bb418bbc4f8eb31802fefa'},
]
def test_fw_get_samples(self):
samples = list(self.pollster.get_samples(
self.manager, {},
resources=self.fake_get_fw_service()))
self.assertEqual(4, len(samples))
for field in self.pollster.FIELDS:
self.assertEqual(self.fake_get_fw_service()[0][field],
samples[0].resource_metadata[field])
resources=self.fake_fw))
self.assertEqual(len(self.fake_fw), len(samples))
self.assertEqual({fw['id'] for fw in self.fake_fw},
{sample.resource_id for sample in samples})
samples_dict = {sample.resource_id: sample for sample in samples}
for fw in self.fake_fw:
sample = samples_dict[fw['id']]
for field in self.pollster.FIELDS:
self.assertEqual(fw[field],
sample.resource_metadata[field])
def test_vpn_volume(self):
samples = list(self.pollster.get_samples(
self.manager, {},
resources=self.fake_get_fw_service()))
resources=self.fake_fw))
self.assertEqual(1, samples[0].volume)
self.assertEqual(0, samples[1].volume)
self.assertEqual(2, samples[2].volume)
self.assertEqual(7, samples[3].volume)
self.assertEqual(-1, samples[4].volume)
self.assertEqual(-1, samples[5].volume)
def test_get_vpn_meter_names(self):
samples = list(self.pollster.get_samples(
self.manager, {},
resources=self.fake_get_fw_service()))
resources=self.fake_fw))
self.assertEqual({'network.services.firewall'},
{s.name for s in samples})
def test_vpn_discovery(self):
discovered_fws = discovery.FirewallDiscovery(
self.CONF).discover(self.manager)
self.assertEqual(3, len(discovered_fws))
self.assertEqual(len(self.fake_fw), len(discovered_fws))
for vpn in self.fake_get_fw_service():
if vpn['status'] == 'error':
self.assertNotIn(vpn, discovered_fws)
else:
self.assertIn(vpn, discovered_fws)
for vpn in self.fake_fw:
self.assertIn(vpn, discovered_fws)
class TestIPSecConnectionsPollster(_BaseTestFWPollster):

View File

@@ -0,0 +1,12 @@
---
upgrade:
- |
The ``network.services.firewall`` pollster now publishes samples for all
found firewalls, even if they are known to have an unknown state, when
they would previously be dropped. The volume of samples for such firewalls
will be set to ``-1``. This improves visibility of firewalls with unknown
states, allowing them to be monitored via samples and the
Gnocchi/Prometheus metrics, making it easier to discover such resources
for troubleshooting. It also moves some of the "business logic" for
downstream rating/billing services such as CloudKitty out of Ceilometer
itself.