pbs_fairshare.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. @tags('sched')
  38. class TestFairshare(TestFunctional):
  39. """
  40. Test the pbs_sched fairshare functionality. Note there are two
  41. fairshare tests in the standard smoke test that are not included here.
  42. """
  43. def set_up_resource_group(self):
  44. """
  45. Set up the resource_group file for test suite
  46. """
  47. self.scheduler.add_to_resource_group('group1', 10, 'root', 40)
  48. self.scheduler.add_to_resource_group('group2', 20, 'root', 60)
  49. self.scheduler.add_to_resource_group(TEST_USER, 11, 'group1', 50)
  50. self.scheduler.add_to_resource_group(TEST_USER1, 12, 'group1', 50)
  51. self.scheduler.add_to_resource_group(TEST_USER2, 21, 'group2', 60)
  52. self.scheduler.add_to_resource_group(TEST_USER3, 22, 'group2', 40)
  53. self.scheduler.set_fairshare_usage(TEST_USER, 100)
  54. self.scheduler.set_fairshare_usage(TEST_USER1, 100)
  55. self.scheduler.set_fairshare_usage(TEST_USER3, 1000)
  56. def test_formula_keyword(self):
  57. """
  58. Test to see if 'fairshare_tree_usage' and 'fairshare_perc' are allowed
  59. to be set in the job_sort_formula
  60. """
  61. # manager() will throw a PbsManagerError exception if this fails
  62. self.server.manager(MGR_CMD_SET, SERVER,
  63. {'job_sort_formula': 'fairshare_tree_usage'})
  64. self.server.manager(MGR_CMD_SET, SERVER,
  65. {'job_sort_formula': 'fairshare_perc'})
  66. formula = '"pow(2,-(fairshare_tree_usage/fairshare_perc))"'
  67. self.server.manager(MGR_CMD_SET, SERVER, {'job_sort_formula': formula})
  68. formula = 'fairshare_factor'
  69. self.server.manager(MGR_CMD_SET, SERVER, {'job_sort_formula': formula})
  70. formula = 'fair_share_factor'
  71. try:
  72. self.server.manager(
  73. MGR_CMD_SET, SERVER, {'job_sort_formula': formula})
  74. except PbsManagerError as e:
  75. self.assertTrue("Formula contains invalid keyword" in e.msg[0])
  76. def test_fairshare_formula(self):
  77. """
  78. Test fairshare in the formula. Make sure the fairshare_tree_usage
  79. is correct
  80. """
  81. self.set_up_resource_group()
  82. self.scheduler.set_sched_config({'log_filter': 2048})
  83. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'})
  84. self.server.manager(MGR_CMD_SET, SERVER,
  85. {'job_sort_formula': 'fairshare_tree_usage'})
  86. J1 = Job(TEST_USER)
  87. jid1 = self.server.submit(J1)
  88. J2 = Job(TEST_USER1)
  89. jid2 = self.server.submit(J2)
  90. J3 = Job(TEST_USER2)
  91. jid3 = self.server.submit(J3)
  92. J4 = Job(TEST_USER3)
  93. jid4 = self.server.submit(J4)
  94. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  95. msg = ';Formula Evaluation = '
  96. self.scheduler.log_match(str(jid1) + msg + '0.1253')
  97. self.scheduler.log_match(str(jid2) + msg + '0.1253')
  98. self.scheduler.log_match(str(jid3) + msg + '0.5004')
  99. self.scheduler.log_match(str(jid4) + msg + '0.8330')
  100. def test_fairshare_formula2(self):
  101. """
  102. Test fairshare in the formula. Make sure the fairshare_perc
  103. is correct
  104. """
  105. self.set_up_resource_group()
  106. self.scheduler.set_sched_config({'log_filter': 2048})
  107. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'})
  108. self.server.manager(MGR_CMD_SET, SERVER,
  109. {'job_sort_formula': 'fairshare_perc'})
  110. J1 = Job(TEST_USER)
  111. jid1 = self.server.submit(J1)
  112. J2 = Job(TEST_USER1)
  113. jid2 = self.server.submit(J2)
  114. J3 = Job(TEST_USER2)
  115. jid3 = self.server.submit(J3)
  116. J4 = Job(TEST_USER3)
  117. jid4 = self.server.submit(J4)
  118. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  119. msg = ';Formula Evaluation = '
  120. self.scheduler.log_match(str(jid1) + msg + '0.2')
  121. self.scheduler.log_match(str(jid2) + msg + '0.2')
  122. self.scheduler.log_match(str(jid3) + msg + '0.36')
  123. self.scheduler.log_match(str(jid4) + msg + '0.24')
  124. def test_fairshare_formula3(self):
  125. """
  126. Test fairshare in the formula. Make sure entities with small usage
  127. are negatively affected by their high usage siblings. Make sure that
  128. jobs run in the correct order. Use fairshare_tree_usage in a
  129. pow() formula
  130. """
  131. self.set_up_resource_group()
  132. self.scheduler.set_sched_config({'log_filter': 2048})
  133. formula = '"pow(2,-(fairshare_tree_usage/fairshare_perc))"'
  134. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'})
  135. self.server.manager(MGR_CMD_SET, SERVER, {'job_sort_formula': formula})
  136. J1 = Job(TEST_USER2)
  137. jid1 = self.server.submit(J1)
  138. J2 = Job(TEST_USER3)
  139. jid2 = self.server.submit(J2)
  140. J3 = Job(TEST_USER)
  141. jid3 = self.server.submit(J3)
  142. J4 = Job(TEST_USER1)
  143. jid4 = self.server.submit(J4)
  144. t = int(time.time())
  145. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  146. msg = ';Formula Evaluation = '
  147. self.scheduler.log_match(str(jid1) + msg + '0.3816')
  148. self.scheduler.log_match(str(jid2) + msg + '0.0902')
  149. self.scheduler.log_match(str(jid3) + msg + '0.6477')
  150. self.scheduler.log_match(str(jid4) + msg + '0.6477')
  151. self.scheduler.log_match('Leaving Scheduling Cycle', starttime=t)
  152. c = self.scheduler.cycles(lastN=1)[0]
  153. job_order = [jid3, jid4, jid1, jid2]
  154. for i in range(len(job_order)):
  155. self.assertEqual(job_order[i].split('.')[0], c.political_order[i])
  156. def test_fairshare_formula4(self):
  157. """
  158. Test fairshare in the formula. Make sure entities with small usage
  159. are negatively affected by their high usage siblings. Make sure that
  160. jobs run in the correct order. Use keyword fairshare_factor
  161. """
  162. self.set_up_resource_group()
  163. self.scheduler.set_sched_config({'log_filter': 2048})
  164. formula = 'fairshare_factor'
  165. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'})
  166. self.server.manager(MGR_CMD_SET, SERVER, {'job_sort_formula': formula})
  167. J1 = Job(TEST_USER2)
  168. jid1 = self.server.submit(J1)
  169. J2 = Job(TEST_USER3)
  170. jid2 = self.server.submit(J2)
  171. J3 = Job(TEST_USER)
  172. jid3 = self.server.submit(J3)
  173. J4 = Job(TEST_USER1)
  174. jid4 = self.server.submit(J4)
  175. t = int(time.time())
  176. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  177. msg = ';Formula Evaluation = '
  178. self.scheduler.log_match(str(jid1) + msg + '0.3816')
  179. self.scheduler.log_match(str(jid2) + msg + '0.0902')
  180. self.scheduler.log_match(str(jid3) + msg + '0.6477')
  181. self.scheduler.log_match(str(jid4) + msg + '0.6477')
  182. self.scheduler.log_match('Leaving Scheduling Cycle', starttime=t)
  183. c = self.scheduler.cycles(lastN=1)[0]
  184. job_order = [jid3, jid4, jid1, jid2]
  185. for i in range(len(job_order)):
  186. self.assertEqual(job_order[i].split('.')[0], c.political_order[i])
  187. def test_fairshare_formula5(self):
  188. """
  189. Test fairshare in the formula with fair_share set to true in scheduler.
  190. Make sure formula takes precedence over fairshare usage. Output will be
  191. same as in test_fairshare_formula4.
  192. """
  193. self.set_up_resource_group()
  194. a = {'log_filter': 2048, 'fair_share': "True ALL"}
  195. self.scheduler.set_sched_config(a)
  196. formula = 'fairshare_factor'
  197. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'})
  198. self.server.manager(MGR_CMD_SET, SERVER, {'job_sort_formula': formula})
  199. J1 = Job(TEST_USER2, {'Resource_List.cput': 10})
  200. jid1 = self.server.submit(J1)
  201. J2 = Job(TEST_USER3, {'Resource_List.cput': 20})
  202. jid2 = self.server.submit(J2)
  203. J3 = Job(TEST_USER, {'Resource_List.cput': 30})
  204. jid3 = self.server.submit(J3)
  205. J4 = Job(TEST_USER1, {'Resource_List.cput': 40})
  206. jid4 = self.server.submit(J4)
  207. t = int(time.time())
  208. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  209. msg = ';Formula Evaluation = '
  210. self.scheduler.log_match(str(jid1) + msg + '0.3816')
  211. self.scheduler.log_match(str(jid2) + msg + '0.0902')
  212. self.scheduler.log_match(str(jid3) + msg + '0.6477')
  213. self.scheduler.log_match(str(jid4) + msg + '0.6477')
  214. self.scheduler.log_match('Leaving Scheduling Cycle', starttime=t)
  215. c = self.scheduler.cycles(start=t, lastN=1)[0]
  216. job_order = [jid3, jid4, jid1, jid2]
  217. for i in range(len(job_order)):
  218. self.assertEqual(job_order[i].split('.')[0], c.political_order[i])
  219. def test_fairshare_formula6(self):
  220. """
  221. Test fairshare in the formula. Make sure entities with small usage
  222. are negatively affected by their high usage siblings. Make sure that
  223. jobs run in the correct order. Use keyword fairshare_factor
  224. with ncpus/walltime
  225. """
  226. self.set_up_resource_group()
  227. self.scheduler.set_sched_config({'log_filter': 2048})
  228. formula = '\"fairshare_factor + (walltime/ncpus)\"'
  229. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'})
  230. self.server.manager(MGR_CMD_SET, SERVER, {'job_sort_formula': formula})
  231. J1 = Job(TEST_USER2, {'Resource_List.ncpus': 1,
  232. 'Resource_List.walltime': "00:01:00"})
  233. jid1 = self.server.submit(J1)
  234. J2 = Job(TEST_USER3, {'Resource_List.ncpus': 2,
  235. 'Resource_List.walltime': "00:01:00"})
  236. jid2 = self.server.submit(J2)
  237. J3 = Job(TEST_USER, {'Resource_List.ncpus': 3,
  238. 'Resource_List.walltime': "00:02:00"})
  239. jid3 = self.server.submit(J3)
  240. J4 = Job(TEST_USER1, {'Resource_List.ncpus': 4,
  241. 'Resource_List.walltime': "00:02:00"})
  242. jid4 = self.server.submit(J4)
  243. t = int(time.time())
  244. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  245. msg = ';Formula Evaluation = '
  246. self.scheduler.log_match(str(jid1) + msg + '60.3816')
  247. self.scheduler.log_match(str(jid2) + msg + '30.0902')
  248. self.scheduler.log_match(str(jid3) + msg + '40.6477')
  249. self.scheduler.log_match(str(jid4) + msg + '30.6477')
  250. self.scheduler.log_match('Leaving Scheduling Cycle', starttime=t)
  251. c = self.scheduler.cycles(start=t, lastN=1)[0]
  252. job_order = [jid1, jid3, jid4, jid2]
  253. for i in range(len(job_order)):
  254. self.assertEqual(job_order[i].split('.')[0], c.political_order[i])
  255. def test_pbsfs(self):
  256. """
  257. Test to see if running pbsfs affects the scheduler's view of the
  258. fairshare usage. This is done by calling the Scheduler()'s
  259. revert_to_defaults(). This will call pbsfs -e to remove all usage.
  260. """
  261. self.scheduler.add_to_resource_group(TEST_USER, 11, 'root', 10)
  262. self.scheduler.add_to_resource_group(TEST_USER1, 12, 'root', 10)
  263. self.scheduler.set_sched_config({'fair_share': 'True'})
  264. self.scheduler.set_fairshare_usage(TEST_USER, 100)
  265. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'})
  266. J1 = Job(TEST_USER)
  267. jid1 = self.server.submit(J1)
  268. J2 = Job(TEST_USER1)
  269. jid2 = self.server.submit(J2)
  270. t = int(time.time())
  271. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  272. self.scheduler.log_match('Leaving Scheduling Cycle', starttime=t,
  273. max_attempts=10)
  274. c = self.scheduler.cycles(lastN=1)[0]
  275. job_order = [jid2, jid1]
  276. for i in range(len(job_order)):
  277. self.assertEqual(job_order[i].split('.')[0], c.political_order[i])
  278. self.server.deljob(id=jid1, wait=True)
  279. self.server.deljob(id=jid2, wait=True)
  280. self.scheduler.revert_to_defaults()
  281. # Set TEST_USER1 to 50. If revert_to_defaults() has affected the
  282. # scheduler's view of the fairshare usage, it's the only entity with
  283. # usage. It's job will run second. If revert_to_defaults() did
  284. # nothing, 50 is less than 100, so TEST_USER1's job will run first
  285. self.scheduler.add_to_resource_group(TEST_USER, 11, 'root', 10)
  286. self.scheduler.add_to_resource_group(TEST_USER1, 12, 'root', 10)
  287. self.scheduler.set_sched_config({'fair_share': 'True'})
  288. self.scheduler.set_fairshare_usage(TEST_USER1, 50)
  289. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'False'},
  290. expect=True)
  291. J3 = Job(TEST_USER)
  292. jid3 = self.server.submit(J3)
  293. J4 = Job(TEST_USER1)
  294. jid4 = self.server.submit(J4)
  295. t = int(time.time())
  296. self.server.manager(MGR_CMD_SET, SERVER, {'scheduling': 'True'})
  297. self.scheduler.log_match('Leaving Scheduling Cycle', starttime=t,
  298. max_attempts=10)
  299. c = self.scheduler.cycles(lastN=1)[0]
  300. job_order = [jid3, jid4]
  301. for i in range(len(job_order)):
  302. self.assertEqual(job_order[i].split('.')[0], c.political_order[i])