]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Remove psutil dependency
authorTerry Wilson <twilson@redhat.com>
Fri, 24 Jan 2014 19:34:15 +0000 (13:34 -0600)
committerThomas Goirand <thomas@goirand.fr>
Thu, 13 Mar 2014 07:20:18 +0000 (15:20 +0800)
The version of psutil that was being required is not hosted on
PyPi which caused some issues. This patch removes the psutil
dependency in favor of using the method that was proposed for
the havana backport of polling minimization.

Closes-bug: #1268711
Change-Id: I5a1672cfd195099d92578321153c42b8bfd09b7d

neutron/agent/linux/async_process.py
neutron/agent/linux/utils.py
neutron/tests/unit/agent/linux/test_async_process.py
neutron/tests/unit/test_agent_linux_utils.py
requirements.txt

index 25b311dfbd2da6fce4ffe32d5025ccecae4f4d35..8a5d92856257bd040c0650903c227e75bb70c614 100644 (file)
@@ -18,7 +18,6 @@ import eventlet
 import eventlet.event
 import eventlet.queue
 import eventlet.timeout
-import psutil
 
 from neutron.agent.linux import utils
 from neutron.openstack.common import log as logging
@@ -141,12 +140,18 @@ class AsyncProcess(object):
         # die is to target the child process directly.
         if self.root_helper:
             try:
-                # This assumes that there are not multiple children in any
-                # level of the process tree under the parent process.
-                pid = psutil.Process(
-                    self._process.pid).get_children(recursive=True)[-1].pid
-            except (psutil.NoSuchProcess, IndexError):
-                pid = None
+                pid = utils.find_child_pids(pid)[0]
+            except IndexError:
+                # Process is already dead
+                return None
+            while True:
+                try:
+                    # We shouldn't have more than one child per process
+                    # so keep getting the children of the first one
+                    pid = utils.find_child_pids(pid)[0]
+                except IndexError:
+                    # Last process in the tree, return it
+                    break
         return pid
 
     def _kill_process(self, pid):
index d6ab603bbe943f44b3239c5abc377b7011e1972e..fdb8d0d4d9a71b838a794fa59b20a9d2411a4208 100644 (file)
@@ -108,3 +108,18 @@ def replace_file(file_name, data):
     tmp_file.close()
     os.chmod(tmp_file.name, 0o644)
     os.rename(tmp_file.name, file_name)
+
+
+def find_child_pids(pid):
+    """Retrieve a list of the pids of child processes of the given pid."""
+
+    try:
+        raw_pids = execute(['ps', '--ppid', pid, '-o', 'pid='])
+    except RuntimeError as e:
+        # Exception has already been logged by execute
+        no_children_found = 'Exit code: 1' in str(e)
+        if no_children_found:
+            return []
+        # Unexpected errors are the responsibility of the caller
+        raise
+    return [x.strip() for x in raw_pids.split('\n') if x.strip()]
index 0ea63655a86496054e46707631f9944688057b9a..d56ad38b9ef3696faca683aa906083a40cf35478 100644 (file)
@@ -14,8 +14,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import collections
-
 import eventlet.event
 import eventlet.queue
 import eventlet.timeout
@@ -187,19 +185,20 @@ class TestAsyncProcess(base.BaseTestCase):
 
     def _test__get_pid_to_kill(self, expected=_marker,
                                root_helper=None, pids=None):
+        def _find_child_pids(x):
+            if not pids:
+                return []
+            pids.pop(0)
+            return pids
+
         if root_helper:
             self.proc.root_helper = root_helper
 
-        xpid = collections.namedtuple('xpid', ['pid'])
-        xpids = [xpid(pid) for pid in pids or []]
-
         with mock.patch.object(self.proc, '_process') as mock_process:
             with mock.patch.object(mock_process, 'pid') as mock_pid:
-                with mock.patch('psutil.Process') as mock_ps_process:
-                    instance = mock_ps_process.return_value
-                    instance.get_children.return_value = xpids
+                with mock.patch.object(utils, 'find_child_pids',
+                                       side_effect=_find_child_pids):
                     actual = self.proc._get_pid_to_kill()
-
         if expected is _marker:
             expected = mock_pid
         self.assertEqual(expected, actual)
@@ -208,7 +207,8 @@ class TestAsyncProcess(base.BaseTestCase):
         self._test__get_pid_to_kill()
 
     def test__get_pid_to_kill_returns_child_pid_with_root_helper(self):
-        self._test__get_pid_to_kill(expected='1', pids=['1'], root_helper='a')
+        self._test__get_pid_to_kill(expected='2', pids=['1', '2'],
+                                    root_helper='a')
 
     def test__get_pid_to_kill_returns_last_child_pid_with_root_Helper(self):
         self._test__get_pid_to_kill(expected='3', pids=['1', '2', '3'],
index cccbf2024f622f4e0872dea9061ecb91a10a7490..6b7fbbfd00d9263984d9541408fae869f1a64130 100644 (file)
@@ -17,6 +17,7 @@
 
 import fixtures
 import mock
+import testtools
 
 from neutron.agent.linux import utils
 from neutron.tests import base
@@ -106,3 +107,25 @@ class AgentUtilsReplaceFile(base.BaseTestCase):
                     ntf.assert_has_calls(expected)
                     chmod.assert_called_once_with('/baz', 0o644)
                     rename.assert_called_once_with('/baz', '/foo')
+
+
+class TestFindChildPids(base.BaseTestCase):
+
+    def test_returns_empty_list_for_exit_code_1(self):
+        with mock.patch.object(utils, 'execute',
+                               side_effect=RuntimeError('Exit code: 1')):
+            self.assertEqual(utils.find_child_pids(-1), [])
+
+    def test_returns_empty_list_for_no_output(self):
+        with mock.patch.object(utils, 'execute', return_value=''):
+            self.assertEqual(utils.find_child_pids(-1), [])
+
+    def test_returns_list_of_child_process_ids_for_good_ouput(self):
+        with mock.patch.object(utils, 'execute', return_value=' 123 \n 185\n'):
+            self.assertEqual(utils.find_child_pids(-1), ['123', '185'])
+
+    def test_raises_unknown_exception(self):
+        with testtools.ExpectedException(RuntimeError):
+            with mock.patch.object(utils, 'execute',
+                                   side_effect=RuntimeError()):
+                utils.find_child_pids(-1)
index d1c0e6c1cf74b48c88a5347aba05548b6c38f20f..74e7f1586b7ebd9c8797657f5cbacf673c0f074a 100644 (file)
@@ -16,7 +16,6 @@ jsonrpclib
 Jinja2
 kombu>=2.4.8
 netaddr>=0.7.6
-psutil>=0.6.1,<1.0
 python-neutronclient>=2.3.0,<3
 SQLAlchemy>=0.7.8,<=0.7.99
 WebOb>=1.2.3,<1.3