From 78e5d343e7ee9fd9f507bfcc5301924a67ba8b3c Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 1 Jul 2020 14:01:28 -0400 Subject: [PATCH] Implement a 'staged' phase of profile This allows a limbo where remote deployment is blocked but final deployment is not yet flagged. --- confluent_client/bin/nodedeploy | 34 +++++++++---------- .../confluent/config/attributes.py | 6 ++++ confluent_server/confluent/selfservice.py | 16 ++++++--- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/confluent_client/bin/nodedeploy b/confluent_client/bin/nodedeploy index fcc1593e..9a29fd79 100755 --- a/confluent_client/bin/nodedeploy +++ b/confluent_client/bin/nodedeploy @@ -29,38 +29,38 @@ def armonce(nr, cli): def setpending(nr, profile, cli): for rsp in cli.update('/noderange/{0}/attributes/current'.format(nr), - {'deployment.pendingprofile': profile}): + {'deployment.pendingprofile': profile, 'deployment.stagedprofile': None}): pass def main(args): ap = argparse.ArgumentParser(description='Deploy OS to nodes') - ap.add_argument('-n', '--network', help='Initiate deployment over PXE', action='store_true') + 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') ap.add_argument('-m', '--maxnodes', help='Specifiy a maximum nodes to be deployed') ap.add_argument('noderange', help='Set of nodes to deploy') ap.add_argument('profile', help='Profile name to deploy') args = ap.parse_args(args) - if not args.network: - sys.stderr.write('Currently only network (-n) deployment is supported\n') + if not args.network and not args.prepare: + sys.stderr.write('-n or -p is a required argument currently) return 1 c = client.Command() c.stop_if_noderange_over(args.noderange, args.maxnodes) armonce(args.noderange, c) setpending(args.noderange, args.profile, c) errnodes = set([]) - 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 - - + 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 return 0 if __name__ == '__main__': diff --git a/confluent_server/confluent/config/attributes.py b/confluent_server/confluent/config/attributes.py index 85913a94..e4b5ccf6 100644 --- a/confluent_server/confluent/config/attributes.py +++ b/confluent_server/confluent/config/attributes.py @@ -188,6 +188,12 @@ node = { 'the network boot subsystem what should be offered when a potential ' 'network boot request comes in') }, + 'deployment.stagedprofile': { + 'description': ('A profile that has been staged, but is awaiting final ' + 'boot to be activated. This allows an OS profile to ' + 'remove itself from netboot without indicating ' + 'completion to any watcher.') + }, 'deployment.profile': { 'description': ('The profile that has most recently reported ' 'completion of deployment. Note that an image may opt ' diff --git a/confluent_server/confluent/selfservice.py b/confluent_server/confluent/selfservice.py index a4ccb1dd..afe207a2 100644 --- a/confluent_server/confluent/selfservice.py +++ b/confluent_server/confluent/selfservice.py @@ -162,17 +162,25 @@ def handle_request(env, start_response): yield dumper(sorted(nodes)) elif env['PATH_INFO'] == '/self/updatestatus': update = yaml.safe_load(reqbody) - if update['status'] != 'complete': + if update['status'] == 'staged': + targattr = 'deployment.stagedprofile' + elif update['status'] == 'complete': + targattr = 'deployment.profile' + else raise Exception('Unknown update status request') currattr = cfg.get_node_attributes(nodename, 'deployment.*').get( nodename, {}) - pending = currattr.get('deployment.pendingprofile', {}).get('value', '') + pending = None + if targattr == 'deployment.profile': + pending = currattr.get('deployment.stagedprofile', {}).get('value', '') + if not pending: + pending = currattr.get('deployment.pendingprofile', {}).get('value', '') updates = {} if pending: updates['deployment.pendingprofile'] = {'value': ''} - currprof = currattr.get('deployment.profile', {}).get('value', '') + currprof = currattr.get(targattr, {}).get('value', '') if currprof != pending: - updates['deployment.profile'] = {'value': pending} + updates[targattr] = {'value': pending} cfg.set_node_attributes({nodename: updates}) start_response('200 OK', (('Content-Type', 'text/plain'),)) yield 'OK'