pbs_stat 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. #!/usr/bin/env python
  2. # coding: utf-8
  3. # Copyright (C) 1994-2018 Altair Engineering, Inc.
  4. # For more information, contact Altair at www.altair.com.
  5. #
  6. # This file is part of the PBS Professional ("PBS Pro") software.
  7. #
  8. # Open Source License Information:
  9. #
  10. # PBS Pro is free software. You can redistribute it and/or modify it under the
  11. # terms of the GNU Affero General Public License as published by the Free
  12. # Software Foundation, either version 3 of the License, or (at your option) any
  13. # later version.
  14. #
  15. # PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
  16. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  17. # FOR A PARTICULAR PURPOSE.
  18. # See the GNU Affero General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU Affero General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. #
  23. # Commercial License Information:
  24. #
  25. # For a copy of the commercial license terms and conditions,
  26. # go to: (http://www.pbspro.com/UserArea/agreement.html)
  27. # or contact the Altair Legal Department.
  28. #
  29. # Altair’s dual-license business model allows companies, individuals, and
  30. # organizations to create proprietary derivative works of PBS Pro and
  31. # distribute them - whether embedded or bundled with other software -
  32. # under a commercial license agreement.
  33. #
  34. # Use of Altair’s trademarks, including but not limited to "PBS™",
  35. # "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
  36. # trademark licensing policies.
  37. import sys
  38. import getopt
  39. import logging
  40. import logging.config
  41. import os
  42. import time
  43. import re
  44. import errno
  45. import ptl
  46. from ptl.utils.pbs_cliutils import CliUtils
  47. from ptl.utils.pbs_logutils import PBSAccountingLog
  48. from ptl.utils.pbs_dshutils import DshUtils
  49. from ptl.lib.pbs_testlib import PtlConfig, BatchUtils, PbsTypeSize
  50. from ptl.lib.pbs_testlib import Server, Scheduler, PbsStatusError
  51. from ptl.lib.pbs_testlib import PTL_AND, PTL_OR, PTL_CLI
  52. from ptl.lib.pbs_testlib import VNODE, JOB, SERVER, SCHED, RSC, QUEUE, HOOK
  53. from ptl.lib.pbs_testlib import RESV, RESOURCES_AVAILABLE, RESOURCES_TOTAL
  54. # trap SIGINT and SIGPIPE
  55. def trap_exceptions(etype, value, tb):
  56. sys.excepthook = sys.__excepthook__
  57. if issubclass(etype, KeyboardInterrupt):
  58. pass
  59. elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
  60. pass
  61. else:
  62. sys.__excepthook__(etype, value, tb)
  63. sys.excepthook = trap_exceptions
  64. def usage():
  65. msg = []
  66. msg += ['Usage: ' + os.path.basename(sys.argv[0]).split('.pyc')[0]]
  67. msg += [' [OPTION]\n\n']
  68. msg += ['\tStatus and filter PBS entities on given attributes\n\n']
  69. msg += ['\tNote: All output of this unsupported tool is experimental, \n']
  70. msg += ['\tdo not rely on it remaining as-is across releases.\n\n']
  71. msg += ['-A <path>: Path to accounting log\n']
  72. msg += ['-a <attrs>: comma-separated list of attributes\n']
  73. msg += [' attrs can be in key[OP]val format where OP is one ' +
  74. 'of <,<=,=,>=,>,~\n']
  75. msg += ['-b: show available resources (a.k.a backfill hole)\n']
  76. msg += ['-c: counts number of matching items\n']
  77. msg += ['-C: counts grand total of given attribute values in complex\n']
  78. msg += ['-d <pbs_diag>: path to diag directory\n']
  79. msg += ['-j: report job equivalence classes\n']
  80. msg += ['-n: report node equivalence classes\n']
  81. msg += ['-r <name>: comma-separated list of resource names, ' +
  82. 'e.g. ncpus, mem.\n']
  83. msg += ['-s: show selected objects in qselect-like format\n']
  84. msg += ['-t <hostname>: target hostname\n']
  85. msg += ['-T: report total resources available. Operates only on '
  86. 'equivalence classes\n']
  87. msg += ['-U: show current utilization of the system, use -r to \n' +
  88. ' specify resources, default to ncpus, mem, nodes\n']
  89. msg += ['--id=<obj_id>: identifier of the object to query\n']
  90. msg += ['--key=<account record>: Accounting record key, one of Q or E\n']
  91. msg += ['--user=<username>: username to query for limits or '
  92. 'utilization\n']
  93. msg += ['--group=<groupname>: group to query for limits or '
  94. 'utilization\n']
  95. msg += ['--project=<project>: project to query for limits\n']
  96. msg += ['--json: display object type information in json format.\n']
  97. msg += ['--mode: show operating mode of PTL, one of cli or api\n']
  98. msg += ['--cli: force stat to query PBS server over cli\n']
  99. msg += ['--report-twiki: produce report in Twiki format\n']
  100. msg += ['--nodes: operate on node\n']
  101. msg += ['--queues: operate on queues\n']
  102. msg += ['--jobs: operate on jobs\n']
  103. msg += ['--resvs: operate on reservations\n']
  104. msg += ['--server: operate on server\n']
  105. msg += ['--scheduler: operate on scheduler\n']
  106. msg += ['--report: produce a site report\n']
  107. msg += ['--resources: operate on resources\n']
  108. msg += ['--resource=<name>: name of resource to stat\n']
  109. msg += ['--resources-set: list resources set for a given object type\n']
  110. msg += ['--fairshare-info=<entity>: query and display fairshare ' +
  111. ' info of entity\n']
  112. msg += ['--fairshare-tree: query and display fairshare tree \n']
  113. msg += ['--sline: show selected objects on a single line\n'
  114. '\t will not affect output of --json, -j, -s, nor -n\n']
  115. msg += ['--eval-formula: evaluate job priority\n']
  116. msg += ['--include-running-jobs: include running jobs in formula'
  117. ' evaluation\n']
  118. msg += ['--pports: show number of privileged ports in use\n']
  119. msg += ['--resolve-indirectness: If set, dereference indirect '
  120. ' resources\n']
  121. msg += ['--db-access=<cred>: file to credentials to access db\n']
  122. msg += ['--limits-info: show limit information per entity and '
  123. 'object\n']
  124. msg += ['--over-soft-limits: show entities that are over soft '
  125. 'limits\n']
  126. msg += [
  127. '--server-file=<path>: path to file with output of qstat -Bf\n']
  128. msg += ['--dedtime-file=<path>: path to a dedicated time file\n']
  129. msg += [
  130. '--nodes-file=<path>: path to file with output of pbsnodes -av\n']
  131. msg += [
  132. '--queues-file=<path>: path to file with output of qstat -Qf\n']
  133. msg += ['--jobs-file=<path>: path to file with output of qstat -f\n']
  134. msg += [
  135. '--resvs-file=<path>: path to file with output of pbs_rstat -f\n']
  136. msg += ['--log-conf=<file>: logging config file\n']
  137. msg += ['--version: print version number and exit\n']
  138. print "".join(msg)
  139. class SiteReportFormatter:
  140. def __init__(self, diag, version, sched_version, jeq, neq, utilization,
  141. limits, qtypes, users, groups, sc, formula, backfill, hooks,
  142. job_states, osrelease):
  143. self.report_tm = time.ctime()
  144. if diag and len(diag) > 1:
  145. diag = diag.replace(".", "")
  146. diag = diag.replace("/", "_")
  147. diag = diag.replace("pbs_diag_", "")
  148. if diag[0] in ('/', '_'):
  149. diag = diag[1:]
  150. m = re.search("(?P<dtm>\d{6}_\d{6})", diag)
  151. if m:
  152. _tm = m.group("dtm")
  153. _kt = time.mktime(time.strptime(_tm, "%y%m%d_%H%M%S"))
  154. self.report_tm = time.ctime(_kt)
  155. self.diag = diag
  156. self.version = version
  157. self.sched_version = sched_version
  158. self.jeq = sorted(jeq, key=lambda e: len(e.entities))
  159. self.neq = sorted(neq, key=lambda e: len(e.entities))
  160. self.utilization = utilization
  161. self.limits = limits
  162. self.qtypes = qtypes
  163. self.users = users
  164. self.groups = groups
  165. self.sc = sc
  166. self.formula = formula
  167. self.backfill = backfill
  168. self.hooks = hooks
  169. self.job_states = job_states
  170. self.delim = "\n"
  171. self.open_tag = []
  172. self.close_tag = []
  173. def __twiki__(self):
  174. self.delim = "---+++++"
  175. self.open_tag = ["<verbatim>"]
  176. self.close_tag = ["</verbatim>"]
  177. return self.__str__()
  178. def __str__(self):
  179. title = 'PBS Pro cluster report on ' + self.report_tm
  180. if len(self.open_tag) == 0 or self.diag is None:
  181. msg = []
  182. self.sep = ['-' * len(title)]
  183. else:
  184. msg = ["---+++" + str(self.diag)]
  185. self.sep = []
  186. msg += [title]
  187. msg += self.sep
  188. msg += [self.delim + 'PBS Pro version']
  189. msg += self.sep
  190. msg += self.open_tag
  191. if self.version == self.sched_version:
  192. msg += [self.version]
  193. else:
  194. msg += ['Server: ' + self.version]
  195. msg += ['Scheduler: ' + self.sched_version]
  196. msg += self.close_tag
  197. if osrelease is not None:
  198. msg += [self.delim + 'OS release']
  199. msg += self.sep
  200. msg += self.open_tag
  201. msg += [osrelease]
  202. msg += self.close_tag
  203. msg += [self.delim + 'Utilization']
  204. msg += self.sep
  205. msg += self.open_tag
  206. msg += u
  207. msg += self.close_tag
  208. msg += [self.delim + 'Job States']
  209. msg += self.sep
  210. msg += self.open_tag
  211. for k, v in self.job_states.items():
  212. msg += ["%s: %d" % (k.split('=')[1], v)]
  213. msg += self.close_tag
  214. if self.limits:
  215. qsl = ssl = qhl = shl = 0
  216. if SERVER in self.limits:
  217. for l in self.limits[SERVER]:
  218. if '_soft' in l.limit_type:
  219. ssl += 1
  220. else:
  221. shl += 1
  222. if QUEUE in self.limits:
  223. for l in self.limits[QUEUE]:
  224. if '_soft' in l.limit_type:
  225. qsl += 1
  226. else:
  227. qhl += 1
  228. msg += [self.delim + 'Limits']
  229. msg += self.sep
  230. msg += self.open_tag
  231. msg += ["Queue soft limits: %d" % qsl]
  232. msg += ["Queue hard limits: %d" % qhl]
  233. msg += ["Server soft limits: %d" % ssl]
  234. msg += ["Server hard limits: %d" % shl]
  235. msg += self.close_tag
  236. msg += [self.delim + 'Queue types']
  237. msg += self.sep
  238. msg += self.open_tag
  239. for k, v in qtypes.items():
  240. msg += ["%s: %d" % (k.split('=')[1], v)]
  241. msg += self.close_tag
  242. msg += [self.delim + 'Number of users and groups']
  243. msg += self.sep
  244. msg += self.open_tag
  245. msg += ["users: %d" % (users)]
  246. msg += ["groups: %d" % (groups)]
  247. msg += self.close_tag
  248. if self.hooks:
  249. msg += [self.delim + 'Hooks']
  250. msg += self.sep
  251. msg += self.open_tag
  252. msg += self.hooks
  253. msg += self.close_tag
  254. msg += [self.delim + 'Scheduling policies']
  255. msg += self.sep
  256. msg += self.open_tag
  257. if 'preemptive_sched' in sc:
  258. msg += ["preemption: %s" % sc['preemptive_sched']]
  259. if 'backfill' in sc:
  260. msg += ["backfilling: %s" % sc['backfill']]
  261. if 'fair_share' in sc:
  262. msg += ["fair share: %s" % sc['fair_share']]
  263. if formula is not None:
  264. msg += ["formula: %s" % self.formula]
  265. if backfill is not None:
  266. msg += ["backfill depth: %s" % self.backfill]
  267. msg += self.close_tag
  268. msg += [self.delim + 'Node Equivalence Classes']
  269. msg += self.sep
  270. msg += self.open_tag
  271. for e in self.neq:
  272. msg += [str(e)]
  273. msg += self.close_tag
  274. msg += [self.delim + 'Job Equivalence Classes']
  275. msg += self.sep
  276. msg += self.open_tag
  277. for e in self.jeq:
  278. msg += [str(e)]
  279. msg += self.close_tag
  280. return "\n".join(msg)
  281. def utilization_to_str(u):
  282. msg = []
  283. for k, v in u.items():
  284. if len(v) != 2:
  285. continue
  286. if v[1] == 0:
  287. perc = 100
  288. else:
  289. perc = 100 * v[0] / v[1]
  290. if 'mem' in k:
  291. v[0] = PbsTypeSize(str(v[0]) + 'kb')
  292. v[1] = PbsTypeSize(str(v[1]) + 'kb')
  293. msg += [k + ': (' + str(v[0]) + '/' + str(v[1]) + ') ' +
  294. str(perc) + '%']
  295. else:
  296. msg += [k + ': (' + str(v[0]) + '/' + str(v[1]) + ') ' +
  297. str(perc) + '%']
  298. return msg
  299. if __name__ == '__main__':
  300. if len(sys.argv) < 2:
  301. usage()
  302. sys.exit(1)
  303. diag = None
  304. lvl = logging.ERROR
  305. hostname = None
  306. objtype = None
  307. jobclasses = False
  308. nodeclasses = False
  309. attributes = None
  310. backfillhole = None
  311. attrop = PTL_OR
  312. accumulate = False
  313. grandtotal = False
  314. inputfile = {}
  315. qselectfmt = False
  316. resources = None
  317. utilization = False
  318. fsusage_entity = None
  319. fstree = False
  320. fsentity = None
  321. fsperc_entity = None
  322. pbsfs = False
  323. eval_formula = False
  324. entity = {}
  325. objid = None
  326. resourcesset = None
  327. dedtimefile = None
  328. limits_info = False
  329. over_soft_limits = False
  330. json_on = False
  331. pports = False
  332. db_access = None
  333. logconf = None
  334. scheduler = None
  335. server = None
  336. report = False
  337. report_twiki = False
  338. force_cli = False
  339. get_mode = False
  340. fmt = {'%6': '\n'}
  341. acct = None
  342. key = 'E'
  343. indirectness = False
  344. osrelease = None
  345. include_running_jobs = False
  346. restotal = RESOURCES_AVAILABLE # equivalence classes report avail - assgnd
  347. lopts = ["nodes", "queues", "server", "scheduler", "jobs", "resvs"]
  348. lopts += ["fairshare-tree", "eval-formula", "user=", "group=", "project="]
  349. lopts += ["fairshare-info=", "resource=", "resources-set", "nodes-file="]
  350. lopts += ["queues-file=", "jobs-file=", "resvs-file=", "server-file="]
  351. lopts += ["dedtime-file=", "limits-info", "json", "pports", "db-access="]
  352. lopts += ["over-soft-limits", "id=", "resources", "log-conf=", "version"]
  353. lopts += ["mode", "cli", "report", "sline", "key=", "resolve-indirectness"]
  354. lopts += ["report-twiki", "include-running-jobs"]
  355. try:
  356. opts, args = getopt.getopt(sys.argv[1:], "a:A:d:l:r:t:fUbcChjnsT",
  357. lopts)
  358. except:
  359. logging.error('unhandled option')
  360. usage()
  361. sys.exit(1)
  362. for o, val in opts:
  363. if o == '-a':
  364. attributes = val
  365. elif o == '-A':
  366. acct = CliUtils.expand_abs_path(val)
  367. elif o == '-b':
  368. backfillhole = True
  369. objtype = VNODE
  370. elif o == '-c':
  371. accumulate = True
  372. elif o == '-C':
  373. grandtotal = True
  374. elif o == '-d':
  375. diag = CliUtils.expand_abs_path(val)
  376. if not os.path.isdir(diag):
  377. sys.stderr.write('Cannnot access diag at ' + str(diag) + '\n')
  378. sys.exit(1)
  379. elif o == '-j':
  380. jobclasses = True
  381. elif o == '-l':
  382. lvl = CliUtils().get_logging_level(val)
  383. elif o == '-n':
  384. nodeclasses = True
  385. elif o == '-r':
  386. resources = val
  387. elif o == '-s':
  388. qselectfmt = True
  389. elif o == '-t':
  390. hostname = val
  391. elif o == '-U':
  392. utilization = True
  393. elif o == '-h':
  394. usage()
  395. sys.exit(0)
  396. elif o == '--cli':
  397. force_cli = True
  398. elif o == '--key':
  399. key = val
  400. elif o == '--id':
  401. objid = val
  402. elif o == '--sline':
  403. fmt = {'%1': ': ', '%2': '', '%3': '=', '%4': ', ', '%5': '\n'}
  404. elif o == "--pports":
  405. pports = True
  406. elif o == '--version':
  407. print ptl.__version__
  408. sys.exit(0)
  409. elif o == "--user":
  410. entity['euser'] = val
  411. elif o == "--group":
  412. entity['egroup'] = val
  413. elif o == "--mode":
  414. get_mode = True
  415. elif o == "--project":
  416. entity['project'] = val
  417. elif o == "--fairshare-info":
  418. fsentity = val
  419. fstree = True
  420. elif o == "--fairshare-tree":
  421. fstree = True
  422. elif o == "--eval-formula":
  423. eval_formula = True
  424. elif o == '--include-running-jobs':
  425. include_running_jobs = True
  426. elif o == "--db-access":
  427. db_access = CliUtils.expand_abs_path(val)
  428. elif o == "--json":
  429. json_on = True
  430. elif o == "--limits-info":
  431. limits_info = True
  432. elif o == "--over-soft-limits":
  433. over_soft_limits = True
  434. elif o == "--log-conf":
  435. logconf = val
  436. elif o == "--nodes":
  437. objtype = VNODE
  438. elif o == "--queues":
  439. objtype = QUEUE
  440. elif o == "--jobs":
  441. objtype = JOB
  442. elif o == "--resvs":
  443. objtype = RESV
  444. elif o == "--server":
  445. objtype = SERVER
  446. elif o == "--scheduler":
  447. objtype = SCHED
  448. elif o == "--report":
  449. report = True
  450. elif o == "--report-twiki":
  451. report_twiki = True
  452. elif o == "--resources":
  453. objtype = RSC
  454. elif o == "--resource":
  455. objtype = RSC
  456. objid = val
  457. elif o == "--resources-set":
  458. resourcesset = True
  459. elif o == "--resolve-indirectness":
  460. indirectness = True
  461. elif o == "--nodes-file":
  462. objtype = VNODE
  463. inputfile[VNODE] = CliUtils.expand_abs_path(val)
  464. elif o == "--queues-file":
  465. objtype = QUEUE
  466. inputfile[QUEUE] = CliUtils.expand_abs_path(val)
  467. elif o == "--jobs-file":
  468. objtype = JOB
  469. inputfile[JOB] = CliUtils.expand_abs_path(val)
  470. elif o == "--resvs-file":
  471. objtype = RESV
  472. inputfile[RESV] = CliUtils.expand_abs_path(val)
  473. elif o == "--server-file":
  474. objtype = SERVER
  475. inputfile[SERVER] = CliUtils.expand_abs_path(val)
  476. elif o == "--dedtime-file":
  477. dedtimefile = CliUtils.expand_abs_path(val)
  478. elif o == '-T':
  479. restotal = None
  480. else:
  481. sys.stderr.write("Unrecognized option")
  482. usage()
  483. sys.exit(1)
  484. PtlConfig()
  485. if pports:
  486. msg = CliUtils().priv_ports_info(hostname)
  487. if not msg:
  488. sys.exit(1)
  489. print "\n".join(msg)
  490. sys.exit(0)
  491. if logconf:
  492. logging.config.fileConfig(logconf)
  493. else:
  494. logging.basicConfig(level=lvl)
  495. bu = BatchUtils()
  496. if len(inputfile) > 0:
  497. server = Server(diagmap=inputfile, db_access=db_access)
  498. elif diag is not None or db_access is not None:
  499. server = Server(hostname, diag=diag, db_access=db_access)
  500. scheduler = Scheduler(server=server, diag=diag, db_access=db_access)
  501. else:
  502. if hostname is None:
  503. if 'PBS_SERVER' in os.environ:
  504. _h = os.environ['PBS_SERVER']
  505. else:
  506. if 'PBS_CONF_FILE' in os.environ:
  507. _c = DshUtils().parse_pbs_config(
  508. file=os.environ['PBS_CONF_FILE'])
  509. elif os.path.isfile('/etc/pbs.conf'):
  510. _c = DshUtils().parse_pbs_config()
  511. if 'PBS_SERVER' in _c:
  512. _h = _c['PBS_SERVER']
  513. else:
  514. _h = None
  515. else:
  516. _h = hostname
  517. server = Server(_h, stat=False)
  518. if force_cli:
  519. if server.get_op_mode() != PTL_CLI:
  520. server.set_op_mode(PTL_CLI)
  521. if get_mode:
  522. print server.get_op_mode()
  523. sys.exit(0)
  524. if dedtimefile:
  525. if scheduler is None:
  526. scheduler = Scheduler(
  527. server=server, diag=diag, db_access=db_access)
  528. scheduler.set_dedicated_time_file(dedtimefile)
  529. # server is assumed up in diag mode
  530. if not server.isUp() and objtype != RSC and\
  531. not db_access:
  532. logging.error('PBS Server is down, exiting')
  533. sys.exit(1)
  534. if report or report_twiki:
  535. jobs = server.status(JOB)
  536. nodes = server.status(VNODE)
  537. queues = server.status(QUEUE)
  538. jeq = server.equivalence_classes(JOB, bslist=jobs)
  539. if scheduler is not None:
  540. res = scheduler.get_resources(exclude=['host', 'vnode', 'arch'])
  541. if res:
  542. for i in range(len(res)):
  543. res[i] = "resources_available." + str(res[i])
  544. else:
  545. res = ['resources_available.ncpus', 'resources_available.mem']
  546. neq = server.equivalence_classes(VNODE, res, bslist=nodes,
  547. show_zero_resources=True,
  548. op=RESOURCES_TOTAL,
  549. resolve_indirectness=indirectness)
  550. job_states = server.counter(JOB, 'job_state', bslist=jobs)
  551. u = utilization_to_str(server.utilization(entity=entity, nodes=nodes,
  552. jobs=jobs))
  553. lims = server.parse_all_limits(
  554. server=[server.attributes], queues=queues)
  555. qtypes = server.counter(QUEUE, 'queue_type', bslist=queues)
  556. d = server.counter(JOB, ['euser', 'egroup'], bslist=jobs)
  557. users = groups = 0
  558. for k, v in d.items():
  559. if 'euser' in k:
  560. users += 1
  561. elif 'egroup' in k:
  562. groups += 1
  563. if scheduler is None:
  564. scheduler = Scheduler(server=server, diag=server.diag,
  565. diagmap=server.diagmap)
  566. sc = scheduler.sched_config
  567. formula = backfill = None
  568. version = 'unavailable'
  569. sched_version = 'unavailable'
  570. hooks = []
  571. _hlist = server.status(HOOK)
  572. for hook in _hlist:
  573. if (not hook['id'].startswith('PBS_translate_mpp') and
  574. not hook['id'].startswith('PBS_ibwins') and
  575. not hook['id'].startswith('PBSadd_spawn')):
  576. if 'enabled' in hook and hook['enabled'] == 'false':
  577. _disabled = '[disabled]'
  578. else:
  579. _disabled = ''
  580. hooks.append(hook['id'] + ': ' + hook['event'] + ' ' +
  581. _disabled)
  582. try:
  583. if 'pbs_version' not in server.attributes:
  584. d = server.status(SERVER, ['pbs_version', 'job_sort_formula',
  585. 'backfill_depth'])
  586. else:
  587. d = [server.attributes]
  588. if 'job_sort_formula' in d[0]:
  589. formula = d[0]['job_sort_formula']
  590. if 'backfill_depth' in d[0]:
  591. backfill = d[0]['backfill_depth']
  592. if 'pbs_version' in d[0]:
  593. version = d[0]['pbs_version']
  594. except PbsStatusError:
  595. pass
  596. try:
  597. server.status(SCHED, 'pbs_version')
  598. except PbsStatusError:
  599. pass
  600. if d and 'pbs_version' in d[0]:
  601. sched_version = d[0]['pbs_version']
  602. if diag is not None:
  603. f = os.path.join(diag, 'OSrelease')
  604. if os.path.isfile(f):
  605. fos = open(f)
  606. osrelease = fos.readline()
  607. fos.close()
  608. sr = SiteReportFormatter(diag, version, sched_version, jeq, neq, u,
  609. lims, qtypes, users, groups, sc, formula,
  610. backfill, hooks, job_states, osrelease)
  611. if report_twiki:
  612. print str(sr.__twiki__())
  613. else:
  614. print str(sr)
  615. sys.exit(0)
  616. if utilization:
  617. if resources:
  618. resources = resources.split(',')
  619. u = server.utilization(resources, entity=entity)
  620. msg = utilization_to_str(u)
  621. if msg:
  622. print "\n".join(msg)
  623. sys.exit(0)
  624. if fstree:
  625. if scheduler is None:
  626. scheduler = Scheduler(server=server, diag=server.diag,
  627. diagmap=server.diagmap, db_access=db_access)
  628. fs_as_bs = scheduler.fairshare_tree.__batch_status__()
  629. if json_on:
  630. print CliUtils.__json__(fs_as_bs)
  631. else:
  632. scheduler.utils.show(fs_as_bs, fsentity)
  633. sys.exit(0)
  634. if eval_formula:
  635. f = server.evaluate_formula(include_running_jobs=include_running_jobs)
  636. if f:
  637. d = server.status(SERVER, 'job_sort_formula')
  638. print 'Formula: ' + d[0]['job_sort_formula']
  639. ret = sorted(f.items(), key=lambda x: x[1][1], reverse=True)
  640. for (jobid, (fml, val)) in ret:
  641. print jobid + ': ' + fml + ' = ' + str(val)
  642. sys.exit(0)
  643. # parse resources and attributes 'language', i.e., handling of && and ||
  644. if resources is not None:
  645. if objtype in (JOB, RESV):
  646. _r = "Resource_List."
  647. else:
  648. _r = "resources_available."
  649. # add the resources to any attributes that may have been specified
  650. if attributes is None:
  651. attributes = ''
  652. else:
  653. attributes += ","
  654. if "&&" in resources:
  655. attrop = PTL_AND
  656. resources = resources.replace("&&", ",")
  657. elif "||" in resources:
  658. resources = resources.replace("||", ',')
  659. attributes += ",".join(map(lambda n: _r + n, resources.split(',')))
  660. if attributes is not None:
  661. attributes = attributes.replace(" ", "")
  662. if "&&" in attributes:
  663. attrop = PTL_AND
  664. attributes = attributes.replace("&&", ",")
  665. elif "||" in attributes:
  666. attributes = attributes.replace("||", ',')
  667. attributes = attributes.split(",")
  668. if attributes:
  669. setattrs = False
  670. operators = ('<=', '>=', '!=', '=', '>', '<', '~')
  671. for a in attributes:
  672. for op in operators:
  673. if op in a:
  674. setattrs = True
  675. break
  676. d = bu.convert_attributes_by_op(attributes, setattrs)
  677. if len(d) > 0:
  678. attributes = d
  679. if backfillhole is not None:
  680. server.show_whats_available(attrib=attributes)
  681. sys.exit(0)
  682. # other than the backfill hole that requires working with server objects,
  683. # since updating object attributes can be an expensive operation on large
  684. # systems, we disable it on these select calls where they are needed
  685. server.ptl_conf['update_attributes'] = False
  686. if limits_info or over_soft_limits:
  687. etype = None
  688. ename = None
  689. if 'euser' in entity:
  690. etype = 'u'
  691. ename = entity['euser']
  692. elif 'egroup' in entity:
  693. etype = 'g'
  694. ename = entity['egroup']
  695. elif 'project' in entity:
  696. etype = 'p'
  697. ename = entity['project']
  698. linfo = server.limits_info(etype=etype,
  699. ename=ename,
  700. db_access=db_access,
  701. over=over_soft_limits)
  702. if json_on:
  703. CliUtils.__json__(server.utils.decode_dictlist(linfo))
  704. else:
  705. server.utils.show(linfo)
  706. sys.exit(0)
  707. if nodeclasses:
  708. if scheduler is None and os.getuid() == 0:
  709. scheduler = Scheduler(server=server, diag=server.diag,
  710. diagmap=server.diagmap, db_access=db_access)
  711. if attributes:
  712. res = attributes
  713. elif scheduler is not None:
  714. res = scheduler.get_resources(exclude=['host', 'vnode', 'arch'])
  715. if res:
  716. # ncpus and mem may have been removed from the resources line
  717. # in which case we must add them back
  718. if 'ncpus' not in res:
  719. res.append('ncpus')
  720. if 'mem' not in res:
  721. res.append('mem')
  722. for i in range(len(res)):
  723. res[i] = "resources_available." + str(res[i])
  724. else:
  725. res = ['resources_available.ncpus', 'resources_available.mem']
  726. server.show_equivalence_classes(None, VNODE, res, op=restotal,
  727. show_zero_resources=True,
  728. db_access=db_access,
  729. resolve_indirectness=indirectness)
  730. if jobclasses or acct is not None:
  731. if acct is not None:
  732. eqclasses = {}
  733. # no need to ping a live server, so pretend
  734. sm = {SERVER: None}
  735. # disable logging to avoid displaying server instatiation messages
  736. logging.disable(logging.INFO)
  737. server = Server('__diagserver__', diagmap=sm)
  738. logging.disable(logging.NOTSET)
  739. alog = PBSAccountingLog(show_progress=True)
  740. alog.enable_accounting_workload_parsing()
  741. alog.analyze(acct)
  742. attrs = alog.job_attrs.values()
  743. eq = server.equivalence_classes(JOB, attributes, bslist=attrs)
  744. server.show_equivalence_classes(eq)
  745. if alog.parser_errors > 0:
  746. print 'Failed to parse: ' + str(alog.parser_errors)
  747. sys.exit(0)
  748. else:
  749. server.show_equivalence_classes(None, JOB, attributes, op=restotal,
  750. db_access=db_access)
  751. # remaining operations are to filter an object type, skip if an
  752. # equivalence class or resource was requested
  753. if (jobclasses or nodeclasses):
  754. sys.exit(0)
  755. if restotal is None:
  756. logging.error('-T option operates only an equivalence classes')
  757. sys.exit(1)
  758. if resourcesset is not None:
  759. if objtype is None:
  760. logging.error('no object type specified')
  761. sys.exit(1)
  762. res = bu.list_resources(objtype, server.status(objtype))
  763. if res:
  764. print "\n".join(res)
  765. sys.exit(0)
  766. if (accumulate or grandtotal) and (attributes is not None):
  767. if objtype is None:
  768. logging.error('no object type specified')
  769. sys.exit(1)
  770. d = server.counter(objtype, attributes, attrop=attrop,
  771. grandtotal=grandtotal, db_access=db_access,
  772. extend='t', resolve_indirectness=indirectness)
  773. for k, v in d.items():
  774. if grandtotal and 'mem' in k:
  775. d[k] = PbsTypeSize().encode(value=v)
  776. else:
  777. d[k] = v
  778. else:
  779. if objtype is None:
  780. logging.error('no object type specified')
  781. sys.exit(1)
  782. idonly = True
  783. if not qselectfmt:
  784. idonly = False
  785. if attributes:
  786. d = server.filter(objtype, attributes, attrop=attrop,
  787. idonly=idonly, id=objid, extend='t',
  788. db_access=db_access, grandtotal=grandtotal,
  789. resolve_indirectness=indirectness)
  790. else:
  791. statinfo = server.status(objtype, id=objid, extend='t',
  792. db_access=db_access,
  793. resolve_indirectness=indirectness)
  794. if json_on:
  795. print CliUtils.__json__(server.utils.decode_dictlist(statinfo))
  796. else:
  797. server.utils.show(statinfo, fmt=fmt)
  798. sys.exit(0)
  799. if not d:
  800. sys.exit(0)
  801. if not qselectfmt and not (grandtotal or accumulate):
  802. if objtype is None:
  803. logging.error('no object type specified')
  804. sys.exit(1)
  805. visited = []
  806. toshow = []
  807. for objs in d.values():
  808. for obj in objs:
  809. if obj['id'] in visited:
  810. continue
  811. else:
  812. toshow.append(obj)
  813. visited.append(obj['id'])
  814. if json_on:
  815. CliUtils.__json__(server.utils.decode_dictlist(toshow))
  816. else:
  817. server.utils.show(toshow, fmt=fmt)
  818. elif attrop == PTL_AND and len(d) > 0:
  819. if objtype is None:
  820. logging.error('no object type specified')
  821. sys.exit(1)
  822. if isinstance(attributes, (list, dict)):
  823. if grandtotal:
  824. for k, v in d.items():
  825. print k + ": " + str(v)
  826. elif accumulate:
  827. print " && ".join(d.keys()) + ": " + str(d.values()[0])
  828. else:
  829. print " && ".join(d.keys()) + ": \n" + \
  830. "\n".join(str(d.values()[0]))
  831. else:
  832. print str(" && ".join(d.keys())) + ": " + str(d.values()[0])
  833. else:
  834. if objtype is None:
  835. logging.error('no object type specified')
  836. sys.exit(1)
  837. for k, v in d.items():
  838. if isinstance(v, list):
  839. print k + ":"
  840. for val in v:
  841. print val
  842. else:
  843. print k + ":" + str(v)