test_skip.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. # -*- coding: utf-8 -*-
  2. # Import Python libs
  3. from __future__ import absolute_import
  4. import copy
  5. import logging
  6. import os
  7. import dateutil.parser as dateutil_parser
  8. # Import Salt Testing libs
  9. from tests.support.case import ModuleCase
  10. from tests.support.mixins import SaltReturnAssertsMixin
  11. # Import Salt Testing Libs
  12. from tests.support.mock import MagicMock, patch
  13. import tests.integration as integration
  14. # Import Salt libs
  15. import salt.utils.schedule
  16. from salt.modules.test import ping as ping
  17. log = logging.getLogger(__name__)
  18. ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests')
  19. SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks')
  20. DEFAULT_CONFIG = salt.config.minion_config(None)
  21. DEFAULT_CONFIG['conf_dir'] = ROOT_DIR
  22. DEFAULT_CONFIG['root_dir'] = ROOT_DIR
  23. DEFAULT_CONFIG['sock_dir'] = SOCK_DIR
  24. DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki')
  25. DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache')
  26. class SchedulerSkipTest(ModuleCase, SaltReturnAssertsMixin):
  27. '''
  28. Validate the pkg module
  29. '''
  30. def setUp(self):
  31. with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)):
  32. functions = {'test.ping': ping}
  33. self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={})
  34. self.schedule.opts['loop_interval'] = 1
  35. def tearDown(self):
  36. self.schedule.reset()
  37. def test_skip(self):
  38. '''
  39. verify that scheduled job is skipped at the specified time
  40. '''
  41. job_name = 'test_skip'
  42. job = {
  43. 'schedule': {
  44. job_name: {
  45. 'function': 'test.ping',
  46. 'when': ['11/29/2017 4pm', '11/29/2017 5pm'],
  47. }
  48. }
  49. }
  50. # Add job to schedule
  51. self.schedule.opts.update(job)
  52. run_time = dateutil_parser.parse('11/29/2017 4:00pm')
  53. self.schedule.skip_job(job_name, {'time': run_time.strftime('%Y-%m-%dT%H:%M:%S'),
  54. 'time_fmt': '%Y-%m-%dT%H:%M:%S'})
  55. # Run 11/29/2017 at 4pm
  56. self.schedule.eval(now=run_time)
  57. ret = self.schedule.job_status(job_name)
  58. self.assertNotIn('_last_run', ret)
  59. self.assertEqual(ret['_skip_reason'], 'skip_explicit')
  60. self.assertEqual(ret['_skipped_time'], run_time)
  61. # Run 11/29/2017 at 5pm
  62. run_time = dateutil_parser.parse('11/29/2017 5:00pm')
  63. self.schedule.eval(now=run_time)
  64. ret = self.schedule.job_status(job_name)
  65. self.assertEqual(ret['_last_run'], run_time)
  66. def test_skip_during_range(self):
  67. '''
  68. verify that scheduled job is skipped during the specified range
  69. '''
  70. job_name = 'test_skip_during_range'
  71. job = {
  72. 'schedule': {
  73. job_name: {
  74. 'function': 'test.ping',
  75. 'hours': '1',
  76. 'skip_during_range': {
  77. 'start': '11/29/2017 2pm',
  78. 'end': '11/29/2017 3pm'
  79. },
  80. }
  81. }
  82. }
  83. # Add job to schedule
  84. self.schedule.opts.update(job)
  85. # eval at 1:30pm to prime.
  86. run_time = dateutil_parser.parse('11/29/2017 1:30pm')
  87. self.schedule.eval(now=run_time)
  88. ret = self.schedule.job_status(job_name)
  89. # eval at 2:30pm, will not run during range.
  90. run_time = dateutil_parser.parse('11/29/2017 2:30pm')
  91. self.schedule.eval(now=run_time)
  92. ret = self.schedule.job_status(job_name)
  93. self.assertNotIn('_last_run', ret)
  94. self.assertEqual(ret['_skip_reason'], 'in_skip_range')
  95. self.assertEqual(ret['_skipped_time'], run_time)
  96. # eval at 3:30pm, will run.
  97. run_time = dateutil_parser.parse('11/29/2017 3:30pm')
  98. self.schedule.eval(now=run_time)
  99. ret = self.schedule.job_status(job_name)
  100. self.assertEqual(ret['_last_run'], run_time)
  101. def test_skip_during_range_invalid_datestring(self):
  102. '''
  103. verify that scheduled job is not not and returns the right error string
  104. '''
  105. run_time = dateutil_parser.parse('11/29/2017 2:30pm')
  106. job_name1 = 'skip_during_range_invalid_datestring1'
  107. job1 = {
  108. 'schedule': {
  109. job_name1: {
  110. 'function': 'test.ping',
  111. 'hours': '1',
  112. '_next_fire_time': run_time,
  113. 'skip_during_range': {
  114. 'start': '25pm',
  115. 'end': '3pm'
  116. },
  117. }
  118. }
  119. }
  120. job_name2 = 'skip_during_range_invalid_datestring2'
  121. job2 = {
  122. 'schedule': {
  123. job_name2: {
  124. 'function': 'test.ping',
  125. 'hours': '1',
  126. '_next_fire_time': run_time,
  127. 'skip_during_range': {
  128. 'start': '2pm',
  129. 'end': '25pm'
  130. },
  131. }
  132. }
  133. }
  134. # Add job1 to schedule
  135. self.schedule.opts.update(job1)
  136. # Eval
  137. self.schedule.eval(now=run_time)
  138. # Check the first job
  139. ret = self.schedule.job_status(job_name1)
  140. _expected = ('Invalid date string for start in '
  141. 'skip_during_range. Ignoring '
  142. 'job {0}.').format(job_name1)
  143. self.assertEqual(ret['_error'], _expected)
  144. # Clear out schedule
  145. self.schedule.opts['schedule'] = {}
  146. # Add job2 to schedule
  147. self.schedule.opts.update(job2)
  148. # Eval
  149. self.schedule.eval(now=run_time)
  150. # Check the second job
  151. ret = self.schedule.job_status(job_name2)
  152. _expected = ('Invalid date string for end in '
  153. 'skip_during_range. Ignoring '
  154. 'job {0}.').format(job_name2)
  155. self.assertEqual(ret['_error'], _expected)
  156. def test_skip_during_range_global(self):
  157. '''
  158. verify that scheduled job is skipped during the specified range
  159. '''
  160. job_name = 'skip_during_range_global'
  161. job = {
  162. 'schedule': {
  163. 'skip_during_range': {
  164. 'start': '11/29/2017 2:00pm',
  165. 'end': '11/29/2017 3:00pm'
  166. },
  167. job_name: {
  168. 'function': 'test.ping',
  169. 'hours': '1',
  170. }
  171. }
  172. }
  173. # Add job to schedule
  174. self.schedule.opts.update(job)
  175. # eval at 1:30pm to prime.
  176. run_time = dateutil_parser.parse('11/29/2017 1:30pm')
  177. self.schedule.eval(now=run_time)
  178. ret = self.schedule.job_status(job_name)
  179. # eval at 2:30pm, will not run during range.
  180. run_time = dateutil_parser.parse('11/29/2017 2:30pm')
  181. self.schedule.eval(now=run_time)
  182. ret = self.schedule.job_status(job_name)
  183. self.assertNotIn('_last_run', ret)
  184. self.assertEqual(ret['_skip_reason'], 'in_skip_range')
  185. self.assertEqual(ret['_skipped_time'], run_time)
  186. # eval at 3:30pm, will run.
  187. run_time = dateutil_parser.parse('11/29/2017 3:30pm')
  188. self.schedule.eval(now=run_time)
  189. ret = self.schedule.job_status(job_name)
  190. self.assertEqual(ret['_last_run'], run_time)
  191. def test_run_after_skip_range(self):
  192. '''
  193. verify that scheduled job is skipped during the specified range
  194. '''
  195. job_name = 'skip_run_after_skip_range'
  196. job = {
  197. 'schedule': {
  198. job_name: {
  199. 'function': 'test.ping',
  200. 'when': '11/29/2017 2:30pm',
  201. 'run_after_skip_range': True,
  202. 'skip_during_range': {
  203. 'start': '11/29/2017 2pm',
  204. 'end': '11/29/2017 3pm'
  205. },
  206. }
  207. }
  208. }
  209. # Add job to schedule
  210. self.schedule.opts.update(job)
  211. # eval at 2:30pm, will not run during range.
  212. run_time = dateutil_parser.parse('11/29/2017 2:30pm')
  213. self.schedule.eval(now=run_time)
  214. ret = self.schedule.job_status(job_name)
  215. self.assertNotIn('_last_run', ret)
  216. self.assertEqual(ret['_skip_reason'], 'in_skip_range')
  217. self.assertEqual(ret['_skipped_time'], run_time)
  218. # eval at 3:00:01pm, will run.
  219. run_time = dateutil_parser.parse('11/29/2017 3:00:01pm')
  220. self.schedule.eval(now=run_time)
  221. ret = self.schedule.job_status(job_name)
  222. self.assertEqual(ret['_last_run'], run_time)
  223. def test_run_seconds_skip(self):
  224. '''
  225. verify that scheduled job is skipped during the specified range
  226. '''
  227. job_name = 'run_seconds_skip'
  228. job = {
  229. 'schedule': {
  230. job_name: {
  231. 'function': 'test.ping',
  232. 'seconds': '10',
  233. }
  234. }
  235. }
  236. # Add job to schedule
  237. self.schedule.opts.update(job)
  238. # eval at 2:00pm, to prime the scheduler
  239. run_time = dateutil_parser.parse('11/29/2017 2:00pm')
  240. self.schedule.eval(now=run_time)
  241. ret = self.schedule.job_status(job_name)
  242. # eval at 2:00:10pm
  243. run_time = dateutil_parser.parse('11/29/2017 2:00:10pm')
  244. self.schedule.eval(now=run_time)
  245. ret = self.schedule.job_status(job_name)
  246. # Skip at 2:00:20pm
  247. run_time = dateutil_parser.parse('11/29/2017 2:00:20pm')
  248. self.schedule.skip_job(job_name, {'time': run_time.strftime('%Y-%m-%dT%H:%M:%S'),
  249. 'time_fmt': '%Y-%m-%dT%H:%M:%S'})
  250. self.schedule.eval(now=run_time)
  251. ret = self.schedule.job_status(job_name)
  252. self.assertIn('_next_fire_time', ret)
  253. self.assertEqual(ret['_skip_reason'], 'skip_explicit')
  254. self.assertEqual(ret['_skipped_time'], run_time)
  255. # Run at 2:00:30pm
  256. run_time = dateutil_parser.parse('11/29/2017 2:00:30pm')
  257. self.schedule.eval(now=run_time)
  258. ret = self.schedule.job_status(job_name)
  259. self.assertIn('_last_run', ret)