Merge pull request #23791 from optix2000/psutil_compat

Psutil compat
This commit is contained in:
Justin Findlay 2015-05-15 22:05:54 -06:00
commit 8ec4fb2a73
5 changed files with 133 additions and 33 deletions

View file

@ -48,7 +48,7 @@ import salt.ext.six as six
from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
HAS_PSUTIL = False
try:
import psutil
import salt.utils.psutil_compat as psutil
HAS_PSUTIL = True
except ImportError:
pass
@ -520,7 +520,7 @@ class SaltLoadModules(ioflo.base.deeding.Deed):
)
modules_max_memory = True
old_mem_limit = resource.getrlimit(resource.RLIMIT_AS)
rss, vms = psutil.Process(os.getpid()).get_memory_info()
rss, vms = psutil.Process(os.getpid()).memory_info()
mem_limit = rss + vms + self.opts.value['modules_max_memory']
resource.setrlimit(resource.RLIMIT_AS, (mem_limit, mem_limit))
elif self.opts.value.get('modules_max_memory', -1) > 0:

View file

@ -42,7 +42,7 @@ except ImportError:
HAS_PSUTIL = False
try:
import psutil
import salt.utils.psutil_compat as psutil
HAS_PSUTIL = True
except ImportError:
pass
@ -924,7 +924,7 @@ class Minion(MinionBase):
log.debug('modules_max_memory set, enforcing a maximum of {0}'.format(self.opts['modules_max_memory']))
modules_max_memory = True
old_mem_limit = resource.getrlimit(resource.RLIMIT_AS)
rss, vms = psutil.Process(os.getpid()).get_memory_info()
rss, vms = psutil.Process(os.getpid()).memory_info()
mem_limit = rss + vms + self.opts['modules_max_memory']
resource.setrlimit(resource.RLIMIT_AS, (mem_limit, mem_limit))
elif self.opts.get('modules_max_memory', -1) > 0:

View file

@ -17,7 +17,7 @@ from salt.exceptions import SaltInvocationError, CommandExecutionError
# Import third party libs
try:
import psutil
import salt.utils.psutil_compat as psutil
HAS_PSUTIL = True
PSUTIL2 = psutil.version_info >= (2, 0)
@ -126,10 +126,10 @@ def top(num_processes=5, interval=3):
'''
result = []
start_usage = {}
for pid in psutil.get_pid_list():
for pid in psutil.pids():
try:
process = psutil.Process(pid)
user, system = process.get_cpu_times()
user, system = process.cpu_times()
except psutil.NoSuchProcess:
continue
start_usage[process] = user + system
@ -137,7 +137,7 @@ def top(num_processes=5, interval=3):
usage = set()
for process, start in start_usage.items():
try:
user, system = process.get_cpu_times()
user, system = process.cpu_times()
except psutil.NoSuchProcess:
continue
now = user + system
@ -159,9 +159,9 @@ def top(num_processes=5, interval=3):
'cpu': {},
'mem': {},
}
for key, value in process.get_cpu_times()._asdict().items():
for key, value in process.cpu_times()._asdict().items():
info['cpu'][key] = value
for key, value in process.get_memory_info()._asdict().items():
for key, value in process.memory_info()._asdict().items():
info['mem'][key] = value
result.append(info)
@ -178,7 +178,7 @@ def get_pid_list():
salt '*' ps.get_pid_list
'''
return psutil.get_pid_list()
return psutil.pids()
def proc_info(pid, attrs=None):
@ -538,7 +538,7 @@ def boot_time(time_format=None):
except AttributeError:
# get_boot_time() has been removed in newer psutil versions, and has
# been replaced by boot_time() which provides the same information.
b_time = int(psutil.get_boot_time())
b_time = int(psutil.boot_time())
if time_format:
# Load epoch timestamp as a datetime.datetime object
b_time = datetime.datetime.fromtimestamp(b_time)
@ -562,9 +562,9 @@ def network_io_counters(interface=None):
salt '*' ps.network_io_counters interface=eth0
'''
if not interface:
return dict(psutil.network_io_counters()._asdict())
return dict(psutil.net_io_counters()._asdict())
else:
stats = psutil.network_io_counters(pernic=True)
stats = psutil.net_io_counters(pernic=True)
if interface in stats:
return dict(stats[interface]._asdict())
else:
@ -604,7 +604,7 @@ def get_users():
salt '*' ps.get_users
'''
try:
recs = psutil.get_users()
recs = psutil.users()
return [dict(x._asdict()) for x in recs]
except AttributeError:
# get_users is only present in psutil > v0.5.0

