Fixed default process metrics.

The fix is from David Kennedy and then I added a much needed simple test
to make sure it stays fixed.

Change-Id: I79a562fd82834751fcd0bac7dd3b79d2d857696b
This commit is contained in:
Tim Kuhlman 2015-08-21 09:32:55 -06:00 committed by Craig Bryant
parent ffd53d8536
commit f3b24fc0ac
5 changed files with 166 additions and 45 deletions

View File

@ -84,7 +84,7 @@ class ProcessCheck(checks.AgentCheck):
# process metrics available for psutil versions 0.6.0 and later
extended_metrics_0_6_0 = (self.is_psutil_version_later_than((0, 6, 0))
and not util.Platform.is_win32())
# On Windows get_ext_memory_info returns different metrics
# On Windows ext_memory_info returns different metrics
if extended_metrics_0_6_0:
real = 0
voluntary_ctx_switches = 0
@ -116,10 +116,10 @@ class ProcessCheck(checks.AgentCheck):
try:
p = psutil.Process(pid)
if extended_metrics_0_6_0:
mem = p.get_ext_memory_info()
mem = p.memory_info_ex()
real += float((mem.rss - mem.shared) / 1048576)
try:
ctx_switches = p.get_num_ctx_switches()
ctx_switches = p.num_ctx_switches()
voluntary_ctx_switches += ctx_switches.voluntary
involuntary_ctx_switches += ctx_switches.involuntary
except NotImplementedError:
@ -127,11 +127,11 @@ class ProcessCheck(checks.AgentCheck):
voluntary_ctx_switches = None
involuntary_ctx_switches = None
else:
mem = p.get_memory_info()
mem = p.memory_info()
if extended_metrics_0_5_0_unix:
try:
open_file_descriptors = float(p.get_num_fds())
open_file_descriptors = float(p.num_fds())
max_open_file_descriptors = float(p.rlimit(psutil.RLIMIT_NOFILE)[1])
if max_open_file_descriptors > 0.0:
open_file_descriptors_perc = open_file_descriptors / max_open_file_descriptors * 100
@ -142,13 +142,13 @@ class ProcessCheck(checks.AgentCheck):
rss += float(mem.rss / 1048576)
vms += float(mem.vms / 1048576)
thr += p.get_num_threads()
cpu += p.get_cpu_percent(cpu_check_interval)
thr += p.num_threads()
cpu += p.cpu_percent(cpu_check_interval)
# user might not have permission to call get_io_counters()
# user might not have permission to call io_counters()
if read_count is not None:
try:
io_counters = p.get_io_counters()
io_counters = p.io_counters()
read_count += io_counters.read_count
write_count += io_counters.write_count
read_kbytes += float(io_counters.read_bytes / 1024)

48
tests/common.py Normal file
View File

@ -0,0 +1,48 @@
import os
import sys
import inspect
import monasca_agent.common.config as configuration
from monasca_agent.common.util import Paths
# Base config must be loaded before AgentCheck or it will try to load with no config file
base_config = configuration.Config(os.path.join(os.path.dirname(__file__), 'test-agent.yaml'))
from monasca_agent.collector.checks import AgentCheck
def load_check(name, config):
checksd_path = Paths().get_checksd_path()
if checksd_path not in sys.path:
sys.path.append(checksd_path)
check_module = __import__(name)
check_class = None
classes = inspect.getmembers(check_module, inspect.isclass)
for name, clsmember in classes:
if clsmember == AgentCheck:
continue
if issubclass(clsmember, AgentCheck):
check_class = clsmember
if AgentCheck in clsmember.__bases__:
continue
else:
break
if check_class is None:
raise Exception(
"Unable to import check %s. Missing a class that inherits AgentCheck" % name)
init_config = config.get('init_config', None)
instances = config.get('instances')
agent_config = base_config.get_config(sections='Main')
# init the check class
try:
return check_class(
name, init_config=init_config, agent_config=agent_config, instances=instances)
except:
# Backwards compatitiblity for old checks that don't support the
# instances argument.
c = check_class(name, init_config=init_config, agent_config=agent_config)
c.instances = instances
return c

53
tests/test-agent.yaml Normal file
View File

