diff --git a/docs/collectors/CPUCollector.md b/docs/collectors/CPUCollector.md index a03911154..a49f7c386 100644 --- a/docs/collectors/CPUCollector.md +++ b/docs/collectors/CPUCollector.md @@ -17,6 +17,7 @@ Setting | Default | Description | Type --------|---------|-------------|----- byte_unit | byte | Default numeric output(s) | str enabled | False | Enable collecting these metrics | bool +extended | False | return aggregate CPU% metric and complex CPU metrics | str measure_collector_time | False | Collect the collector run time in ms | bool metrics_blacklist | None | Regex to match metrics to block. Mutually exclusive with metrics_whitelist | NoneType metrics_whitelist | None | Regex to match metrics to transmit. Mutually exclusive with metrics_blacklist | NoneType @@ -33,4 +34,3 @@ servers.hostname.cpu.total.nice 0.0 servers.hostname.cpu.total.system 0.2 servers.hostname.cpu.total.user 0.4 ``` - diff --git a/src/collectors/cpu/cpu.py b/src/collectors/cpu/cpu.py index fdb93f9ce..d30a0ed55 100644 --- a/src/collectors/cpu/cpu.py +++ b/src/collectors/cpu/cpu.py @@ -43,6 +43,7 @@ def get_default_config_help(self): config_help.update({ 'percore': 'Collect metrics per cpu core or just total', 'simple': 'only return aggregate CPU% metric', + 'extended': 'return aggregate CPU% metric and complex CPU metrics', 'normalize': 'for cpu totals, divide by the number of CPUs', }) return config_help @@ -57,6 +58,7 @@ def get_default_config(self): 'percore': 'True', 'xenfix': None, 'simple': 'False', + 'extended': 'False', 'normalize': 'False', }) return config @@ -91,12 +93,16 @@ def cpu_delta_time(interval): if os.access(self.PROC, os.R_OK): - # If simple only return aggregate CPU% metric - if str_to_bool(self.config['simple']): + # If simple only return aggregate CPU% metric, unless extended is + # set (in which case return both) + if str_to_bool(self.config['simple']) or \ + str_to_bool(self.config['extended']): dt = cpu_delta_time(self.INTERVAL) cpuPct = 100 - (dt[len(dt) - 1] * 100.00 / sum(dt)) self.publish('percent', str('%.4f' % cpuPct)) - return True + # Only return simple metrics, unless the `extended` flag is set + if not str_to_bool(self.config['extended']): + return True results = {} # Open file diff --git a/src/collectors/cpu/test/testcpu.py b/src/collectors/cpu/test/testcpu.py index 44336e20b..f44268e3d 100644 --- a/src/collectors/cpu/test/testcpu.py +++ b/src/collectors/cpu/test/testcpu.py @@ -257,6 +257,106 @@ def test_should_work_psutil(self, psutil_mock, os_mock, publish_mock): self.assertPublishedMany(publish_mock, self.expected) + +class TestCPUCollectorSimple(CollectorTestCase): + + def setUp(self): + self.config = get_collector_config('CPUCollector', { + 'interval': 10, + 'normalize': False, + 'simple': True, + }) + + self.collector = CPUCollector(self.config, None) + + def test_import(self): + self.assertTrue(CPUCollector) + + @patch.object(Collector, 'publish') + def test_produces_simple_percent(self, publish_mock): + # when the simple config option is set, we check the CPU values twice + # to calculate a delta, so we need two mock values + m = Mock() + patch_open = patch('__builtin__.open', m) + m.side_effect = [ + StringIO('cpu 100 200 300 400 500 0 0 0 0 0'), + StringIO('cpu 110 220 330 440 550 0 0 0 0 0'), + ] + + patch_open.start() + self.collector.collect() + patch_open.stop() + + self.assertPublishedMany(publish_mock, {}) + + m = Mock() + patch_open = patch('__builtin__.open', m) + m.side_effect = [ + StringIO('cpu 110 220 330 440 550 0 0 0 0 0'), + StringIO('cpu 120 230 340 450 560 0 0 0 0 0'), + ] + + patch_open.start() + self.collector.collect() + patch_open.stop() + + self.assertPublishedMany(publish_mock, { + 'percent': 75.0 + }) + + +class TestCPUCollectorExtended(CollectorTestCase): + + def setUp(self): + self.config = get_collector_config('CPUCollector', { + 'interval': 10, + 'normalize': False, + 'extended': True, + }) + + self.collector = CPUCollector(self.config, None) + + def test_import(self): + self.assertTrue(CPUCollector) + + @patch.object(Collector, 'publish') + def test_produces_simple_and_complex(self, publish_mock): + # similar to above, we need three mock values: two for the simple + # metric (to calculate a delta), plus one more for the standard + # metrics + patch_open = patch('__builtin__.open', Mock(side_effect=[ + StringIO('cpu 100 200 300 400 500 0 0 0 0 0'), + StringIO('cpu 110 220 330 440 550 0 0 0 0 0'), + StringIO('cpu 100 200 300 400 500 0 0 0 0 0'), + ])) + + patch_open.start() + self.collector.collect() + patch_open.stop() + + self.assertPublishedMany(publish_mock, {}) + + patch_open = patch('__builtin__.open', Mock(side_effect=[ + StringIO('cpu 110 220 330 440 550 0 0 0 0 0'), + StringIO('cpu 120 230 340 450 560 0 0 0 0 0'), + StringIO('cpu 110 220 330 440 550 0 0 0 0 0'), + ])) + + patch_open.start() + self.collector.collect() + patch_open.stop() + + # Since the `extended` config option is set, we should see both simple + # percent-only metrics, but also the standard metrics + self.assertPublishedMany(publish_mock, { + 'total.user': 1.0, + 'total.nice': 2.0, + 'total.system': 3.0, + 'total.idle': 4.0, + 'percent': 75.0 + }) + + ########################################################################## if __name__ == "__main__": unittest.main()