100
salt/utils/psutil_compat.py Normal file
View file

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
'''
Version agnostic psutil hack to fully support both old (<2.0) and new (>=2.0)
psutil versions.
The old <1.0 psutil API is dropped in psutil 3.0
Should be removed once support for psutil <2.0 is dropped. (eg RHEL 6)
Built off of http://grodola.blogspot.com/2014/01/psutil-20-porting.html
'''
from __future__ import absolute_import
# No exception handling, as we want ImportError if psutil doesn't exist
import psutil
if psutil.version_info >= (2, 0):
from psutil import * # pylint: disable=wildcard-import
else:
# Import hack to work around bugs in old psutil's
# Psuedo "from psutil import *"
_globals = globals()
for attr in psutil.__all__:
_temp = __import__('psutil', globals(), locals(), [attr], -1)
try:
_globals[attr] = getattr(_temp, attr)
except AttributeError:
pass
# Import functions not in __all__
from psutil import disk_partitions # pylint: disable=unused-import
from psutil import disk_usage # pylint: disable=unused-import
# Alias new module functions
def boot_time():
return psutil.BOOT_TIME
def cpu_count():
return psutil.NUM_CPUS
# Alias renamed module functions
pids = psutil.get_pid_list
users = psutil.get_users
# Deprecated in 1.0.1, but not mentioned in blog post
if psutil.version_info < (1, 0, 1):
net_io_counters = psutil.network_io_counters()
class Process(psutil.Process): # pylint: disable=no-init
# Reimplement overloaded getters/setters
def cpu_affinity(self, *args, **kwargs):
if args or kwargs:
return self.set_cpu_affinity(*args, **kwargs)
else:
return self.get_cpu_affinity()
def ionice(self, *args, **kwargs):
if args or kwargs:
return self.set_ionice(*args, **kwargs)
else:
return self.get_ionice()
def nice(self, *args, **kwargs):
if args or kwargs:
return self.set_nice(*args, **kwargs)
else:
return self.get_nice()
def rlimit(self, *args, **kwargs):
if args or kwargs:
return self.set_rlimit(*args, **kwargs)
else:
return self.get_rlimit()
# Alias renamed Process functions
_PROCESS_FUNCTION_MAP = {
"children": "get_children",
"connections": "get_connections",
"cpu_percent": "get_cpu_percent",
"cpu_times": "get_cpu_times",
"io_counters": "get_io_counters",
"memory_info": "get_memory_info",
"memory_info_ex": "get_ext_memory_info",
"memory_maps": "get_memory_maps",
"memory_percent": "get_memory_percent",
"num_ctx_switches": "get_num_ctx_switches",
"num_fds": "get_num_fds",
"num_threads": "get_num_threads",
"open_files": "get_open_files",
"threads": "get_threads",
"cwd": "getcwd",
}
for new, old in _PROCESS_FUNCTION_MAP.iteritems():
try:
setattr(Process, new, psutil.Process.__dict__[old])
except KeyError:
pass

View file

