2020-05-20 20:21:08 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
path = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
|
|
|
if path.startswith('/opt'):
|
|
|
|
sys.path.append(path)
|
|
|
|
|
|
|
|
import confluent.client as client
|
|
|
|
|
2020-08-24 20:05:58 +00:00
|
|
|
|
|
|
|
def cleararm(nr, cli):
|
|
|
|
nodes = set([])
|
|
|
|
for rsp in cli.read('/noderange/{0}/attributes/current'.format(nr)):
|
|
|
|
for node in rsp.get('databynode', {}):
|
|
|
|
nodeinfo = rsp['databynode'][node]
|
|
|
|
for attr in nodeinfo:
|
|
|
|
if attr == 'deployment.apiarmed':
|
|
|
|
curr = nodeinfo[attr].get('value', '')
|
|
|
|
if curr == 'continuous':
|
|
|
|
nodes.add(node)
|
|
|
|
noderange = nr
|
|
|
|
if nodes:
|
|
|
|
noderange += ',-({0})'.format(','.join(nodes))
|
|
|
|
for rsp in cli.update('/noderange/{0}/attributes/current'.format(noderange),
|
|
|
|
{'deployment.apiarmed': ''}):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2020-05-20 20:21:08 +00:00
|
|
|
def armonce(nr, cli):
|
|
|
|
nodes = set([])
|
|
|
|
for rsp in cli.read('/noderange/{0}/attributes/current'.format(nr)):
|
|
|
|
for node in rsp.get('databynode', {}):
|
|
|
|
nodeinfo = rsp['databynode'][node]
|
|
|
|
for attr in nodeinfo:
|
|
|
|
if attr == 'deployment.apiarmed':
|
|
|
|
curr = nodeinfo[attr].get('value', '')
|
|
|
|
if curr == 'continuous':
|
|
|
|
nodes.add(node)
|
|
|
|
noderange = nr
|
|
|
|
if nodes:
|
|
|
|
noderange += ',-({0})'.format(','.join(nodes))
|
|
|
|
for rsp in cli.update('/noderange/{0}/attributes/current'.format(noderange),
|
|
|
|
{'deployment.apiarmed': 'once'}):
|
|
|
|
pass
|
|
|
|
|
2020-08-24 20:05:58 +00:00
|
|
|
|
2020-05-20 20:21:08 +00:00
|
|
|
def setpending(nr, profile, cli):
|
2020-08-24 20:05:58 +00:00
|
|
|
args = {'deployment.pendingprofile': profile}
|
|
|
|
if not profile.startswith('genesis-'):
|
|
|
|
args['deployment.stagedprofile'] = ''
|
|
|
|
args['deployment.profile'] = ''
|
2020-05-20 20:21:08 +00:00
|
|
|
for rsp in cli.update('/noderange/{0}/attributes/current'.format(nr),
|
2020-08-24 20:05:58 +00:00
|
|
|
args):
|
2020-05-20 20:21:08 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2020-08-25 18:27:46 +00:00
|
|
|
def clearpending(nr, cli):
|
2020-08-24 20:05:58 +00:00
|
|
|
for rsp in cli.update('/noderange/{0}/attributes/current'.format(nr),
|
|
|
|
{'deployment.pendingprofile': ''}):
|
|
|
|
pass
|
|
|
|
|
2020-05-20 20:21:08 +00:00
|
|
|
def main(args):
|
|
|
|
ap = argparse.ArgumentParser(description='Deploy OS to nodes')
|
2020-08-25 13:58:58 +00:00
|
|
|
ap.add_argument('-c', '--clear', help='Clear any pending deployment action', action='store_true')
|
2020-07-01 18:01:28 +00:00
|
|
|
ap.add_argument('-n', '--network', help='Initiate deployment over PXE/HTTP', action='store_true')
|
|
|
|
ap.add_argument('-p', '--prepare', help='Configure for deployment without setting boot device or rebooting', action='store_true')
|
2020-05-20 20:21:08 +00:00
|
|
|
ap.add_argument('-m', '--maxnodes', help='Specifiy a maximum nodes to be deployed')
|
|
|
|
ap.add_argument('noderange', help='Set of nodes to deploy')
|
2020-08-25 18:05:43 +00:00
|
|
|
ap.add_argument('profile', nargs='?', help='Profile name to deploy')
|
2020-08-25 18:29:23 +00:00
|
|
|
args, extra = ap.parse_known_args(args)
|
2020-08-25 18:05:43 +00:00
|
|
|
if not args.network and not args.prepare and not args.clear:
|
2020-08-24 20:05:58 +00:00
|
|
|
sys.stderr.write('-n or -p is a required argument currently\n')
|
2020-05-20 20:21:08 +00:00
|
|
|
return 1
|
2020-08-25 18:27:46 +00:00
|
|
|
if args.profile is None and len(extra) == 1:
|
|
|
|
args.profile = extra[0]
|
|
|
|
extra = extra[1:]
|
|
|
|
if extra:
|
|
|
|
sys.stderr.write('Unrecognized arguments: ' + repr(extra) + '\n')
|
2020-08-25 18:05:43 +00:00
|
|
|
if not args.clear and args.profile is None:
|
2020-08-25 18:27:46 +00:00
|
|
|
sys.stderr.write('<profile> is a required argument\n')
|
2020-08-25 18:05:43 +00:00
|
|
|
return 1
|
2020-05-20 20:21:08 +00:00
|
|
|
c = client.Command()
|
|
|
|
c.stop_if_noderange_over(args.noderange, args.maxnodes)
|
2020-08-24 20:05:58 +00:00
|
|
|
if args.clear:
|
|
|
|
cleararm(args.noderange, c)
|
|
|
|
clearpending(args.noderange, c)
|
|
|
|
else:
|
|
|
|
armonce(args.noderange, c)
|
|
|
|
setpending(args.noderange, args.profile, c)
|
2020-05-20 20:21:08 +00:00
|
|
|
errnodes = set([])
|
2020-07-01 18:01:28 +00:00
|
|
|
if args.network:
|
|
|
|
rc = c.simple_noderange_command(args.noderange, '/boot/nextdevice', 'network',
|
|
|
|
bootmode='uefi',
|
|
|
|
persistent=False,
|
|
|
|
errnodes=errnodes)
|
|
|
|
if errnodes:
|
|
|
|
sys.stderr.write(
|
|
|
|
'Unable to set boot device for following nodes: {0}\n'.format(
|
|
|
|
','.join(errnodes)))
|
|
|
|
return 1
|
|
|
|
rc |= c.simple_noderange_command(args.noderange, '/power/state', 'boot')
|
|
|
|
return rc
|
2020-05-20 20:21:08 +00:00
|
|
|
return 0
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main(sys.argv[1:]))
|