Merge lp:~bjornt/landscape-client/process-state-key-error into lp:~landscape/landscape-client/trunk

Proposed by Björn Tillenius
Status: Merged
Approved by: Jerry Seutter
Approved revision: 591
Merged at revision: 585
Proposed branch: lp:~bjornt/landscape-client/process-state-key-error
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 204 lines (+91/-26)
3 files modified
landscape/lib/process.py (+21/-23)
landscape/lib/tests/test_process.py (+67/-0)
landscape/monitor/tests/test_activeprocessinfo.py (+3/-3)
To merge this branch: bzr merge lp:~bjornt/landscape-client/process-state-key-error
Reviewer Review Type Date Requested Status
Jerry Seutter (community) Approve
Alberto Donato (community) Approve
Review via email: mp+128940@code.launchpad.net

Description of the change

Handle new process states in when getting process information.

The bug report was about "t (tracing stop)", but a bunch of other states
can occur as well that we don't handle. I've change the code to use the
one-character representation of the process state that the kernel uses,
instead of using our own mapping. This ensure that future state
additions will be handled as well. As a result, we now represent the
"tracing stop" state as "t" instead of "I", but as far as I can see, the
only state we're explicitly checking for in our code is Z, so that
change shouldn't cause a problem.

I put in a special-case for "T (tracing stop)", since in Lucid you can
have both "T (stopped)" and "T (tracing stop)". I made it so that the
latter is represented by "t", since that's the way it is in releases
newer than Lucid.

To post a comment you must log in.
Revision history for this message
Alberto Donato (ack) wrote :

Nice fix, +1!

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

#1:
I have a test failure in landscape.monitor.tests.test_activeprocessinfo.ActiveProcessInfoTest.test_read_sample_data, related to the "I" to "t" change.

591. By Björn Tillenius

Fix test failures.

Revision history for this message
Björn Tillenius (bjornt) wrote :

On Wed, Oct 10, 2012 at 03:20:07PM -0000, Alberto Donato wrote:
> #1:
> I have a test failure in
> landscape.monitor.tests.test_activeprocessinfo.ActiveProcessInfoTest.test_read_sample_data,
> related to the "I" to "t" change.

Indeed. There were two other similar test failures as well. I've fixed
the tests to expect "t" now.

--
Björn Tillenius | https://launchpad.net/~bjornt

Revision history for this message
Jerry Seutter (jseutter) wrote :

