pbs_resv_end_hook.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. # coding: utf-8
  2. # Copyright (C) 1994-2018 Altair Engineering, Inc.
  3. # For more information, contact Altair at www.altair.com.
  4. #
  5. # This file is part of the PBS Professional ("PBS Pro") software.
  6. #
  7. # Open Source License Information:
  8. #
  9. # PBS Pro is free software. You can redistribute it and/or modify it under the
  10. # terms of the GNU Affero General Public License as published by the Free
  11. # Software Foundation, either version 3 of the License, or (at your option) any
  12. # later version.
  13. #
  14. # PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
  15. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. # FOR A PARTICULAR PURPOSE.
  17. # See the GNU Affero General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU Affero General Public License
  20. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. #
  22. # Commercial License Information:
  23. #
  24. # For a copy of the commercial license terms and conditions,
  25. # go to: (http://www.pbspro.com/UserArea/agreement.html)
  26. # or contact the Altair Legal Department.
  27. #
  28. # Altair’s dual-license business model allows companies, individuals, and
  29. # organizations to create proprietary derivative works of PBS Pro and
  30. # distribute them - whether embedded or bundled with other software -
  31. # under a commercial license agreement.
  32. #
  33. # Use of Altair’s trademarks, including but not limited to "PBS™",
  34. # "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
  35. # trademark licensing policies.
  36. from tests.functional import *
  37. class TestResvEndHook(TestFunctional):
  38. """
  39. Tests to verify the reservation end hook for a confirm standing/advance/
  40. degraded reservation once the reservation ends or gets deleted.
  41. """
  42. advance_resv_hook_script = """
  43. import pbs
  44. e=pbs.event()
  45. pbs.logmsg(pbs.LOG_DEBUG, 'Reservation End Hook name - %s' % e.hook_name)
  46. if e.type == pbs.RESV_END:
  47. pbs.logmsg(pbs.LOG_DEBUG, 'Reservation ID - %s' % e.resv.resvid)
  48. """
  49. standing_resv_hook_script = """
  50. import pbs
  51. e=pbs.event()
  52. pbs.logmsg(pbs.LOG_DEBUG, 'Reservation End Hook name - %s' % e.hook_name)
  53. if e.type == pbs.RESV_END:
  54. pbs.logmsg(pbs.LOG_DEBUG, 'Reservation occurrence - %s' %
  55. e.resv.reserve_index)
  56. """
  57. def setUp(self):
  58. """
  59. Create a reservation end hook and set the server log level.
  60. """
  61. super(TestResvEndHook, self).setUp()
  62. self.hook_name = 'resvend_hook'
  63. attrs = {'event': 'resv_end'}
  64. self.server.create_hook(self.hook_name, attrs)
  65. self.server.manager(MGR_CMD_SET, SERVER, {'log_events': 2047},
  66. expect=True)
  67. def submit_resv(self, offset, duration, select='1:ncpus=1', rrule=''):
  68. """
  69. Helper function to submit an advance/a standing reservation.
  70. """
  71. start_time = int(time.time()) + offset
  72. end_time = start_time + duration
  73. attrs = {
  74. 'reserve_start': start_time,
  75. 'reserve_end': end_time,
  76. 'Resource_List.select': select
  77. }
  78. if rrule:
  79. if 'PBS_TZID' in self.conf:
  80. tzone = self.conf['PBS_TZID']
  81. elif 'PBS_TZID' in os.environ:
  82. tzone = os.environ['PBS_TZID']
  83. else:
  84. self.logger.info('Missing timezone, using Asia/Kolkata')
  85. tzone = 'Asia/Kolkata'
  86. attrs[ATTR_resv_rrule] = rrule
  87. attrs[ATTR_resv_timezone] = tzone
  88. rid = self.server.submit(Reservation(TEST_USER, attrs))
  89. return rid
  90. def test_delete_advance_resv(self):
  91. """
  92. Testcase to submit and confirm advance reservation, delete the same
  93. and verify the resvend hook.
  94. """
  95. self.server.import_hook(self.hook_name,
  96. TestResvEndHook.advance_resv_hook_script)
  97. offset = 10
  98. duration = 30
  99. rid = self.submit_resv(offset, duration)
  100. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  101. self.server.expect(RESV, attrs, id=rid)
  102. self.server.delete(rid)
  103. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  104. (self.server.shortname, rid)
  105. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  106. def test_delete_degraded_resv(self):
  107. """
  108. Testcase to submit and confirm an advance reservation, turn the mom
  109. off, delete the degraded reservation and verify the resvend
  110. hook.
  111. """
  112. self.server.import_hook(self.hook_name,
  113. TestResvEndHook.advance_resv_hook_script)
  114. offset = 10
  115. duration = 30
  116. rid = self.submit_resv(offset, duration)
  117. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  118. self.server.expect(RESV, attrs, id=rid)
  119. self.mom.stop()
  120. attrs['reserve_state'] = (MATCH_RE, 'RESV_DEGRADED|10')
  121. self.server.expect(RESV, attrs, id=rid)
  122. self.server.delete(rid)
  123. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  124. (self.server.shortname, rid)
  125. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  126. def test_server_down_case_1(self):
  127. """
  128. Testcase to submit and confirm an advance reservation, turn the server
  129. off, turn the server on, delete the reservation and verify the resvend
  130. hook.
  131. """
  132. self.server.import_hook(self.hook_name,
  133. TestResvEndHook.advance_resv_hook_script)
  134. offset = 10
  135. duration = 30
  136. rid = self.submit_resv(offset, duration)
  137. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  138. self.server.expect(RESV, attrs, id=rid)
  139. self.server.stop()
  140. self.server.start()
  141. self.server.delete(rid)
  142. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  143. (self.server.shortname, rid)
  144. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  145. @timeout(300)
  146. def test_server_down_case_2(self):
  147. """
  148. Testcase to submit and confirm an advance reservation, turn the
  149. server off, wait for the reservation duration to finish, turn the
  150. server on and verify the resvend hook.
  151. """
  152. self.server.import_hook(self.hook_name,
  153. TestResvEndHook.advance_resv_hook_script)
  154. offset = 10
  155. duration = 30
  156. rid = self.submit_resv(offset, duration)
  157. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  158. self.server.expect(RESV, attrs, id=rid)
  159. self.server.stop()
  160. self.logger.info('wait for 30 seconds till the reservation ends')
  161. time.sleep(30)
  162. self.server.start()
  163. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  164. (self.server.shortname, rid)
  165. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  166. @timeout(240)
  167. def test_end_advance_resv(self):
  168. """
  169. Testcase to submit and confirm an advance reservation, wait for it
  170. to end and verify the reservation end hook.
  171. """
  172. self.server.import_hook(self.hook_name,
  173. TestResvEndHook.advance_resv_hook_script)
  174. offset = 10
  175. duration = 30
  176. rid = self.submit_resv(offset, duration)
  177. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  178. self.server.expect(RESV, attrs, id=rid)
  179. attrs['reserve_state'] = (MATCH_RE, 'RESV_RUNNING|5')
  180. self.server.expect(RESV, attrs, id=rid, offset=10)
  181. self.logger.info('wait 30 seconds until the reservation ends')
  182. time.sleep(30)
  183. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  184. (self.server.shortname, rid)
  185. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  186. def test_delete_advance_resv_with_jobs(self):
  187. """
  188. Testcase to submit and confirm an advance reservation, submit
  189. some jobs to the same, wait for the same to end and
  190. verify the reservation end hook.
  191. """
  192. self.server.import_hook(self.hook_name,
  193. TestResvEndHook.advance_resv_hook_script)
  194. offset = 10
  195. duration = 30
  196. rid = self.submit_resv(offset, duration)
  197. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  198. self.server.expect(RESV, attrs, id=rid)
  199. attrs['reserve_state'] = (MATCH_RE, 'RESV_RUNNING|5')
  200. self.server.expect(RESV, attrs, id=rid, offset=10)
  201. job_attrs = {
  202. 'Resource_List.walltime': 5,
  203. 'Resource_List.select': '1:ncpus=1',
  204. 'queue': rid.split('.')[0]
  205. }
  206. for _ in xrange(10):
  207. self.server.submit(Job(TEST_USER, job_attrs))
  208. self.logger.info('wait 10 seconds till the reservation runs some jobs')
  209. time.sleep(10)
  210. self.server.delete(rid)
  211. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  212. (self.server.shortname, rid)
  213. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  214. @timeout(240)
  215. def test_end_advance_resv_with_jobs(self):
  216. """
  217. Testcase to submit and confirm an advance reservation, submit
  218. some jobs to the same, wait for it to start and end, verify
  219. the resvend hook.
  220. """
  221. self.server.import_hook(self.hook_name,
  222. TestResvEndHook.advance_resv_hook_script)
  223. offset = 10
  224. duration = 30
  225. rid = self.submit_resv(offset, duration)
  226. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  227. self.server.expect(RESV, attrs, id=rid)
  228. attrs['reserve_state'] = (MATCH_RE, 'RESV_RUNNING|5')
  229. self.server.expect(RESV, attrs, id=rid, offset=10)
  230. job_attrs = {
  231. 'Resource_List.walltime': 10,
  232. 'Resource_List.select': '1:ncpus=1',
  233. 'queue': rid.split('.')[0]
  234. }
  235. for _ in xrange(10):
  236. self.server.submit(Job(TEST_USER, job_attrs))
  237. self.logger.info('wait till 30 seconds until the reservation ends')
  238. time.sleep(30)
  239. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  240. (self.server.shortname, rid)
  241. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  242. def test_set_attrs(self):
  243. """
  244. Testcase to submit and confirm an advance reservation, delete the
  245. reservation and verify the read permissions in the resvend hook.
  246. """
  247. hook_script = """
  248. import pbs
  249. e=pbs.event()
  250. pbs.logmsg(pbs.LOG_DEBUG, 'Reservation End Hook name - %s' % e.hook_name)
  251. if e.type == pbs.RESV_END:
  252. e.resv.resources_used.walltime = 10
  253. pbs.logmsg(pbs.LOG_DEBUG, 'Reservation ID - %s' %
  254. e.resv.resources_used.walltime)
  255. """
  256. self.server.import_hook(self.hook_name, hook_script)
  257. offset = 10
  258. duration = 30
  259. rid = self.submit_resv(offset, duration)
  260. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  261. self.server.expect(RESV, attrs, id=rid)
  262. self.server.delete(rid)
  263. msg = 'Svr;Server@%s;PBS server internal error (15011) in Error ' \
  264. 'evaluating Python script, attribute '"'resources_used'"' is ' \
  265. 'part of a readonly object' % self.server.shortname
  266. self.server.log_match(msg, tail=True, max_attempts=30, interval=2)
  267. @timeout(300)
  268. def test_delete_resv_occurrence(self):
  269. """
  270. Testcase to submit and confirm a standing reservation for two
  271. occurrences, wait for the first occurrence to end and verify
  272. the end hook for the same, delete the second occurrence and
  273. verify the resvend hook for the latter.
  274. """
  275. self.server.import_hook(self.hook_name,
  276. TestResvEndHook.standing_resv_hook_script)
  277. offset = 10
  278. duration = 30
  279. rid = self.submit_resv(offset, duration, rrule='FREQ=MINUTELY;COUNT=2')
  280. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  281. self.server.expect(RESV, attrs, id=rid)
  282. attrs['reserve_state'] = (MATCH_RE, 'RESV_RUNNING|5')
  283. self.server.expect(RESV, attrs, id=rid, offset=10)
  284. self.logger.info('wait till 30 seconds until the reservation ends')
  285. time.sleep(30)
  286. msg = 'Hook;Server@%s;Reservation occurrence - 1' % \
  287. self.server.shortname
  288. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  289. self.logger.info('Reservation end hook ran for first occurrence of '
  290. 'a standing reservation')
  291. self.logger.info(
  292. 'wait for 10 seconds till the next occurrence is submitted')
  293. time.sleep(10)
  294. self.server.delete(rid)
  295. msg = 'Hook;Server@%s;Reservation occurrence - 2' % \
  296. self.server.shortname
  297. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  298. @timeout(300)
  299. def test_end_resv_occurrences(self):
  300. """
  301. Testcase to submit and confirm a standing reservation for two
  302. occurrences, wait for the first occurrence to end and verify
  303. the end hook for the same, wait for the second occurrence to
  304. start and end, verify the resvend hook for the latter.
  305. """
  306. self.server.import_hook(self.hook_name,
  307. TestResvEndHook.standing_resv_hook_script)
  308. offset = 10
  309. duration = 30
  310. rid = self.submit_resv(offset, duration, rrule='FREQ=MINUTELY;COUNT=2')
  311. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  312. self.server.expect(RESV, attrs, id=rid)
  313. attrs['reserve_state'] = (MATCH_RE, 'RESV_RUNNING|5')
  314. self.server.expect(RESV, attrs, id=rid, offset=10)
  315. self.logger.info('Sleep for 30 seconds for the reservation occurrence '
  316. 'to end')
  317. time.sleep(30)
  318. msg = 'Hook;Server@%s;Reservation occurrence - 1' % \
  319. self.server.shortname
  320. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  321. self.logger.info('Reservation end hook ran for first occurrence of a '
  322. 'standing reservation')
  323. self.logger.info('Sleep for 30 seconds as this is a '
  324. 'minutely occurrence')
  325. time.sleep(30)
  326. attrs = {'reserve_state': (MATCH_RE, 'RESV_RUNNING|5'),
  327. 'reserve_index': 2}
  328. self.server.expect(RESV, attrs, id=rid, attrop=PTL_AND)
  329. msg = 'Hook;Server@%s;Reservation occurrence - 2' % \
  330. self.server.shortname
  331. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  332. self.logger.info('Reservation end hook ran for second occurrence of a'
  333. ' standing reservation')
  334. @timeout(300)
  335. def test_delete_resv_occurrence_with_jobs(self):
  336. """
  337. Testcase to submit and confirm a standing reservation for two
  338. occurrences, submit some jobs to it, wait for the first
  339. occurrence to end and verify the end hook for the same,
  340. delete the second occurrence and verify the resvend hook
  341. for the latter.
  342. """
  343. self.server.import_hook(self.hook_name,
  344. TestResvEndHook.standing_resv_hook_script)
  345. offset = 10
  346. duration = 30
  347. rid = self.submit_resv(offset, duration, rrule='FREQ=MINUTELY;COUNT=2')
  348. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  349. self.server.expect(RESV, attrs, id=rid)
  350. attrs['reserve_state'] = (MATCH_RE, 'RESV_RUNNING|5')
  351. self.server.expect(RESV, attrs, id=rid, offset=10)
  352. job_attrs = {
  353. 'Resource_List.walltime': 5,
  354. 'Resource_List.select': '1:ncpus=1',
  355. 'queue': rid.split('.')[0]
  356. }
  357. for _ in xrange(10):
  358. self.server.submit(Job(TEST_USER, job_attrs))
  359. self.logger.info('Sleep for 30 seconds for the reservation occurrence '
  360. 'to end')
  361. time.sleep(30)
  362. msg = 'Hook;Server@%s;Reservation occurrence - 1' % \
  363. self.server.shortname
  364. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  365. self.logger.info('Reservation end hook ran for first occurrence of a '
  366. 'standing reservation')
  367. self.logger.info(
  368. 'wait for 10 seconds till the next occurrence is submitted')
  369. time.sleep(10)
  370. self.server.delete(rid)
  371. msg = 'Hook;Server@%s;Reservation occurrence - 2' % \
  372. self.server.shortname
  373. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  374. @timeout(300)
  375. def test_end_resv_occurrences_with_jobs(self):
  376. """
  377. Testcase to submit and confirm a standing reservation for two
  378. occurrences, wait for the first occurrence to end and verify
  379. the end hook for the same, wait for the second occurrence to
  380. end and verify the resvend hook for the latter.
  381. """
  382. self.server.import_hook(self.hook_name,
  383. TestResvEndHook.standing_resv_hook_script)
  384. offset = 10
  385. duration = 30
  386. rid = self.submit_resv(offset, duration, rrule='FREQ=MINUTELY;COUNT=2')
  387. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  388. self.server.expect(RESV, attrs, id=rid)
  389. job_attrs = {
  390. 'Resource_List.walltime': 5,
  391. 'Resource_List.select': '1:ncpus=1',
  392. 'queue': rid.split('.')[0]
  393. }
  394. for _ in xrange(10):
  395. self.server.submit(Job(TEST_USER, job_attrs))
  396. attrs['reserve_state'] = (MATCH_RE, 'RESV_RUNNING|5')
  397. self.server.expect(RESV, attrs, id=rid, offset=10)
  398. self.logger.info('Sleep for 30 seconds for the reservation occurrence '
  399. 'to end')
  400. time.sleep(30)
  401. msg = 'Hook;Server@%s;Reservation occurrence - 1' % \
  402. self.server.shortname
  403. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  404. self.logger.info('Reservation end hook ran for first occurrence of a '
  405. 'standing reservation')
  406. self.logger.info('Sleep for 30 seconds as this is a '
  407. 'minutely occurrence')
  408. time.sleep(30)
  409. attrs = {'reserve_state': (MATCH_RE, 'RESV_RUNNING|5'),
  410. 'reserve_index': 2}
  411. self.server.expect(RESV, attrs, id=rid, attrop=PTL_AND)
  412. msg = 'Hook;Server@%s;Reservation occurrence - 2' % \
  413. self.server.shortname
  414. self.server.log_match(msg, tail=True, interval=2, max_attempts=30)
  415. self.logger.info('Reservation end hook ran for second occurrence of a '
  416. 'standing reservation')
  417. def test_unconfirmed_resv_with_node(self):
  418. """
  419. Testcase to set the node attributes such that the number of ncpus is 1,
  420. submit and confirm a reservation on the same node, submit another
  421. reservation on the same node and verify the reservation end hook
  422. as the latter one stays in unconfirmed state.
  423. """
  424. self.server.import_hook(self.hook_name,
  425. TestResvEndHook.advance_resv_hook_script)
  426. node_attrs = {'resources_available.ncpus': 1}
  427. self.server.manager(MGR_CMD_SET, NODE, node_attrs,
  428. id=self.mom.shortname, expect=True)
  429. offset = 10
  430. duration = 10
  431. rid = self.submit_resv(offset, duration)
  432. attrs = {'reserve_state': (MATCH_RE, 'RESV_CONFIRMED|2')}
  433. self.server.expect(RESV, attrs, id=rid)
  434. new_rid = self.submit_resv(offset, duration)
  435. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  436. (self.server.shortname, new_rid)
  437. self.server.log_match(msg, tail=True, max_attempts=10,
  438. existence=False)
  439. @timeout(240)
  440. def test_scheduler_down_case_1(self):
  441. """
  442. Testcase to turn off the scheduler and submit a reservation,
  443. the same will be in unconfirmed state and upon ending the
  444. resvend hook shall not run.
  445. """
  446. self.server.import_hook(self.hook_name,
  447. TestResvEndHook.advance_resv_hook_script)
  448. self.scheduler.stop()
  449. offset = 10
  450. duration = 30
  451. rid = self.submit_resv(offset, duration)
  452. self.logger.info('wait for 30 seconds till the reservation ends ')
  453. time.sleep(30)
  454. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  455. (self.server.shortname, rid)
  456. self.server.log_match(msg, tail=True, max_attempts=10,
  457. existence=False)
  458. def test_scheduler_down_case_2(self):
  459. """
  460. Testcase to turn off the scheduler and submit a reservation,
  461. the same will be in unconfirmed state and deleting that should
  462. not run the resvend hook.
  463. """
  464. self.server.import_hook(self.hook_name,
  465. TestResvEndHook.advance_resv_hook_script)
  466. self.scheduler.stop()
  467. offset = 10
  468. duration = 10
  469. rid = self.submit_resv(offset, duration)
  470. self.server.delete(rid)
  471. msg = 'Hook;Server@%s;Reservation ID - %s' % \
  472. (self.server.shortname, rid)
  473. self.server.log_match(msg, tail=True, max_attempts=10,
  474. existence=False)