mirror of
https://github.com/xcat2/confluent.git
synced 2026-08-04 00:17:01 +00:00
Add redfish OEM implementation for EUREKA Chassis
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
# Copyright 2026 MEGWARE Computer Vertrieb und Service GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
OEM handler for Megware EUREKA chassis Redfish service.
|
||||
|
||||
The EUREKA chassis is a software-only Redfish service (not a BMC).
|
||||
It manages up to 12 nodes with individual BMCs and up to 10 PSUs.
|
||||
"""
|
||||
|
||||
import aiohmi.redfish.oem.generic as generic
|
||||
import aiohmi.exceptions as exc
|
||||
import aiohmi.constants as const
|
||||
|
||||
|
||||
class OEMHandler(generic.OEMHandler):
|
||||
usegenericsensors = True
|
||||
|
||||
async def get_default_sysurl(self):
|
||||
"""Return the system URL for the first available node.
|
||||
|
||||
The EUREKA enclosure has up to 12 nodes (Node1-Node12).
|
||||
Returns the first enabled/inserted node, or the first node overall.
|
||||
"""
|
||||
if not self._varsysurl and 'Systems' in self._rootinfo:
|
||||
systems = self._rootinfo['Systems']['@odata.id']
|
||||
res = await self.webclient.grab_json_response_with_status(systems)
|
||||
if res[1] == 401:
|
||||
raise exc.PyghmiException('Access Denied')
|
||||
elif res[1] < 200 or res[1] >= 300:
|
||||
raise exc.PyghmiException(repr(res[0]))
|
||||
members = res[0].get('Members', [])
|
||||
if len(members) == 1:
|
||||
self._varsysurl = members[0]['@odata.id']
|
||||
elif len(members) > 1:
|
||||
for member in members:
|
||||
murlinfo, status = await self.webclient.grab_json_response_with_status(
|
||||
member['@odata.id'])
|
||||
if status == 200:
|
||||
if murlinfo.get('Status', {}).get('State') == 'Enabled':
|
||||
self._varsysurl = member['@odata.id']
|
||||
break
|
||||
if not self._varsysurl:
|
||||
self._varsysurl = members[0]['@odata.id']
|
||||
return self._varsysurl
|
||||
|
||||
async def get_system_power_watts(self, fishclient):
|
||||
"""Read node input power from EUREKA sensor endpoints.
|
||||
|
||||
The EUREKA chassis reports node power via Node{N}InputPower
|
||||
sensors under /redfish/v1/Chassis/1/Sensors/ rather than
|
||||
standard Redfish PowerControl.
|
||||
"""
|
||||
totalwatts = 0
|
||||
gotpower = False
|
||||
for sysurl in self._allsysurls:
|
||||
nodeid = sysurl.rstrip('/').rsplit('/', 1)[-1]
|
||||
nodeid = nodeid.replace('Node', '')
|
||||
if not nodeid.isdigit():
|
||||
continue
|
||||
try:
|
||||
sensor_url = '/redfish/v1/Chassis/1/Sensors/Node{}InputPower'.format(nodeid)
|
||||
sensor = await fishclient._do_web_request(sensor_url)
|
||||
if sensor and 'Reading' in sensor:
|
||||
totalwatts += float(sensor['Reading'])
|
||||
gotpower = True
|
||||
except Exception:
|
||||
pass
|
||||
if not gotpower:
|
||||
raise exc.UnsupportedFunctionality(
|
||||
'Node power sensors not available')
|
||||
return totalwatts
|
||||
|
||||
async def _get_cpu_temps(self, fishclient):
|
||||
"""Read CPU temperatures from EUREKA BMC sensor endpoints.
|
||||
|
||||
Reads BMC{N}CPU0Temp and BMC{N}CPU1Temp sensors from
|
||||
/redfish/v1/Chassis/1/Sensors/.
|
||||
"""
|
||||
cputemps = []
|
||||
for sysurl in self._allsysurls:
|
||||
nodeid = sysurl.rstrip('/').rsplit('/', 1)[-1]
|
||||
nodeid = nodeid.replace('Node', '')
|
||||
if not nodeid.isdigit():
|
||||
continue
|
||||
for cpu in ('CPU0', 'CPU1'):
|
||||
try:
|
||||
sensor_url = '/redfish/v1/Chassis/1/Sensors/BMC{}Cpu{}Temp'.format(nodeid, cpu)
|
||||
sensor = await fishclient._do_web_request(sensor_url)
|
||||
if sensor and 'Reading' in sensor:
|
||||
cputemps.append({
|
||||
'name': 'CPU {} Node {}'.format(cpu, nodeid),
|
||||
'value': float(sensor['Reading']),
|
||||
'state_ids': [],
|
||||
'units': const.SensorUnits.Celsius,
|
||||
'imprecision': None,
|
||||
})
|
||||
except Exception:
|
||||
pass
|
||||
return cputemps
|
||||
|
||||
async def reseat_bay(self, bay):
|
||||
"""Power cycle a specific node in the EUREKA enclosure.
|
||||
|
||||
Uses ComputerSystem.Reset with ForceRestart on the target node.
|
||||
bay=-1 (enclosure-level) is not supported.
|
||||
"""
|
||||
if bay == -1:
|
||||
raise exc.UnsupportedFunctionality(
|
||||
'Enclosure-level reset is not supported')
|
||||
nodeurl = '/redfish/v1/Systems/Node{}'.format(bay)
|
||||
await self._do_web_request(
|
||||
nodeurl + '/Actions/ComputerSystem.Reset',
|
||||
{'ResetType': 'ForceRestart'},
|
||||
method='POST')
|
||||
|
||||
async def get_health(self, fishclient, verbose=True):
|
||||
"""Gather health status for the EUREKA chassis and all nodes."""
|
||||
issues = []
|
||||
try:
|
||||
chassis = await self._do_web_request('/redfish/v1/Chassis/1')
|
||||
health = chassis.get('Status', {}).get('Health', 'OK')
|
||||
if health != 'OK':
|
||||
issues.append('Chassis health: {}'.format(health))
|
||||
except Exception:
|
||||
issues.append('Cannot reach chassis health endpoint')
|
||||
|
||||
for sysurl in self._allsysurls:
|
||||
try:
|
||||
sysinfo = await self._do_web_request(sysurl)
|
||||
except Exception:
|
||||
continue
|
||||
state = sysinfo.get('Status', {}).get('State', 'Absent')
|
||||
name = sysurl.rstrip('/').rsplit('/', 1)[-1]
|
||||
if state == 'Absent':
|
||||
issues.append('{}: Absent'.format(name))
|
||||
elif state != 'Enabled':
|
||||
issues.append('{}: {}'.format(name, state))
|
||||
|
||||
health = 0
|
||||
if issues:
|
||||
health = 1
|
||||
return {'badreadings': [], 'health': health}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright 2026 MEGWARE Computer Vertrieb und Service GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
OEM handler for Megware EUREKA chassis Redfish service.
|
||||
"""
|
||||
|
||||
import aiohmi.redfish.oem.megware.eureka as eureka
|
||||
|
||||
|
||||
async def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
|
||||
return await eureka.OEMHandler.create(sysinfo, sysurl, webclient, cache, gpool=cmd._gpool)
|
||||
Reference in New Issue
Block a user