test_win_service.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Rahul Handay <rahulha@saltstack.com>
  4. '''
  5. # Import Python Libs
  6. from __future__ import absolute_import, unicode_literals, print_function
  7. # Import Salt Testing Libs
  8. from tests.support.mixins import LoaderModuleMockMixin
  9. from tests.support.unit import TestCase, skipIf
  10. from tests.support.mock import (
  11. MagicMock,
  12. patch,
  13. )
  14. # Import Salt Libs
  15. import salt.modules.win_service as win_service
  16. import salt.utils.path
  17. # Import 3rd Party Libs
  18. try:
  19. WINAPI = True
  20. import win32serviceutil
  21. import pywintypes
  22. except ImportError:
  23. WINAPI = False
  24. class WinServiceTestCase(TestCase, LoaderModuleMockMixin):
  25. '''
  26. Test cases for salt.modules.win_service
  27. '''
  28. def setup_loader_modules(self):
  29. return {win_service: {}}
  30. def test_get_enabled(self):
  31. '''
  32. Test to return the enabled services
  33. '''
  34. mock = MagicMock(return_value=[{'ServiceName': 'spongebob'},
  35. {'ServiceName': 'squarepants'},
  36. {'ServiceName': 'patrick'}])
  37. with patch.object(win_service, '_get_services', mock):
  38. mock_info = MagicMock(side_effect=[{'StartType': 'Auto'},
  39. {'StartType': 'Manual'},
  40. {'StartType': 'Disabled'}])
  41. with patch.object(win_service, 'info', mock_info):
  42. self.assertListEqual(win_service.get_enabled(),
  43. ['spongebob'])
  44. def test_get_disabled(self):
  45. '''
  46. Test to return the disabled services
  47. '''
  48. mock = MagicMock(return_value=[{'ServiceName': 'spongebob'},
  49. {'ServiceName': 'squarepants'},
  50. {'ServiceName': 'patrick'}])
  51. with patch.object(win_service, '_get_services', mock):
  52. mock_info = MagicMock(side_effect=[{'StartType': 'Auto'},
  53. {'StartType': 'Manual'},
  54. {'StartType': 'Disabled'}])
  55. with patch.object(win_service, 'info', mock_info):
  56. self.assertListEqual(win_service.get_disabled(),
  57. ['patrick', 'squarepants'])
  58. def test_available(self):
  59. '''
  60. Test to Returns ``True`` if the specified service
  61. is available, otherwise returns ``False``
  62. '''
  63. mock = MagicMock(return_value=['c', 'a', 'b'])
  64. with patch.object(win_service, 'get_all', mock):
  65. self.assertTrue(win_service.available("a"))
  66. def test_missing(self):
  67. '''
  68. Test to the inverse of service.available
  69. '''
  70. mock = MagicMock(return_value=['c', 'a', 'b'])
  71. with patch.object(win_service, 'get_all', mock):
  72. self.assertTrue(win_service.missing("d"))
  73. def test_get_all(self):
  74. '''
  75. Test to return all installed services
  76. '''
  77. mock = MagicMock(return_value=[{'ServiceName': 'spongebob'},
  78. {'ServiceName': 'squarepants'},
  79. {'ServiceName': 'patrick'}])
  80. with patch.object(win_service, '_get_services', mock):
  81. self.assertListEqual(win_service.get_all(),
  82. ['patrick', 'spongebob', 'squarepants'])
  83. def test_get_service_name(self):
  84. '''
  85. Test to the Display Name is what is displayed
  86. in Windows when services.msc is executed.
  87. '''
  88. mock = MagicMock(return_value=[{'ServiceName': 'spongebob',
  89. 'DisplayName': 'Sponge Bob'},
  90. {'ServiceName': 'squarepants',
  91. 'DisplayName': 'Square Pants'},
  92. {'ServiceName': 'patrick',
  93. 'DisplayName': 'Patrick the Starfish'}])
  94. with patch.object(win_service, '_get_services', mock):
  95. self.assertDictEqual(win_service.get_service_name(),
  96. {'Patrick the Starfish': 'patrick',
  97. 'Sponge Bob': 'spongebob',
  98. 'Square Pants': 'squarepants'})
  99. self.assertDictEqual(win_service.get_service_name('patrick'),
  100. {'Patrick the Starfish': 'patrick'})
  101. @skipIf(not WINAPI, 'win32serviceutil not available')
  102. def test_start(self):
  103. '''
  104. Test to start the specified service
  105. '''
  106. mock_true = MagicMock(return_value=True)
  107. mock_false = MagicMock(return_value=False)
  108. mock_info = MagicMock(side_effect=[{'Status': 'Running'}])
  109. with patch.object(win32serviceutil, 'StartService', mock_true), \
  110. patch.object(win_service, 'disabled', mock_false), \
  111. patch.object(win_service, 'info', mock_info):
  112. self.assertTrue(win_service.start('spongebob'))
  113. mock_info = MagicMock(side_effect=[{'Status': 'Stopped', 'Status_WaitHint': 0},
  114. {'Status': 'Start Pending', 'Status_WaitHint': 0},
  115. {'Status': 'Running'}])
  116. with patch.object(win32serviceutil, 'StartService', mock_true), \
  117. patch.object(win_service, 'disabled', mock_false), \
  118. patch.object(win_service, 'info', mock_info), \
  119. patch.object(win_service, 'status', mock_true):
  120. self.assertTrue(win_service.start('spongebob'))
  121. @skipIf(not WINAPI, 'pywintypes not available')
  122. def test_start_already_running(self):
  123. '''
  124. Test starting a service that is already running
  125. '''
  126. mock_false = MagicMock(return_value=False)
  127. mock_error = MagicMock(
  128. side_effect=pywintypes.error(1056,
  129. 'StartService',
  130. 'Service is running'))
  131. mock_info = MagicMock(side_effect=[{'Status': 'Running'}])
  132. with patch.object(win32serviceutil, 'StartService', mock_error), \
  133. patch.object(win_service, 'disabled', mock_false), \
  134. patch.object(win_service, '_status_wait', mock_info):
  135. self.assertTrue(win_service.start('spongebob'))
  136. @skipIf(not WINAPI, 'win32serviceutil not available')
  137. def test_stop(self):
  138. '''
  139. Test to stop the specified service
  140. '''
  141. mock_true = MagicMock(return_value=True)
  142. mock_false = MagicMock(return_value=False)
  143. mock_info = MagicMock(side_effect=[{'Status': 'Stopped'}])
  144. with patch.object(win32serviceutil, 'StopService', mock_true), \
  145. patch.object(win_service, '_status_wait', mock_info):
  146. self.assertTrue(win_service.stop('spongebob'))
  147. mock_info = MagicMock(side_effect=[{'Status': 'Running', 'Status_WaitHint': 0},
  148. {'Status': 'Stop Pending', 'Status_WaitHint': 0},
  149. {'Status': 'Stopped'}])
  150. with patch.object(win32serviceutil, 'StopService', mock_true), \
  151. patch.object(win_service, 'info', mock_info), \
  152. patch.object(win_service, 'status', mock_false):
  153. self.assertTrue(win_service.stop('spongebob'))
  154. @skipIf(not WINAPI, 'pywintypes not available')
  155. def test_stop_not_running(self):
  156. '''
  157. Test stopping a service that is already stopped
  158. '''
  159. mock_error = MagicMock(
  160. side_effect=pywintypes.error(1062,
  161. 'StopService',
  162. 'Service is not running'))
  163. mock_info = MagicMock(side_effect=[{'Status': 'Stopped'}])
  164. with patch.object(win32serviceutil, 'StopService', mock_error), \
  165. patch.object(win_service, '_status_wait', mock_info):
  166. self.assertTrue(win_service.stop('spongebob'))
  167. def test_restart(self):
  168. '''
  169. Test to restart the named service
  170. '''
  171. mock_true = MagicMock(return_value=True)
  172. with patch.object(win_service, 'create_win_salt_restart_task',
  173. mock_true):
  174. with patch.object(win_service, 'execute_salt_restart_task',
  175. mock_true):
  176. self.assertTrue(win_service.restart("salt-minion"))
  177. with patch.object(win_service, 'stop', mock_true):
  178. with patch.object(win_service, 'start', mock_true):
  179. self.assertTrue(win_service.restart("salt"))
  180. def test_createwin_saltrestart_task(self):
  181. '''
  182. Test to create a task in Windows task
  183. scheduler to enable restarting the salt-minion
  184. '''
  185. cmd = salt.utils.path.which('cmd')
  186. mock = MagicMock()
  187. with patch.dict(win_service.__salt__, {'task.create_task': mock}):
  188. win_service.create_win_salt_restart_task()
  189. mock.assert_called_once_with(
  190. action_type='Execute',
  191. arguments='/c ping -n 3 127.0.0.1 && net stop salt-minion && '
  192. 'net start salt-minion',
  193. cmd=cmd,
  194. force=True,
  195. name='restart-salt-minion',
  196. start_date='1975-01-01',
  197. start_time='01:00',
  198. trigger_type='Once',
  199. user_name='System'
  200. )
  201. def test_execute_salt_restart_task(self):
  202. '''
  203. Test to run the Windows Salt restart task
  204. '''
  205. mock_true = MagicMock(return_value=True)
  206. with patch.dict(win_service.__salt__, {'task.run': mock_true}):
  207. self.assertTrue(win_service.execute_salt_restart_task())
  208. @skipIf(not WINAPI, 'win32serviceutil not available')
  209. def test_status(self):
  210. '''
  211. Test to return the status for a service
  212. '''
  213. mock_info = MagicMock(side_effect=[{'Status': 'Running'},
  214. {'Status': 'Stop Pending'},
  215. {'Status': 'Stopped'}])
  216. with patch.object(win_service, 'info', mock_info):
  217. self.assertTrue(win_service.status('spongebob'))
  218. self.assertTrue(win_service.status('patrick'))
  219. self.assertFalse(win_service.status('squidward'))
  220. def test_getsid(self):
  221. '''
  222. Test to return the sid for this windows service
  223. '''
  224. mock_info = MagicMock(side_effect=[{'sid': 'S-1-5-80-1956725871...'},
  225. {'sid': None}])
  226. with patch.object(win_service, 'info', mock_info):
  227. self.assertEqual(win_service.getsid('spongebob'),
  228. 'S-1-5-80-1956725871...')
  229. self.assertEqual(win_service.getsid('plankton'), None)
  230. def test_enable(self):
  231. '''
  232. Test to enable the named service to start at boot
  233. '''
  234. mock_modify = MagicMock(return_value=True)
  235. mock_info = MagicMock(return_value={'StartType': 'Auto',
  236. 'StartTypeDelayed': False})
  237. with patch.object(win_service, 'modify', mock_modify):
  238. with patch.object(win_service, 'info', mock_info):
  239. self.assertTrue(win_service.enable('spongebob'))
  240. def test_disable(self):
  241. '''
  242. Test to disable the named service to start at boot
  243. '''
  244. mock_modify = MagicMock(return_value=True)
  245. mock_info = MagicMock(return_value={'StartType': 'Disabled'})
  246. with patch.object(win_service, 'modify', mock_modify):
  247. with patch.object(win_service, 'info', mock_info):
  248. self.assertTrue(win_service.disable('spongebob'))
  249. def test_enabled(self):
  250. '''
  251. Test to check to see if the named
  252. service is enabled to start on boot
  253. '''
  254. mock = MagicMock(side_effect=[{'StartType': 'Auto'},
  255. {'StartType': 'Disabled'}])
  256. with patch.object(win_service, 'info', mock):
  257. self.assertTrue(win_service.enabled('spongebob'))
  258. self.assertFalse(win_service.enabled('squarepants'))
  259. def test_enabled_with_space_in_name(self):
  260. '''
  261. Test to check to see if the named
  262. service is enabled to start on boot
  263. when have space in service name
  264. '''
  265. mock = MagicMock(side_effect=[{'StartType': 'Auto'},
  266. {'StartType': 'Disabled'}])
  267. with patch.object(win_service, 'info', mock):
  268. self.assertTrue(win_service.enabled('spongebob test'))
  269. self.assertFalse(win_service.enabled('squarepants test'))
  270. def test_disabled(self):
  271. '''
  272. Test to check to see if the named
  273. service is disabled to start on boot
  274. '''
  275. mock = MagicMock(side_effect=[False, True])
  276. with patch.object(win_service, 'enabled', mock):
  277. self.assertTrue(win_service.disabled('spongebob'))
  278. self.assertFalse(win_service.disabled('squarepants'))
  279. def test_cmd_quote(self):
  280. '''
  281. Make sure the command gets quoted correctly
  282. '''
  283. # Should always return command wrapped in double quotes
  284. expected = r'"C:\Program Files\salt\test.exe"'
  285. # test no quotes
  286. bin_path = r'C:\Program Files\salt\test.exe'
  287. self.assertEqual(win_service._cmd_quote(bin_path), expected)
  288. # test single quotes
  289. bin_path = r"'C:\Program Files\salt\test.exe'"
  290. self.assertEqual(win_service._cmd_quote(bin_path), expected)
  291. # test double quoted single quotes
  292. bin_path = '"\'C:\\Program Files\\salt\\test.exe\'"'
  293. self.assertEqual(win_service._cmd_quote(bin_path), expected)
  294. # test single quoted, double quoted, single quotes
  295. bin_path = "\'\"\'C:\\Program Files\\salt\\test.exe\'\"\'"
  296. self.assertEqual(win_service._cmd_quote(bin_path), expected)