From c6f6b8c47f03eaeca8148955908bc8697863ac1e Mon Sep 17 00:00:00 2001 From: Luca Miccini Date: Sat, 12 Oct 2024 11:00:43 +0200 Subject: [PATCH] Add BaremetalHost annotation-based fencing This commit adds one more way of fencing a compute node, using baremetal apis (metal3). It works by adding a reboot annotation, see: https://book.metal3.io/bmo/reboot_annotation.html {"annotations":{"reboot.metal3.io/iha":"{\"mode\": \"hard\"}"}} After the evacuation is completed this annotation is removed and the compute host powered back on. fencing.yaml should contain something like: FencingConfig: edpm-compute-1: agent: bmh token: host: edpm-compute-1 The token is obtained with something like the following: kubectl create serviceaccount k8sadmin -n kube-system kubectl create clusterrolebinding k8sadmin --clusterrole=cluster-admin --serviceaccount=kube-system:k8sadmin kubectl -n kube-system describe secret $(sudo kubectl -n kube-system get secret | (grep k8sadmin || echo "$_") | awk '{print $1}') | grep token: | awk '{print $2}' Customers should create a serviceaccount that has enough rights to perform operations on the baremetalhost resources instead of using a cluster-admin. --- templates/instanceha/bin/instanceha.py | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/templates/instanceha/bin/instanceha.py b/templates/instanceha/bin/instanceha.py index 8b722df0..c29311ff 100755 --- a/templates/instanceha/bin/instanceha.py +++ b/templates/instanceha/bin/instanceha.py @@ -498,6 +498,21 @@ def _redfish_reset(url, user, passwd, timeout, action): r = requests.post(url, data=json.dumps(payload), headers=headers, auth=(user, passwd), verify=False, timeout=timeout) return r +def _bmh_fence(token, host, action): + + url = "https://kubernetes.default.svc/apis/metal3.io/v1alpha1/namespaces/openstack/baremetalhosts/%s?fieldManager=kubectl-patch" % host + headers={'Authorization': 'Bearer '+token, 'Content-Type': 'application/merge-patch+json'} + cacert = '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt' + + if action == 'off': + ann={"metadata":{"annotations":{"reboot.metal3.io/iha":"{\"mode\": \"hard\"}"}}} + r = requests.patch(url, headers=headers, verify=cacert, data=json.dumps(ann)) + return r + + else: + ann={"metadata":{"annotations":{"reboot.metal3.io/iha":None}}} + r = requests.patch(url, headers=headers, verify=cacert, data=json.dumps(ann)) + return r def _host_fence(host, action): logging.info('Fencing host %s %s' % (host, action)) @@ -589,6 +604,26 @@ def _host_fence(host, action): logging.warning('Could not power on %s' % host) #return True + elif 'bmh' in fencing_data["agent"]: + + token = str(fencing_data["token"]) + host = str(fencing_data["host"]) + + if action == 'off': + r = _bmh_fence(token, host, "off") + if r.status_code == 200: + logging.info('Power off of %s ok' % host) + return True + else: + logging.error('Could not power off %s' % host) + return False + else: + r = _bmh_fence(token, host, "on") + if r.status_code == 200: + logging.info('Power on of %s ok' % host) + else: + logging.warning('Could not power on %s' % host) + else: logging.error('No valid fencing method detected for %s' % host) return False