test_ps.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Mike Place <mp@saltstack.com>
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, unicode_literals, print_function
  7. from collections import namedtuple
  8. import time
  9. # Import Salt Testing libs
  10. from tests.support.unit import TestCase, skipIf
  11. from tests.support.mock import MagicMock, patch, call, Mock
  12. # Import Salt libs
  13. import salt.utils.data
  14. import salt.modules.ps as ps
  15. HAS_PSUTIL_VERSION = False
  16. # Import 3rd-party libs
  17. # pylint: disable=import-error,unused-import
  18. from salt.ext.six.moves import range # pylint: disable=redefined-builtin
  19. import salt.utils.psutil_compat as psutil
  20. PSUTIL2 = psutil.version_info >= (2, 0)
  21. STUB_CPU_TIMES = namedtuple('cputimes', 'user nice system idle')(1, 2, 3, 4)
  22. STUB_VIRT_MEM = namedtuple('vmem', 'total available percent used free')(1000, 500, 50, 500, 500)
  23. STUB_SWAP_MEM = namedtuple('swap', 'total used free percent sin sout')(1000, 500, 500, 50, 0, 0)
  24. STUB_PHY_MEM_USAGE = namedtuple('usage', 'total used free percent')(1000, 500, 500, 50)
  25. STUB_DISK_PARTITION = namedtuple(
  26. 'partition',
  27. 'device mountpoint fstype, opts')(
  28. '/dev/disk0s2', '/', 'hfs', 'rw,local,rootfs,dovolfs,journaled,multilabel')
  29. STUB_DISK_USAGE = namedtuple('usage', 'total used free percent')(1000, 500, 500, 50)
  30. STUB_NETWORK_IO = namedtuple(
  31. 'iostat',
  32. 'bytes_sent, bytes_recv, packets_sent, packets_recv, errin errout dropin dropout')(
  33. 1000, 2000, 500, 600, 1, 2, 3, 4)
  34. STUB_DISK_IO = namedtuple(
  35. 'iostat',
  36. 'read_count, write_count, read_bytes, write_bytes, read_time, write_time')(
  37. 1000, 2000, 500, 600, 2000, 3000)
  38. STUB_USER = namedtuple('user', 'name, terminal, host, started')('bdobbs', 'ttys000', 'localhost', 0.0)
  39. if psutil.version_info >= (0, 6, 0):
  40. HAS_PSUTIL_VERSION = True
  41. STUB_PID_LIST = [0, 1, 2, 3]
  42. try:
  43. import utmp # pylint: disable=W0611
  44. HAS_UTMP = True
  45. except ImportError:
  46. HAS_UTMP = False
  47. # pylint: enable=import-error,unused-import
  48. def _get_proc_name(proc):
  49. return proc.name() if PSUTIL2 else proc.name
  50. def _get_proc_pid(proc):
  51. return proc.pid
  52. class DummyProcess(object):
  53. '''
  54. Dummy class to emulate psutil.Process. This ensures that _any_ string
  55. values used for any of the options passed in are converted to str types on
  56. both Python 2 and Python 3.
  57. '''
  58. def __init__(self, cmdline=None, create_time=None, name=None, status=None,
  59. username=None, pid=None):
  60. self._cmdline = salt.utils.data.decode(
  61. cmdline if cmdline is not None else [],
  62. to_str=True)
  63. self._create_time = salt.utils.data.decode(
  64. create_time if create_time is not None else time.time(),
  65. to_str=True)
  66. self._name = salt.utils.data.decode(
  67. name if name is not None else [],
  68. to_str=True)
  69. self._status = salt.utils.data.decode(status, to_str=True)
  70. self._username = salt.utils.data.decode(username, to_str=True)
  71. self._pid = salt.utils.data.decode(
  72. pid if pid is not None else 12345,
  73. to_str=True)
  74. def cmdline(self):
  75. return self._cmdline
  76. def create_time(self):
  77. return self._create_time
  78. def name(self):
  79. return self._name
  80. def status(self):
  81. return self._status
  82. def username(self):
  83. return self._username
  84. def pid(self):
  85. return self._pid
  86. class PsTestCase(TestCase):
  87. def setUp(self):
  88. self.mocked_proc = mocked_proc = MagicMock('salt.utils.psutil_compat.Process')
  89. if PSUTIL2:
  90. self.mocked_proc.name = Mock(return_value="test_mock_proc")
  91. self.mocked_proc.pid = Mock(return_value=9999999999)
  92. else:
  93. self.mocked_proc.name = 'test_mock_proc'
  94. self.mocked_proc.pid = 9999999999
  95. @skipIf(not ps.PSUTIL2, 'Only run for psutil 2.x')
  96. def test__get_proc_cmdline(self):
  97. cmdline = ['echo', 'питон']
  98. ret = ps._get_proc_cmdline(DummyProcess(cmdline=cmdline))
  99. assert ret == cmdline, ret
  100. def test_get_pid_list(self):
  101. with patch('salt.utils.psutil_compat.pids',
  102. MagicMock(return_value=STUB_PID_LIST)):
  103. self.assertListEqual(STUB_PID_LIST, ps.get_pid_list())
  104. def test_kill_pid(self):
  105. with patch('salt.utils.psutil_compat.Process') as send_signal_mock:
  106. ps.kill_pid(0, signal=999)
  107. self.assertEqual(send_signal_mock.call_args, call(0))
  108. def test_pkill(self):
  109. with patch('salt.utils.psutil_compat.Process.send_signal'), \
  110. patch('salt.utils.psutil_compat.process_iter',
  111. MagicMock(return_value=[self.mocked_proc])):
  112. self.mocked_proc.send_signal = MagicMock()
  113. test_signal = 1234
  114. ps.pkill(_get_proc_name(self.mocked_proc), signal=test_signal)
  115. self.assertEqual(self.mocked_proc.send_signal.call_args, call(test_signal))
  116. def test_pgrep(self):
  117. with patch('salt.utils.psutil_compat.process_iter',
  118. MagicMock(return_value=[self.mocked_proc])):
  119. self.assertIn(_get_proc_pid(self.mocked_proc), ps.pgrep(_get_proc_name(self.mocked_proc)))
  120. def test_cpu_percent(self):
  121. with patch('salt.utils.psutil_compat.cpu_percent',
  122. MagicMock(return_value=1)):
  123. self.assertEqual(ps.cpu_percent(), 1)
  124. def test_cpu_times(self):
  125. with patch('salt.utils.psutil_compat.cpu_times',
  126. MagicMock(return_value=STUB_CPU_TIMES)):
  127. self.assertDictEqual({'idle': 4, 'nice': 2, 'system': 3, 'user': 1}, ps.cpu_times())
  128. @skipIf(HAS_PSUTIL_VERSION is False, 'psutil 0.6.0 or greater is required for this test')
  129. def test_virtual_memory(self):
  130. with patch('salt.utils.psutil_compat.virtual_memory',
  131. MagicMock(return_value=STUB_VIRT_MEM)):
  132. self.assertDictEqual({'used': 500, 'total': 1000, 'available': 500, 'percent': 50, 'free': 500},
  133. ps.virtual_memory())
  134. @skipIf(HAS_PSUTIL_VERSION is False, 'psutil 0.6.0 or greater is required for this test')
  135. def test_swap_memory(self):
  136. with patch('salt.utils.psutil_compat.swap_memory',
  137. MagicMock(return_value=STUB_SWAP_MEM)):
  138. self.assertDictEqual({'used': 500, 'total': 1000, 'percent': 50, 'free': 500, 'sin': 0, 'sout': 0},
  139. ps.swap_memory())
  140. def test_disk_partitions(self):
  141. with patch('salt.utils.psutil_compat.disk_partitions',
  142. MagicMock(return_value=[STUB_DISK_PARTITION])):
  143. self.assertDictEqual(
  144. {'device': '/dev/disk0s2', 'mountpoint': '/', 'opts': 'rw,local,rootfs,dovolfs,journaled,multilabel',
  145. 'fstype': 'hfs'},
  146. ps.disk_partitions()[0])
  147. def test_disk_usage(self):
  148. with patch('salt.utils.psutil_compat.disk_usage',
  149. MagicMock(return_value=STUB_DISK_USAGE)):
  150. self.assertDictEqual({'used': 500, 'total': 1000, 'percent': 50, 'free': 500}, ps.disk_usage('DUMMY_PATH'))
  151. def test_disk_partition_usage(self):
  152. with patch('salt.utils.psutil_compat.disk_partitions',
  153. MagicMock(return_value=[STUB_DISK_PARTITION])):
  154. self.assertDictEqual(
  155. {'device': '/dev/disk0s2', 'mountpoint': '/', 'opts': 'rw,local,rootfs,dovolfs,journaled,multilabel',
  156. 'fstype': 'hfs'},
  157. ps.disk_partitions()[0])
  158. def test_network_io_counters(self):
  159. with patch('salt.utils.psutil_compat.net_io_counters',
  160. MagicMock(return_value=STUB_NETWORK_IO)):
  161. self.assertDictEqual(
  162. {'packets_sent': 500, 'packets_recv': 600, 'bytes_recv': 2000, 'dropout': 4, 'bytes_sent': 1000,
  163. 'errout': 2, 'errin': 1, 'dropin': 3}, ps.network_io_counters())
  164. def test_disk_io_counters(self):
  165. with patch('salt.utils.psutil_compat.disk_io_counters',
  166. MagicMock(return_value=STUB_DISK_IO)):
  167. self.assertDictEqual(
  168. {'read_time': 2000, 'write_bytes': 600, 'read_bytes': 500, 'write_time': 3000, 'read_count': 1000,
  169. 'write_count': 2000}, ps.disk_io_counters())
  170. def test_get_users(self):
  171. with patch('salt.utils.psutil_compat.users',
  172. MagicMock(return_value=[STUB_USER])):
  173. self.assertDictEqual({'terminal': 'ttys000', 'started': 0.0, 'host': 'localhost', 'name': 'bdobbs'},
  174. ps.get_users()[0])
  175. ## This is commented out pending discussion on https://github.com/saltstack/salt/commit/2e5c3162ef87cca8a2c7b12ade7c7e1b32028f0a
  176. # @skipIf(not HAS_UTMP, "The utmp module must be installed to run test_get_users_utmp()")
  177. # @patch('salt.utils.psutil_compat.get_users', new=MagicMock(return_value=None)) # This will force the function to use utmp
  178. # def test_get_users_utmp(self):
  179. # pass