From 30c4d6b863e74cd42d974867c68a45c5873f3ecc Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Mon, 5 Aug 2024 11:07:50 -0400 Subject: [PATCH] Add IPMI enablement to generic Redfish handler If attributes indicate desire for IPMI, try to accomodate. --- .../discovery/handlers/redfishbmc.py | 102 +++++++++++++----- .../confluent/discovery/handlers/xcc.py | 2 +- .../confluent/discovery/handlers/xcc3.py | 1 + 3 files changed, 76 insertions(+), 29 deletions(-) diff --git a/confluent_server/confluent/discovery/handlers/redfishbmc.py b/confluent_server/confluent/discovery/handlers/redfishbmc.py index 97629f36..d4e164d6 100644 --- a/confluent_server/confluent/discovery/handlers/redfishbmc.py +++ b/confluent_server/confluent/discovery/handlers/redfishbmc.py @@ -57,8 +57,28 @@ class NodeHandler(generic.NodeHandler): self.csrftok = None self.channel = None self.atdefault = True + self._srvroot = None + self._mgrinfo = None super(NodeHandler, self).__init__(info, configmanager) + def srvroot(self, wc): + if not self._srvroot: + srvroot, status = wc.grab_json_response_with_status('/redfish/v1/') + if status == 200: + self._srvroot = srvroot + return self._srvroot + + def mgrinfo(self, wc): + if not self._mgrinfo: + mgrs = self.srvroot(wc)['Managers']['@odata.id'] + rsp = wc.grab_json_response(mgrs) + if len(rsp['Members']) != 1: + raise Exception("Can not handle multiple Managers") + mgrurl = rsp['Members'][0]['@odata.id'] + self._mgrinfo = wc.grab_json_response(mgrurl) + return self._mgrinfo + + def get_firmware_default_account_info(self): raise Exception('This must be subclassed') @@ -75,6 +95,30 @@ class NodeHandler(generic.NodeHandler): fprint = util.get_fingerprint(self.https_cert) return util.cert_matches(fprint, certificate) + def enable_ipmi(self, wc): + npu = self.mgrinfo(wc).get( + 'NetworkProtocol', {}).get('@odata.id', None) + if not npu: + raise Exception('Cannot enable IPMI, no NetworkProtocol on BMC') + npi = wc.grab_json_response(npu) + if not npi.get('IPMI', {}).get('ProtocolEnabled'): + wc.set_header('If-Match', '*') + wc.grab_json_response_with_status( + npu, {'IPMI': {'ProtocolEnabled': True}}, method='PATCH') + acctinfo = wc.grab_json_response_with_status( + self.target_account_url(wc)) + acctinfo = acctinfo[0] + actypes = acctinfo['AccountTypes'] + candidates = acctinfo['AccountTypes@Redfish.AllowableValues'] + if 'IPMI' not in actypes and 'IPMI' in candidates: + actypes.append('IPMI') + acctupd = { + 'AccountTypes': actypes, + 'Password': self.currpass, + } + rsp = wc.grab_json_response_with_status( + self.target_account_url(wc), acctupd, method='PATCH') + def _get_wc(self): defuser, defpass = self.get_firmware_default_account_info() wc = webclient.SecureHTTPConnection(self.ipaddr, 443, verifycallback=self.validate_cert) @@ -144,13 +188,33 @@ class NodeHandler(generic.NodeHandler): self.currpass = self.targpass return wc + def target_account_url(self, wc): + asrv = self.srvroot(wc).get('AccountService', {}).get('@odata.id') + rsp, status = wc.grab_json_response_with_status(asrv) + accts = rsp.get('Accounts', {}).get('@odata.id') + rsp, status = wc.grab_json_response_with_status(accts) + accts = rsp.get('Members', []) + for accturl in accts: + accturl = accturl.get('@odata.id', '') + if accturl: + rsp, status = wc.grab_json_response_with_status(accturl) + if rsp.get('UserName', None) == self.curruser: + targaccturl = accturl + break + else: + raise Exception("Unable to identify Account URL to modify on this BMC") + return targaccturl + def config(self, nodename): + mgrs = None self.nodename = nodename creds = self.configmanager.get_node_attributes( nodename, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword', - 'hardwaremanagement.manager', 'hardwaremanagement.method', 'console.method'], - True) + 'hardwaremanagement.manager', + 'hardwaremanagement.method', + 'console.method'], + True) cd = creds.get(nodename, {}) defuser, defpass = self.get_firmware_default_account_info() user, passwd, _ = self.get_node_credentials( @@ -160,7 +224,6 @@ class NodeHandler(generic.NodeHandler): self.targuser = user self.targpass = passwd wc = self._get_wc() - srvroot, status = wc.grab_json_response_with_status('/redfish/v1/') curruserinfo = {} authupdate = {} wc.set_header('Content-Type', 'application/json') @@ -169,21 +232,7 @@ class NodeHandler(generic.NodeHandler): if passwd != self.currpass: authupdate['Password'] = passwd if authupdate: - targaccturl = None - asrv = srvroot.get('AccountService', {}).get('@odata.id') - rsp, status = wc.grab_json_response_with_status(asrv) - accts = rsp.get('Accounts', {}).get('@odata.id') - rsp, status = wc.grab_json_response_with_status(accts) - accts = rsp.get('Members', []) - for accturl in accts: - accturl = accturl.get('@odata.id', '') - if accturl: - rsp, status = wc.grab_json_response_with_status(accturl) - if rsp.get('UserName', None) == self.curruser: - targaccturl = accturl - break - else: - raise Exception("Unable to identify Account URL to modify on this BMC") + targaccturl = self.target_account_url(wc) rsp, status = wc.grab_json_response_with_status(targaccturl, authupdate, method='PATCH') if status >= 300: raise Exception("Failed attempting to update credentials on BMC") @@ -193,7 +242,11 @@ class NodeHandler(generic.NodeHandler): while tries and status >= 300: tries -= 1 eventlet.sleep(1.0) - _, status = wc.grab_json_response_with_status('/redfish/v1/Managers') + _, status = wc.grab_json_response_with_status( + '/redfish/v1/Managers') + if (cd.get('hardwaremanagement.method', {}).get('value', 'ipmi') != 'redfish' + or cd.get('console.method', {}).get('value', None) == 'ipmi'): + self.enable_ipmi(wc) if ('hardwaremanagement.manager' in cd and cd['hardwaremanagement.manager']['value'] and not cd['hardwaremanagement.manager']['value'].startswith( @@ -204,14 +257,8 @@ class NodeHandler(generic.NodeHandler): newip = newipinfo[-1][0] if ':' in newip: raise exc.NotImplementedException('IPv6 remote config TODO') - mgrs = srvroot['Managers']['@odata.id'] - rsp = wc.grab_json_response(mgrs) - if len(rsp['Members']) != 1: - raise Exception("Can not handle multiple Managers") - mgrurl = rsp['Members'][0]['@odata.id'] - mginfo = wc.grab_json_response(mgrurl) - hifurls = get_host_interface_urls(wc, mginfo) - mgtnicinfo = mginfo['EthernetInterfaces']['@odata.id'] + hifurls = get_host_interface_urls(wc, self.mgrinfo(wc)) + mgtnicinfo = self.mgrinfo(wc)['EthernetInterfaces']['@odata.id'] mgtnicinfo = wc.grab_json_response(mgtnicinfo) mgtnics = [x['@odata.id'] for x in mgtnicinfo.get('Members', [])] actualnics = [] @@ -240,7 +287,6 @@ class NodeHandler(generic.NodeHandler): rsp, status = wc.grab_json_response_with_status(actualnics[0], { 'DHCPv4': {'DHCPEnabled': False}, 'IPv4StaticAddresses': [newconfig]}, method='PATCH') - elif self.ipaddr.startswith('fe80::'): self.configmanager.set_node_attributes( {nodename: {'hardwaremanagement.manager': self.ipaddr}}) diff --git a/confluent_server/confluent/discovery/handlers/xcc.py b/confluent_server/confluent/discovery/handlers/xcc.py index 49fe1e87..d4d67590 100644 --- a/confluent_server/confluent/discovery/handlers/xcc.py +++ b/confluent_server/confluent/discovery/handlers/xcc.py @@ -639,7 +639,7 @@ def remote_nodecfg(nodename, cfm): ipaddr = ipaddr.split('/', 1)[0] ipaddr = getaddrinfo(ipaddr, 0)[0][-1] if not ipaddr: - raise Excecption('Cannot remote configure a system without known ' + raise Exception('Cannot remote configure a system without known ' 'address') info = {'addresses': [ipaddr]} nh = NodeHandler(info, cfm) diff --git a/confluent_server/confluent/discovery/handlers/xcc3.py b/confluent_server/confluent/discovery/handlers/xcc3.py index 780de4fc..24974172 100644 --- a/confluent_server/confluent/discovery/handlers/xcc3.py +++ b/confluent_server/confluent/discovery/handlers/xcc3.py @@ -24,6 +24,7 @@ getaddrinfo = eventlet.support.greendns.getaddrinfo class NodeHandler(redfishbmc.NodeHandler): + devname = 'XCC' def get_firmware_default_account_info(self): return ('USERID', 'PASSW0RD')