@ -16,7 +16,7 @@ HAS_PSUTIL = ps.__virtual__()
HAS_PSUTIL_VERSION = False
if HAS_PSUTIL:
import psutil
import salt.utils.psutil_compat as psutil
from collections import namedtuple
PSUTIL2 = psutil.version_info >= (2, 0)
@ -51,7 +51,7 @@ else:
STUB_USER) = [None for val in range(9)]
STUB_PID_LIST = [0, 1, 2, 3]
MOCK_PROC = mocked_proc = MagicMock('psutil.Process')
MOCK_PROC = mocked_proc = MagicMock('salt.utils.psutil_compat.Process')
try:
import utmp # pylint: disable=W0611
@ -79,59 +79,59 @@ class PsTestCase(TestCase):
MOCK_PROC.name = 'test_mock_proc'
MOCK_PROC.pid = 9999999999
@patch('psutil.get_pid_list', new=MagicMock(return_value=STUB_PID_LIST))
@patch('salt.utils.psutil_compat.pids', new=MagicMock(return_value=STUB_PID_LIST))
def test_get_pid_list(self):
self.assertListEqual(STUB_PID_LIST, ps.get_pid_list())
@patch('psutil.Process')
@patch('salt.utils.psutil_compat.Process')
def test_kill_pid(self, send_signal_mock):
ps.kill_pid(0, signal=999)
self.assertEqual(send_signal_mock.call_args, call(0))
@patch('psutil.Process.send_signal')
@patch('psutil.process_iter', new=MagicMock(return_value=[MOCK_PROC]))
@patch('salt.utils.psutil_compat.Process.send_signal')
@patch('salt.utils.psutil_compat.process_iter', new=MagicMock(return_value=[MOCK_PROC]))
def test_pkill(self, send_signal_mock):
mocked_proc.send_signal = MagicMock()
test_signal = 1234
ps.pkill(_get_proc_name(mocked_proc), signal=test_signal)
self.assertEqual(mocked_proc.send_signal.call_args, call(test_signal))
@patch('psutil.process_iter', new=MagicMock(return_value=[MOCK_PROC]))
@patch('salt.utils.psutil_compat.process_iter', new=MagicMock(return_value=[MOCK_PROC]))
def test_pgrep(self):
self.assertIn(_get_proc_pid(MOCK_PROC), ps.pgrep(_get_proc_name(MOCK_PROC)))
@patch('psutil.cpu_percent', new=MagicMock(return_value=1))
@patch('salt.utils.psutil_compat.cpu_percent', new=MagicMock(return_value=1))
def test_cpu_percent(self):
self.assertEqual(ps.cpu_percent(), 1)
@patch('psutil.cpu_times', new=MagicMock(return_value=STUB_CPU_TIMES))
@patch('salt.utils.psutil_compat.cpu_times', new=MagicMock(return_value=STUB_CPU_TIMES))
def test_cpu_times(self):
self.assertDictEqual({'idle': 4, 'nice': 2, 'system': 3, 'user': 1}, ps.cpu_times())
@skipIf(HAS_PSUTIL_VERSION is False, 'psutil 0.6.0 or greater is required for this test')
@patch('psutil.virtual_memory', new=MagicMock(return_value=STUB_VIRT_MEM))
@patch('salt.utils.psutil_compat.virtual_memory', new=MagicMock(return_value=STUB_VIRT_MEM))
def test_virtual_memory(self):
self.assertDictEqual({'used': 500, 'total': 1000, 'available': 500, 'percent': 50, 'free': 500},
ps.virtual_memory())
@skipIf(HAS_PSUTIL_VERSION is False, 'psutil 0.6.0 or greater is required for this test')
@patch('psutil.swap_memory', new=MagicMock(return_value=STUB_SWAP_MEM))
@patch('salt.utils.psutil_compat.swap_memory', new=MagicMock(return_value=STUB_SWAP_MEM))
def test_swap_memory(self):
self.assertDictEqual({'used': 500, 'total': 1000, 'percent': 50, 'free': 500, 'sin': 0, 'sout': 0},
ps.swap_memory())
@patch('psutil.disk_partitions', new=MagicMock(return_value=[STUB_DISK_PARTITION]))
@patch('salt.utils.psutil_compat.disk_partitions', new=MagicMock(return_value=[STUB_DISK_PARTITION]))
def test_disk_partitions(self):
self.assertDictEqual(
{'device': '/dev/disk0s2', 'mountpoint': '/', 'opts': 'rw,local,rootfs,dovolfs,journaled,multilabel',
'fstype': 'hfs'},
ps.disk_partitions()[0])
@patch('psutil.disk_usage', new=MagicMock(return_value=STUB_DISK_USAGE))
@patch('salt.utils.psutil_compat.disk_usage', new=MagicMock(return_value=STUB_DISK_USAGE))
def test_disk_usage(self):
self.assertDictEqual({'used': 500, 'total': 1000, 'percent': 50, 'free': 500}, ps.disk_usage('DUMMY_PATH'))
@patch('psutil.disk_partitions', new=MagicMock(return_value=[STUB_DISK_PARTITION]))
@patch('salt.utils.psutil_compat.disk_partitions', new=MagicMock(return_value=[STUB_DISK_PARTITION]))
def test_disk_partition_usage(self):
self.assertDictEqual(
{'device': '/dev/disk0s2', 'mountpoint': '/', 'opts': 'rw,local,rootfs,dovolfs,journaled,multilabel',
@ -149,26 +149,26 @@ class PsTestCase(TestCase):
## Should only be tested in integration
# def test_boot_time(self):
# pass
@patch('psutil.network_io_counters', new=MagicMock(return_value=STUB_NETWORK_IO))
@patch('salt.utils.psutil_compat.net_io_counters', new=MagicMock(return_value=STUB_NETWORK_IO))
def test_network_io_counters(self):
self.assertDictEqual(
{'packets_sent': 500, 'packets_recv': 600, 'bytes_recv': 2000, 'dropout': 4, 'bytes_sent': 1000,
'errout': 2, 'errin': 1, 'dropin': 3}, ps.network_io_counters())
@patch('psutil.disk_io_counters', new=MagicMock(return_value=STUB_DISK_IO))
@patch('salt.utils.psutil_compat.disk_io_counters', new=MagicMock(return_value=STUB_DISK_IO))
def test_disk_io_counters(self):
self.assertDictEqual(
{'read_time': 2000, 'write_bytes': 600, 'read_bytes': 500, 'write_time': 3000, 'read_count': 1000,
'write_count': 2000}, ps.disk_io_counters())
@patch('psutil.get_users', new=MagicMock(return_value=[STUB_USER]))
@patch('salt.utils.psutil_compat.users', new=MagicMock(return_value=[STUB_USER]))
def test_get_users(self):
self.assertDictEqual({'terminal': 'ttys000', 'started': 0.0, 'host': 'localhost', 'name': 'bdobbs'},
ps.get_users()[0])
## This is commented out pending discussion on https://github.com/saltstack/salt/commit/2e5c3162ef87cca8a2c7b12ade7c7e1b32028f0a
# @skipIf(not HAS_UTMP, "The utmp module must be installed to run test_get_users_utmp()")
# @patch('psutil.get_users', new=MagicMock(return_value=None)) # This will force the function to use utmp
# @patch('salt.utils.psutil_compat.get_users', new=MagicMock(return_value=None)) # This will force the function to use utmp
# def test_get_users_utmp(self):
# pass