pbs_config 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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 getopt
  38. import logging
  39. import errno
  40. import ptl
  41. from ptl.lib.pbs_testlib import *
  42. from ptl.utils.pbs_testsuite import PBS_GROUPS
  43. # trap SIGINT and SIGPIPE
  44. def trap_exceptions(etype, value, tb):
  45. sys.excepthook = sys.__excepthook__
  46. if issubclass(etype, KeyboardInterrupt):
  47. pass
  48. elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
  49. pass
  50. else:
  51. sys.__excepthook__(etype, value, tb)
  52. sys.excepthook = trap_exceptions
  53. def usage():
  54. msg = []
  55. msg += ['Usage: ' + os.path.basename(sys.argv[0]) + ' [OPTION]\n\n']
  56. msg += ['-t <hostnames>: comma-separated hosts to operate on. Defaults '
  57. 'to localhost\n']
  58. msg += ['-l <log level>: one of DEBUG, INFO, ERROR, FATAL, WARNING\n']
  59. msg += ['\n']
  60. msg += ['--log-conf=<file>: logging config file\n']
  61. msg += ['--as-diag=<pbs_diag>: Mimic pbs_diag snapshot.\n']
  62. msg += ['--revert-config: revert services to their default ' +
  63. 'configuration\n']
  64. msg += ['\t --scheduler: operate on scheduler\n']
  65. msg += ['\t --server: operate on server\n']
  66. msg += ['\t --mom: operate on MoM\n']
  67. msg += ['\t --del-hooks=<True|False>: If True delete hooks.']
  68. msg += [' Defaults to True\n']
  69. msg += ['\t --del-queues=<True|False>: Delete non-default queues.']
  70. msg += [' Defaults to True\n']
  71. msg += ['--save-config=<config>: save configuration to file\n']
  72. msg += ['\t revert-config and save-config can operate on the following\n']
  73. msg += ['\t --scheduler: operate on scheduler\n']
  74. msg += ['\t --server: operate on server\n']
  75. msg += ['\t --mom: operate on MoM\n']
  76. msg += ['--load-config=<config>: load configuration from saved file.\n']
  77. msg += ['\n']
  78. msg += ['--vnodify: define vnodes using the following suboptions:\n']
  79. msg += ['\t-a <attrs>: comma separated list of attributes to set ' +
  80. 'on vnodes.\n']
  81. msg += ['\t format: <name>=<value>. Defaults to 8 cpus ' +
  82. '8gb of mem.\n']
  83. msg += ['\t-A: set additive mode, leave vnode definitions in ' +
  84. 'place. \n']
  85. msg += ['\t Default is to clear all existing vnode definition ' +
  86. 'files.\n']
  87. msg += ['\t-d <y|n>: if y, delete all server nodes. Defaults to y.\n']
  88. msg += ['\t-f <filename>: use output of pbsnodes -av from file as ' +
  89. 'definition\n']
  90. msg += ['\t-P <num>: number of vnodes per host\n']
  91. msg += ['\t-o <filename>: output vnode definition to filename\n']
  92. msg += ['\t-M <mom>: MoM to operate on, format <host>@<path/to/conf>.\n'
  93. '\t Defaults to localhost.\n']
  94. msg += ['\t-N <num vnodes>: number of vnodes to create. No default.\n']
  95. msg += ['\t-n <name>: name of the natural vnode to create. ' +
  96. 'Defaults to MoM FQDN\n']
  97. msg += ['\t-p <name>: prefix of name of node to create. ' +
  98. 'Output format: \n']
  99. msg += ['\t prefix followed by [<num]. Defaults to vnode\n']
  100. msg += ['\t-r <y|n>: restart MoM or not, defaults to y\n']
  101. msg += ['\t-s: if set, share vnodes on the host. ' +
  102. 'Default is "standalone" hosts\n']
  103. msg += ['\t-u: if set, allocate the natural vnode\n']
  104. msg += ['\n']
  105. msg += ['--multi-mom: Define and create multiple MoMs on a host\n']
  106. msg += ['\t--create=<num>: number of MoMs to create. No default.\n']
  107. msg += ['\t--restart=<[seq]>: restart MoMs in sequence\n']
  108. msg += ['\t--stop=<[seq]>: stop MoMs in sequence\n']
  109. msg += ['\t--serverhost=<host>: hostname of server, defaults to '
  110. 'localhost\n']
  111. msg += ['\t--home-prefix=<path>: prefix to PBS_HOME directory, defaults\n'
  112. '\t to /var/spool/PBS_m\n']
  113. msg += ['\t--conf-prefix=<path>: prefix to pbs.conf file. Defaults to\n'
  114. '\t /etc/pbs.conf.m\n']
  115. msg += ['\t--init-port=<number>: initial port to allocate. Defaults to\n'
  116. '\t 15011\n']
  117. msg += ['\t--step-port=<number>: step for port sequence. Defaults to 2\n']
  118. msg += ['\n']
  119. msg += ['--switch-version=<version>: switch to a given installed ' +
  120. 'version of PBS\n']
  121. msg += ['\tcurrently only works for "vanilla" installs, i.e, not '
  122. 'developer installs\n']
  123. msg += ['\tbased on /etc/pbs.conf and "default" PBS_EXEC\n']
  124. msg += ['\n']
  125. msg += ['--check-ug: verifies whether test users and groups are ']
  126. msg += [' defined as expected.\n Note that -t option '
  127. 'will be ignored.\n']
  128. msg += ['--make-ug: create users and groups to match what is expected\n']
  129. msg += [' Note that -t option will be ignored\n']
  130. msg += ['--del-ug: delete users and groups which is expected for PTL\n']
  131. msg += [' Note that -t option will be ignored\n']
  132. msg += ['--version: print version number and exit\n']
  133. print "".join(msg)
  134. def process_config(hosts, process_obj, conf_file=None, type='default',
  135. delqueues=False, delhooks=False):
  136. for host in hosts:
  137. if MGR_OBJ_SCHED in process_obj:
  138. if type == 'default':
  139. Scheduler(host).revert_to_defaults()
  140. elif type == 'load':
  141. Scheduler(host).load_configuration(conf_file)
  142. elif type == 'save':
  143. Scheduler(host).save_configuration(conf_file)
  144. if MGR_OBJ_SERVER in process_obj:
  145. if type == 'default':
  146. Server(host).revert_to_defaults(delhooks=delhooks,
  147. delqueues=delqueues)
  148. elif type == 'load':
  149. Server(host).load_configuration(conf_file)
  150. elif type == 'save':
  151. Server(host).save_configuration(conf_file)
  152. if MGR_OBJ_NODE in process_obj:
  153. if type == 'default':
  154. MoM(host).revert_to_defaults()
  155. elif type == 'load':
  156. MoM(host).load_configuration(conf_file)
  157. elif type == 'save':
  158. MoM(host).save_configuration(conf_file)
  159. def process_attributes(attrs):
  160. nattrs = {}
  161. for a in attrs.split(','):
  162. if '=' not in a:
  163. logging.error('attributes must be of the form' +
  164. ' <name>=<value>')
  165. sys.exit(1)
  166. k, v = a.split('=')
  167. nattrs[k] = v
  168. return nattrs
  169. def common_users_groups_ops():
  170. du = DshUtils()
  171. g_create = []
  172. u_create = []
  173. gm_expected = {}
  174. gm_actual = du.group_memberships(map(lambda g: str(g), PBS_GROUPS))
  175. for g in PBS_GROUPS:
  176. gm_expected[g] = g.users
  177. for k, v in gm_expected.items():
  178. if str(k) not in gm_actual:
  179. g_create.append(k)
  180. for _u in v:
  181. if _u not in u_create:
  182. u_create.append(_u)
  183. else:
  184. for _u in v:
  185. if ((str(_u) not in gm_actual[str(k)]) and
  186. (_u not in u_create)):
  187. u_create.append(_u)
  188. return (gm_expected, gm_actual, g_create, u_create)
  189. def check_users_groups():
  190. gm_expected, gm_actual, g_create, u_create = common_users_groups_ops()
  191. if ((len(g_create) > 0) or (len(u_create) > 0)):
  192. out = ['Expected (format is <group name>: <user> [, <user2>...) ']
  193. for k, v in gm_expected.items():
  194. out += [str(k) + ': ' + ', '.join(map(lambda u:str(u), v))]
  195. out += ['\n', 'Actual: ']
  196. for k, v in gm_actual.items():
  197. out += [k + ': ' + ', '.join(v)]
  198. print '\n'.join(out)
  199. return False
  200. else:
  201. return True
  202. def make_users_groups():
  203. du = DshUtils()
  204. _, _, g_create, u_create = common_users_groups_ops()
  205. for g in g_create:
  206. du.groupadd(g, g.gid, logerr=False)
  207. for u in u_create:
  208. du.useradd(name=u, uid=u.uid, gid=u.groups[0], groups=u.groups,
  209. logerr=False)
  210. return True
  211. def delete_users_groups():
  212. du = DshUtils()
  213. _, gm_actual, _, _ = common_users_groups_ops()
  214. for v in gm_actual.values():
  215. for u in v:
  216. du.userdel(u, logerr=False)
  217. for k in gm_actual.keys():
  218. du.groupdel(k, logerr=False)
  219. return True
  220. if __name__ == '__main__':
  221. if len(sys.argv) < 2:
  222. usage()
  223. sys.exit(0)
  224. # vnodify options
  225. vnodify = False
  226. vnodeprefix = 'vnode'
  227. num_vnodes = None
  228. additive = False
  229. sharedhost = False
  230. filename = None
  231. attrs = "resources_available.ncpus=8,resources_available.mem=8gb"
  232. hostname = None
  233. conf_file = None
  234. restart = True
  235. delall = True
  236. natvnode = None
  237. usenatvnode = False
  238. vdefname = None
  239. # end of vnodify options
  240. hosts = None
  241. revert = False
  242. op = None
  243. loadconf = None
  244. saveconf = None
  245. logconf = None
  246. vnodes_per_host = 1
  247. delqueues = True
  248. delhooks = True
  249. lvl = logging.INFO
  250. switchversion = None
  251. check_ug = False
  252. make_ug = False
  253. del_ug = False
  254. multimom = False
  255. num_moms = None
  256. restart_moms = None
  257. stop_moms = None
  258. clienthost = None
  259. serverhost = None
  260. init_port = 15011
  261. step_port = 2
  262. import_jobs = False
  263. home_prefix = 'PBS_m'
  264. conf_prefix = 'pbs.conf_m'
  265. asdiag = None
  266. process_obj = []
  267. vnodify_args = "a:d:f:N:n:o:P:p:M:l:r:v:Asu"
  268. generic_args = "l:t:h"
  269. largs = ["scheduler", "server", "mom", "revert-config", "load-config=",
  270. "save-config=", "vnodify", "import-jobs", "del-ug",
  271. "switch-version=", "log-conf=", "check-ug", "del-hooks=",
  272. "del-queues=", "version", "make-ug", "multi-mom", "clienthost=",
  273. "home-prefix=", "conf-prefix=", "serverhost=", "init-port=",
  274. "step-port=", "as-diag=", "create=", "restart=", "stop="]
  275. try:
  276. opts, args = getopt.getopt(sys.argv[1:], vnodify_args + generic_args,
  277. largs)
  278. except:
  279. usage()
  280. sys.exit(1)
  281. for o, val in opts:
  282. if o == '-l':
  283. lvl = CliUtils().get_logging_level(val)
  284. elif o == '-t':
  285. hosts = val
  286. elif o == '-a':
  287. attrs = val
  288. elif o == '-A':
  289. additive = True
  290. elif o == '-d':
  291. if val.startswith('y'):
  292. delall = True
  293. else:
  294. delall = False
  295. elif o == '-f':
  296. filename = CliUtils.expand_abs_path(val)
  297. elif o == '-P':
  298. vnodes_per_host = int(val)
  299. elif o == '-p':
  300. vnodeprefix = val
  301. elif o == '-M':
  302. if '@' in val:
  303. (hostname, conf_file) = val.split('@')
  304. else:
  305. hostname = val
  306. elif o == '-N':
  307. num_vnodes = int(val)
  308. elif o == '-o':
  309. vdefname = val
  310. elif o == '-s':
  311. sharedhost = True
  312. elif o == '-r':
  313. if val.startswith('y'):
  314. restart = True
  315. elif o == '-n':
  316. natvnode = val
  317. elif o == '-u':
  318. usenatvnode = True
  319. elif o == '--check-ug':
  320. check_ug = True
  321. elif o == '--make-ug':
  322. make_ug = True
  323. elif o == '--del-ug':
  324. del_ug = True
  325. elif o == '--del-hooks':
  326. delhooks = eval(val)
  327. elif o == '--del-queues':
  328. delqueues = eval(val)
  329. elif o == '--as-diag':
  330. asdiag = CliUtils.expand_abs_path(val)
  331. elif o == '--import-jobs':
  332. import_jobs = True
  333. elif o == '--log-conf':
  334. logconf = val
  335. elif o == '--multi-mom':
  336. multimom = True
  337. elif o == '--create':
  338. num_moms = int(val)
  339. elif o == '--home-prefix':
  340. home_prefix = val
  341. elif o == '--conf-prefix':
  342. conf_prefix = val
  343. elif o == '--scheduler':
  344. process_obj.append(MGR_OBJ_SCHED)
  345. elif o == '--server':
  346. process_obj.append(MGR_OBJ_SERVER)
  347. elif o == '--mom':
  348. process_obj.append(MGR_OBJ_NODE)
  349. elif o == '--restart':
  350. restart_moms = eval(val, {}, {})
  351. elif o == '--stop':
  352. stop_moms = eval(val, {}, {})
  353. elif o == '--vnodify':
  354. vnodify = True
  355. elif o == '--revert-config':
  356. revert = True
  357. elif o == '--load-config':
  358. loadconf = CliUtils.expand_abs_path(val)
  359. elif o == '--save-config':
  360. saveconf = CliUtils.expand_abs_path(val)
  361. elif o == '--serverhost':
  362. serverhost = val
  363. elif o == '--init-port':
  364. init_port = int(val)
  365. elif o == '--step-port':
  366. step_port = int(val)
  367. elif o == '--switch-version':
  368. switchversion = val
  369. elif o == '--version':
  370. print ptl.__version__
  371. sys.exit(0)
  372. else:
  373. sys.stderr.write("Unrecognized option " + o + "\n")
  374. usage()
  375. sys.exit(1)
  376. PtlConfig()
  377. if logconf:
  378. logging.config.fileConfig(logconf)
  379. else:
  380. logging.basicConfig(level=lvl)
  381. if hosts is None:
  382. hosts = [socket.gethostname()]
  383. else:
  384. hosts = hosts.split(',')
  385. if check_ug:
  386. rv = check_users_groups()
  387. if rv:
  388. sys.exit(0)
  389. sys.exit(1)
  390. if del_ug:
  391. rv = delete_users_groups()
  392. if rv:
  393. sys.exit(0)
  394. sys.exit(1)
  395. if make_ug:
  396. rv = make_users_groups()
  397. if rv:
  398. sys.exit(0)
  399. sys.exit(1)
  400. if revert:
  401. process_config(hosts, process_obj, type='default', delqueues=delqueues,
  402. delhooks=delhooks)
  403. elif loadconf:
  404. # when loading configuration apply the saved configuration based
  405. # on what was saved irregardless of what object types were passed in
  406. allobjs = [MGR_OBJ_SCHED, MGR_OBJ_SERVER, MGR_OBJ_NODE]
  407. process_config(hosts, allobjs, loadconf, type='load',
  408. delqueues=delqueues, delhooks=delhooks)
  409. elif saveconf:
  410. if os.path.isfile(saveconf):
  411. answer = raw_input('file ' + saveconf + ' exists, overwrite? '
  412. '[y]/n: ')
  413. if answer == 'n':
  414. sys.exit(1)
  415. if not process_obj:
  416. process_obj = [MGR_OBJ_SERVER, MGR_OBJ_SCHED, MGR_OBJ_NODE]
  417. process_config(hosts, process_obj, saveconf, type='save',
  418. delqueues=delqueues, delhooks=delhooks)
  419. elif vnodify:
  420. if filename:
  421. vdef = BatchUtils().file_to_vnodedef(filename)
  422. if vdef:
  423. MoM(hostname, pbsconf_file=conf_file).insert_vnode_def(vdef)
  424. elif num_vnodes is None:
  425. logging.error('A number of vnodes to create is required\n')
  426. sys.exit(1)
  427. else:
  428. nattrs = process_attributes(attrs)
  429. for hostname in hosts:
  430. s = Server(hostname)
  431. m = MoM(hostname, pbsconf_file=conf_file)
  432. s.create_vnodes(vnodeprefix, nattrs, num_vnodes, m,
  433. additive, sharedhost, restart, delall,
  434. natvnode, usenatvnode, fname=vdefname,
  435. vnodes_per_host=vnodes_per_host)
  436. elif switchversion:
  437. pi = PBSInitServices()
  438. for host in hosts:
  439. pi.switch_version(host, switchversion)
  440. elif multimom:
  441. if num_moms is not None:
  442. if os.getuid() != 0:
  443. logging.error('Must be run as root')
  444. sys.exit(1)
  445. du = DshUtils()
  446. conf = du.parse_pbs_config(serverhost)
  447. serverhost = DshUtils().get_pbs_server_name(conf)
  448. s = Server(serverhost)
  449. nattrs = process_attributes(attrs)
  450. s.create_moms(num=num_moms, attrib=nattrs, conf_prefix=conf_prefix,
  451. home_prefix=home_prefix, momhosts=hosts,
  452. init_port=init_port, step_port=step_port)
  453. if (restart_moms or stop_moms):
  454. mom_op = []
  455. if restart_moms:
  456. mom_op = restart_moms
  457. if stop_moms:
  458. mom_op += stop_moms
  459. for i in mom_op:
  460. c = os.path.join('/etc', conf_prefix + str(i))
  461. pi = PBSInitServices(serverhost, conf=c)
  462. if restart_moms:
  463. ret = pi.restart()
  464. if stop_moms:
  465. ret = pi.stop()
  466. if ret['rc'] != 0:
  467. logging.error(ret['err'])
  468. del pi
  469. elif asdiag is not None:
  470. if os.getuid() != 0:
  471. logging.error('Must be run as root')
  472. sys.exit(1)
  473. Server(diag=asdiag).clusterize(conf_file, hosts,
  474. import_jobs=import_jobs)