4
# Copyright (C) 2013 Canonical
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22
# health-check-test-pid.py pid [ path-to-threshold-files ]
24
# The process name is resolved and the tool will use a
25
# `procname`.threshold file to compare against. If this file does not exist,
26
# default.threshold is used.
37
# Default test run durations in seconds
41
default_threshold_path = os.path.join(
42
os.path.join(os.path.dirname(__file__), 'thresholds'), platform.machine())
44
default_pass_unknown_process = True
47
def read_threshold(procname):
48
"""Parse thresholds file.
50
lines starting with '#' are comments
51
format is: key value, e.g.
52
health-check.cpu-load.cpu-load-total.total-cpu-percent 0.5
53
health-check.cpu-load.cpu-load-total.user-cpu-percent 0.5
54
health-check.cpu-load.cpu-load-total.system-cpu-percent 0.5
56
fname = default_threshold_path + "/" + procname + ".threshold"
60
with open(fname) as file:
63
if len(line) > 1 and not line.startswith("#"):
66
thresholds[tmp[0]] = tmp[1]
69
"Threshold file %s line %d format error" % (fname, n))
74
def check_threshold(data, key, fullkey, threshold):
75
"""Locate a threshold in the JSON data, compare it to the threshold"""
81
"health-check JSON data does not have key " + fullkey + "\n")
82
return (True, "Attribute not found and ignored")
86
return check_threshold(d, key, fullkey, threshold)
90
cmp = str(threshold) + " >= " + str(val)
93
cmp = str(threshold) + " < " + str(val)
97
def check_thresholds(procname, data, thresholds):
98
print("process: " + procname)
100
for key in sorted(thresholds.keys()):
101
if key.startswith("health-check"):
102
threshold = float(thresholds[key])
103
(ret, str) = check_threshold(data, key.split('.'), key, threshold)
110
sys.stderr.write(msg + ": " + str + ": " + key + "\n")
115
def health_check(pid, procname):
116
"""run health-check on a given process
118
:return: True if failed, False if passed
120
thresholds = read_threshold(procname)
122
# Can't test without thresholds
124
if len(thresholds) == 0:
125
if default_pass_unknown_process:
126
sys.stderr.write("No thresholds for process " + procname + "\n")
127
sys.stderr.write("Defaulting to pass this test\n")
130
thresholds = read_threshold("default")
131
if len(thresholds) == 0:
133
"No thresholds for process " + procname + "\n")
136
"Using default thresholds for process " + procname + "\n")
138
duration = default_duration
140
if 'duration' in thresholds:
141
duration = int(thresholds['duration'])
143
filename = "health-check-" + str(pid) + ".json"
145
'health-check', '-c', '-f', '-d', str(duration),
146
'-w', '-W', '-r', '-p', str(pid), '-o', filename
150
subprocess.check_output(args)
151
with open(filename) as f:
153
return check_thresholds(procname, data, thresholds)
154
except subprocess.CalledProcessError as e:
162
p = psutil.Process(pid)
163
pgid = os.getpgid(pid)
166
"Cannot run health-check on kernel task with PID(%d)\n" % pid)
169
except psutil.NoSuchProcess as e:
170
sys.stderr.write('%s\n' % e)
173
sys.stderr.write("Cannot find pgid on process with PID %d\n" % pid)
174
return proc.as_dict(attrs=['pid', 'name'])
179
sys.stderr.write("Need to run as root\n")
186
procname = os.path.basename(p['name'])
187
if (health_check(pid, procname)):
192
if __name__ == '__main__':
193
if len(sys.argv) != 2:
194
sys.stderr.write("Usage: %s PID\n" % sys.argv[0])
197
pid = int(sys.argv[1])