From 9101b07d54dba84baf1f69cdc1a8367b765b9c4d Mon Sep 17 00:00:00 2001 From: Markus Hilger Date: Sat, 18 Jul 2026 05:03:05 +0200 Subject: [PATCH] Report stateless boot completion via new 'booted' status Diskless profiles had the updatestatus callback in onboot.sh commented out because no suitable status existed: 'complete' clears deployment.pendingprofile, which the PXE responder requires to answer the next network boot of a diskless node. Add a 'booted' status that records the pending profile as deployment.profile while leaving pendingprofile armed and skipping autolock, and enable the onboot.sh callback in all diskless profiles. nodedeploy now shows 'pending: (booted)' for a running stateless node. --- .../profiles/default/scripts/onboot.sh | 2 +- .../profiles/default/scripts/onboot.sh | 2 +- .../profiles/default/scripts/onboot.sh | 2 +- .../profiles/default/scripts/onboot.sh | 2 +- .../profiles/default/scripts/onboot.sh | 2 +- confluent_server/confluent/selfservice.py | 25 +++++++++++++------ 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/confluent_osdeploy/el7-diskless/profiles/default/scripts/onboot.sh b/confluent_osdeploy/el7-diskless/profiles/default/scripts/onboot.sh index 818fb3d0..fc61683f 100644 --- a/confluent_osdeploy/el7-diskless/profiles/default/scripts/onboot.sh +++ b/confluent_osdeploy/el7-diskless/profiles/default/scripts/onboot.sh @@ -49,5 +49,5 @@ run_remote_parts onboot.d # Induce execution of remote configuration, e.g. ansible plays in ansible/onboot.d/ run_remote_config onboot.d -#curl -X POST -d 'status: booted' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus +printf 'state: booted\nstatus: booted' | curl -X POST --data-binary @- -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus kill $logshowpid diff --git a/confluent_osdeploy/el8-diskless/profiles/default/scripts/onboot.sh b/confluent_osdeploy/el8-diskless/profiles/default/scripts/onboot.sh index d2d50a7d..9216a005 100644 --- a/confluent_osdeploy/el8-diskless/profiles/default/scripts/onboot.sh +++ b/confluent_osdeploy/el8-diskless/profiles/default/scripts/onboot.sh @@ -70,5 +70,5 @@ if [ -f /run/confluent/onboot_sleep.pid ]; then kill "$sleeppid" rm -f /run/confluent/onboot_sleep.pid fi -#curl -X POST -d 'status: booted' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus +printf 'state: booted\nstatus: booted' | curl -X POST --data-binary @- -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus kill $logshowpid diff --git a/confluent_osdeploy/el9-diskless/profiles/default/scripts/onboot.sh b/confluent_osdeploy/el9-diskless/profiles/default/scripts/onboot.sh index 44e97235..28bf6348 100644 --- a/confluent_osdeploy/el9-diskless/profiles/default/scripts/onboot.sh +++ b/confluent_osdeploy/el9-diskless/profiles/default/scripts/onboot.sh @@ -65,5 +65,5 @@ if [ -f /run/confluent/onboot_sleep.pid ]; then rm -f /run/confluent/onboot_sleep.pid fi -#curl -X POST -d 'status: booted' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus +printf 'state: booted\nstatus: booted' | curl -X POST --data-binary @- -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus kill $logshowpid diff --git a/confluent_osdeploy/suse15-diskless/profiles/default/scripts/onboot.sh b/confluent_osdeploy/suse15-diskless/profiles/default/scripts/onboot.sh index 985d1c02..3b434ae0 100644 --- a/confluent_osdeploy/suse15-diskless/profiles/default/scripts/onboot.sh +++ b/confluent_osdeploy/suse15-diskless/profiles/default/scripts/onboot.sh @@ -29,5 +29,5 @@ run_remote_parts onboot.d # Induce execution of remote configuration, e.g. ansible plays in ansible/onboot.d/ run_remote_config onboot.d -#curl -X POST -d 'status: booted' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus +printf 'state: booted\nstatus: booted' | curl -X POST --data-binary @- -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus kill $logshowpid diff --git a/confluent_osdeploy/ubuntu20.04-diskless/profiles/default/scripts/onboot.sh b/confluent_osdeploy/ubuntu20.04-diskless/profiles/default/scripts/onboot.sh index cc470d6f..a2b1eef3 100644 --- a/confluent_osdeploy/ubuntu20.04-diskless/profiles/default/scripts/onboot.sh +++ b/confluent_osdeploy/ubuntu20.04-diskless/profiles/default/scripts/onboot.sh @@ -36,5 +36,5 @@ run_remote_parts onboot.d # Induce execution of remote configuration, e.g. ansible plays in ansible/onboot.d/ run_remote_config onboot.d -#curl -X POST -d 'status: booted' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus +printf 'state: booted\nstatus: booted' | curl -X POST --data-binary @- -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_mgr/confluent-api/self/updatestatus kill $logshowpid diff --git a/confluent_server/confluent/selfservice.py b/confluent_server/confluent/selfservice.py index c9499a7f..2d20586c 100644 --- a/confluent_server/confluent/selfservice.py +++ b/confluent_server/confluent/selfservice.py @@ -480,10 +480,17 @@ async def handle_request(req, make_response, mimetype): mrsp = await make_response(mimetype, 200, 'Ok') await mrsp.write(b'Accepted') return + stayarmed = False if update['status'] == 'staged': targattr = 'deployment.stagedprofile' elif update['status'] == 'complete': targattr = 'deployment.profile' + elif update['status'] == 'booted': + # a stateless node reporting a successful boot; record the booted + # profile as current, but leave pendingprofile armed since the + # node needs it to network boot the same image on next boot + targattr = 'deployment.profile' + stayarmed = True else: raise Exception('Unknown update status request') currattr = cfg.get_node_attributes(nodename, 'deployment.*').get( @@ -495,17 +502,19 @@ async def handle_request(req, make_response, mimetype): pending = currattr.get('deployment.pendingprofile', {}).get('value', '') updates = {} if pending: - updates['deployment.pendingprofile'] = {'value': ''} - if targattr == 'deployment.profile': - updates['deployment.stagedprofile'] = {'value': ''} - dls = cfg.get_node_attributes(nodename, 'deployment.lock') - dls = dls.get(nodename, {}).get('deployment.lock', {}).get('value', None) - if dls == 'autolock': - updates['deployment.lock'] = 'locked' + if not stayarmed: + updates['deployment.pendingprofile'] = {'value': ''} + if targattr == 'deployment.profile': + updates['deployment.stagedprofile'] = {'value': ''} + dls = cfg.get_node_attributes(nodename, 'deployment.lock') + dls = dls.get(nodename, {}).get('deployment.lock', {}).get('value', None) + if dls == 'autolock': + updates['deployment.lock'] = 'locked' currprof = currattr.get(targattr, {}).get('value', '') if currprof != pending: updates[targattr] = {'value': pending} - await cfg.set_node_attributes({nodename: updates}) + if updates: + await cfg.set_node_attributes({nodename: updates}) return await make_response('text/plain', 200, 'OK', body='OK') else: return await make_response('text/plain', 500, 'Error', body='No pending profile detected, unable to accept status update')