@ -0,0 +1,53 @@
Api:
# To configure Keystone correctly, a project-scoped token must be acquired.
# To accomplish this, the configuration must be set up with one of the
# following scenarios:
# Set username and password and you have a default project set in keystone.
# Set username, password and project id.
# Set username, password, project name and (domain id or domain name).
#
# Monitoring API URL: URL for the monitoring API, if undefined it will be pulled from the keystone service catalog
# Example: https://region-a.geo-1.monitoring.hpcloudsvc.com:8080/v2.0
url: http://localhost:8080
# Keystone Username
username: test
# Keystone Password
password: password
# Keystone API URL: URL for the Keystone server to use
# Example: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v3/
keystone_url: http://localhost:5000
# Project name to be used by this agent
project_name: test
# The following 2 options are for handling buffering and reconnection to the monasca-api
# If you want the messages to be sent as fast as possible, set these two options to
# the same number. If you have a larger system with many agents, you may want to throttle
# the number of messages sent to the API by setting the backlog_send_rate to a lower number.
# Maximum number of messages to buffer when unable to communicate with the monasca-api
max_buffer_size: 1000
# Maximum number of messages to send at one time when communication with the monasca-api is restored
backlog_send_rate: 1000
# Publish extra metrics to the API by adding this number of 'amplifier' dimensions.
# For load testing purposes only; set to 0 for production use.
Main:
# Force the hostname to whatever you want.
hostname: localhost
Logging:
# ========================================================================== #
# Logging
# ========================================================================== #
log_level: DEBUG
collector_log_file: /var/log/monasca/agent/collector.log
forwarder_log_file: /var/log/monasca/agent/forwarder.log
statsd_log_file: /var/log/monasca/agent/statsd.log
# if syslog is enabled but a host and port are not set, a local domain socket
# connection will be attempted
#
# log_to_syslog: yes
# syslog_host:
# syslog_port:

56
tests/test_process.py Normal file
View File

@ -0,0 +1,56 @@
import unittest
from tests.common import load_check
class TestSimpleProcess(unittest.TestCase):
def setUp(self):
config = {'init_config': {}, 'instances': [{'name': 'test',
'search_string': ['python'],
'detailed': False}]}
self.check = load_check('process', config)
def testPidCount(self):
self.check.run()
metrics = self.check.get_metrics()
self.assertTrue(len(metrics) == 1, metrics)
self.assertTrue(metrics[0].name == 'process.pid_count')
class TestDetailedProcess(unittest.TestCase):
def setUp(self):
config = {'init_config': {}, 'instances': [{'name': 'test',
'search_string': ['python'],
'detailed': True}]}
self.check = load_check('process', config)
def testPidCount(self):
self.check.run()
metrics = self.check.get_metrics()
self.assertTrue(len(metrics) > 1, metrics)
def testMeasurements(self):
self.check.run()
metrics = self.check.get_metrics()
measurement_names = []
for metric in metrics:
measurement_names.append(metric.name)
measurement_names.sort()
expected_names = ['process.cpu_perc',
'process.involuntary_ctx_switches',
'process.io.read_count',
'process.io.read_kbytes',
'process.io.write_count',
'process.io.write_kbytes',
'process.mem.real_mbytes',
'process.mem.rss_mbytes',
'process.mem.vsz_mbytes',
'process.open_file_descriptors',
'process.open_file_descriptors_perc',
'process.pid_count',
'process.thread_count',
'process.voluntary_ctx_switches']
self.assertTrue(measurement_names == expected_names)

View File

@ -8,42 +8,6 @@ from monasca_agent.common.util import Paths
from monasca_agent.common.util import get_os
def load_check(name, config, agent_config):
checksd_path = Paths().get_checksd_path()
if checksd_path not in sys.path:
sys.path.append(checksd_path)
check_module = __import__(name)
check_class = None
classes = inspect.getmembers(check_module, inspect.isclass)
for name, clsmember in classes:
if clsmember == AgentCheck:
continue
if issubclass(clsmember, AgentCheck):
check_class = clsmember
if AgentCheck in clsmember.__bases__:
continue
else:
break
if check_class is None:
raise Exception(
"Unable to import check %s. Missing a class that inherits AgentCheck" % name)
init_config = config.get('init_config', None)
instances = config.get('instances')
# init the check class
try:
return check_class(
name, init_config=init_config, agent_config=agent_config, instances=instances)
except:
# Backwards compatitiblity for old checks that don't support the
# instances argument.
c = check_class(name, init_config=init_config, agent_config=agent_config)
c.instances = instances
return c
def kill_subprocess(process_obj):
try:
process_obj.terminate()