+1, looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/lib/process.py'
2--- landscape/lib/process.py 2011-07-28 12:54:34 +0000
3+++ landscape/lib/process.py 2012-10-11 08:30:30 +0000
4@@ -10,15 +10,6 @@
5 from landscape.monitor.computeruptime import BootTimes, get_uptime
6
7
8-STATES = {"R (running)": "R",
9- "D (disk sleep)": "D",
10- "S (sleeping)": "S",
11- "T (stopped)": "T",
12- "T (tracing stop)": "I",
13- "X (dead)": "X",
14- "Z (zombie)": "Z"}
15-
16-
17 class ProcessInformation(object):
18 """
19 @param proc_dir: The directory to use for process information.
20@@ -66,8 +57,8 @@
21 file = open(os.path.join(process_dir, "cmdline"), "r")
22 try:
23 # cmdline is a \0 separated list of strings
24- # We take the first, and then strip off the path, leaving us with
25- # the basename.
26+ # We take the first, and then strip off the path, leaving
27+ # us with the basename.
28 cmd_line = file.readline()
29 cmd_line_name = os.path.basename(cmd_line.split("\0")[0])
30 finally:
31@@ -82,7 +73,12 @@
32 parts[1].strip())
33 elif parts[0] == "State":
34 state = parts[1].strip()
35- process_info["state"] = STATES[state]
36+ # In Lucid, capital T is used for both tracing stop
37+ # and stopped. Starting with Natty, lowercase t is
38+ # used for tracing stop.
39+ if state == "T (tracing stop)":
40+ state = state.lower()
41+ process_info["state"] = state[0]
42 elif parts[0] == "Uid":
43 value_parts = parts[1].split()
44 process_info["uid"] = int(value_parts[0])
45@@ -99,14 +95,14 @@
46 file = open(os.path.join(process_dir, "stat"), "r")
47 try:
48 # These variable names are lifted directly from proc(5)
49- # utime: The number of jiffies that this process has been scheduled in
50- # user mode.
51- # stime: The number of jiffies that this process has been scheduled in
52- # kernel mode.
53- # cutime: The number of jiffies that this process's waited-for children
54- # have been scheduled in user mode.
55- # cstime: The number of jiffies that this process's waited-for children
56- # have been scheduled in kernel mode.
57+ # utime: The number of jiffies that this process has been
58+ # scheduled in user mode.
59+ # stime: The number of jiffies that this process has been
60+ # scheduled in kernel mode.
61+ # cutime: The number of jiffies that this process's waited-for
62+ # children have been scheduled in user mode.
63+ # cstime: The number of jiffies that this process's waited-for
64+ # children have been scheduled in kernel mode.
65 parts = file.read().split()
66 start_time = int(parts[21])
67 utime = int(parts[13])
68@@ -117,9 +113,11 @@
69 process_info["percent-cpu"] = pcpu
70 delta = timedelta(0, start_time // self._jiffies_per_sec)
71 if self._boot_time is None:
72- logging.warning("Skipping process (PID %s) without boot time.")
73- return None
74- process_info["start-time"] = to_timestamp(self._boot_time + delta)
75+ logging.warning(
76+ "Skipping process (PID %s) without boot time.")
77+ return None
78+ process_info["start-time"] = to_timestamp(
79+ self._boot_time + delta)
80 finally:
81 file.close()
82
83
84=== modified file 'landscape/lib/tests/test_process.py'
85--- landscape/lib/tests/test_process.py 2011-07-05 05:09:11 +0000
86+++ landscape/lib/tests/test_process.py 2012-10-11 08:30:30 +0000
87@@ -1,12 +1,44 @@
88 import unittest
89+import os
90
91 from landscape.tests.helpers import LandscapeTest
92
93 from landscape.lib.process import calculate_pcpu, ProcessInformation
94+from landscape.lib.fs import create_file
95
96
97 class ProcessInfoTest(LandscapeTest):
98
99+ def setUp(self):
100+ super(ProcessInfoTest, self).setUp()
101+ self.proc_dir = self.makeDir()
102+
103+ def _add_process_info(self, process_id, state="R (running)"):
104+ """Add information about a process.
105+
106+ The cmdline, status and stat files will be created in the
107+ process directory, so that get_process_info can get the required
108+ information.
109+ """
110+ process_dir = os.path.join(self.proc_dir, str(process_id))
111+ os.mkdir(process_dir)
112+
113+ cmd_line = "/usr/bin/foo"
114+ create_file(os.path.join(process_dir, "cmdline"), cmd_line)
115+
116+ status = "\n".join([
117+ "Name: foo",
118+ "State: %s" % state,
119+ "Uid: 1000",
120+ "Gid: 2000",
121+ "VmSize: 3000",
122+ "Ignored: value"])
123+ create_file(os.path.join(process_dir, "status"), status)
124+
125+ stat_array = [str(index) for index in range(44)]
126+ stat = " ".join(stat_array)
127+ create_file(os.path.join(process_dir, "stat"), stat)
128+
129 def test_missing_process_race(self):
130 """
131 We use os.listdir("/proc") to get the list of active processes, if a
132@@ -52,6 +84,41 @@
133 self.assertTrue(fakefile1.closed)
134 self.assertTrue(fakefile2.closed)
135
136+ def test_get_process_info_state(self):
137+ """
138+ C{get_process_info} reads the process state from the status file
139+ and uses the first character to represent the process state.
140+ """
141+ self._add_process_info(12, state="A (some state)")
142+ process_info = ProcessInformation(self.proc_dir)
143+ info = process_info.get_process_info(12)
144+ self.assertEqual("A", info["state"])
145+
146+ def test_get_process_info_state_preserves_case(self):
147+ """
148+ C{get_process_info} retains the case of the process state, since
149+ for example both x and X can be different states.
150+ """
151+ self._add_process_info(12, state="a (some state)")
152+ process_info = ProcessInformation(self.proc_dir)
153+ info = process_info.get_process_info(12)
154+ self.assertEqual("a", info["state"])
155+
156+ def test_get_process_info_state_tracing_stop_lucid(self):
157+ """
158+ In Lucid, capital T was used for both stopped and tracing stop.
159+ From Natty and onwards lowercase t is used for tracing stop, so
160+ we special-case that state and always return lowercase t for
161+ tracing stop.
162+ """
163+ self._add_process_info(12, state="T (tracing stop)")
164+ self._add_process_info(13, state="t (tracing stop)")
165+ process_info = ProcessInformation(self.proc_dir)
166+ info1 = process_info.get_process_info(12)
167+ info2 = process_info.get_process_info(12)
168+ self.assertEqual("t", info1["state"])
169+ self.assertEqual("t", info2["state"])
170+
171
172 class CalculatePCPUTest(unittest.TestCase):
173
174
175=== modified file 'landscape/monitor/tests/test_activeprocessinfo.py'
176--- landscape/monitor/tests/test_activeprocessinfo.py 2011-07-05 05:09:11 +0000
177+++ landscape/monitor/tests/test_activeprocessinfo.py 2012-10-11 08:30:30 +0000
178@@ -122,7 +122,7 @@
179 expected_process_1 = {"state": "T", "gid": 1000, "pid": 671,
180 "vm-size": 11676, "name": "blargh", "uid": 1000,
181 "start-time": 111, "percent-cpu": 0.0}
182- expected_process_2 = {"state": "I", "gid": 1000, "pid": 672,
183+ expected_process_2 = {"state": "t", "gid": 1000, "pid": 672,
184 "vm-size": 11676, "name": "blarpy", "uid": 1000,
185 "start-time": 112, "percent-cpu": 0.0}
186 processes = message["add-processes"]
187@@ -213,7 +213,7 @@
188 "vm-size": 11676, "name": "blargh",
189 "uid": 1000, "start-time": 102,
190 "percent-cpu": 0.0}
191- expected_process_2 = {"state": "I", "gid": 1000, "pid": 672,
192+ expected_process_2 = {"state": "t", "gid": 1000, "pid": 672,
193 "vm-size": 11676, "name": "blarpy",
194 "uid": 1000, "start-time": 104,
195 "percent-cpu": 0.0}
196@@ -376,7 +376,7 @@
197 "name": u"blarpy",
198 "pid": 672,
199 "start-time": 112,
200- "state": "I",
201+ "state": "t",
202 "uid": 1000,
203 "vm-size": 11676,
204 "percent-cpu": 0.0},

Subscribers

People subscribed via source and target branches

to all changes: