mirror of
https://github.com/xcat2/confluent.git
synced 2026-07-13 17:38:20 +00:00
Merge pull request #218 from gosforthcross/eureka-chassis-support
Add MEGWARE Eureka Chassis support + Small change for how IPs/Hostnames are handled in the redfish hardwaremanagement plugin
This commit is contained in:
@@ -16,12 +16,14 @@ import aiohmi.redfish.oem.dell.main as dell
|
||||
import aiohmi.redfish.oem.generic as generic
|
||||
import aiohmi.redfish.oem.lenovo.main as lenovo
|
||||
import aiohmi.redfish.oem.ami.main as ami
|
||||
import aiohmi.redfish.oem.megware.main as megware
|
||||
|
||||
OEMMAP = {
|
||||
'Lenovo': lenovo,
|
||||
'Dell': dell,
|
||||
'AMI': ami,
|
||||
'Ami': ami,
|
||||
'Megware': megware,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -78,6 +78,7 @@ import confluent.discovery.handlers.xcc as xcc
|
||||
import confluent.discovery.handlers.xcc3 as xcc3
|
||||
import confluent.discovery.handlers.smm3 as smm3
|
||||
import confluent.discovery.handlers.megarac as megarac
|
||||
import confluent.discovery.handlers.eureka as eureka
|
||||
import confluent.exceptions as exc
|
||||
import confluent.log as log
|
||||
import confluent.messages as msg
|
||||
@@ -121,6 +122,7 @@ nodehandlers = {
|
||||
'lenovo-xcc': xcc,
|
||||
'lenovo-xcc3': xcc3,
|
||||
'megarac-bmc': megarac,
|
||||
'megware-chassis': eureka,
|
||||
'service:management-hardware.IBM:integrated-management-module2': imm,
|
||||
'pxe-client': pxeh,
|
||||
'onie-switch': None,
|
||||
@@ -143,6 +145,7 @@ servicenames = {
|
||||
'lenovo-xcc': 'lenovo-xcc',
|
||||
'lenovo-xcc3': 'lenovo-xcc3',
|
||||
'megarac-bmc': 'megarac-bmc',
|
||||
'megware-chassis': 'megware-chassis',
|
||||
#'openbmc': 'openbmc',
|
||||
'service:management-hardware.IBM:integrated-management-module2': 'lenovo-imm2',
|
||||
'service:io-device.Lenovo:management-module': 'lenovo-switch',
|
||||
@@ -161,6 +164,7 @@ servicebyname = {
|
||||
'lenovo-xcc': 'lenovo-xcc',
|
||||
'lenovo-xcc3': 'lenovo-xcc3',
|
||||
'megarac-bmc': 'megarac-bmc',
|
||||
'megware-chassis': 'megware-chassis',
|
||||
'lenovo-imm2': 'service:management-hardware.IBM:integrated-management-module2',
|
||||
'lenovo-switch': 'service:io-device.Lenovo:management-module',
|
||||
'thinkagile-storage': 'service:thinkagile-storagebmc',
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Discovery handler for Megware EUREKA chassis Redfish service.
|
||||
|
||||
This is a software-only Redfish service (not a BMC). It provides
|
||||
chassis-level information, node inventory, power supply data, and
|
||||
sensor readings. It does NOT support IPMI, NIC configuration,
|
||||
firmware update, or other BMC-specific operations.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import confluent.discovery.handlers.generic as generic
|
||||
import confluent.util as util
|
||||
import aiohmi.util.webclient as webclient
|
||||
|
||||
|
||||
class NodeHandler(generic.NodeHandler):
|
||||
devname = 'EUREKA'
|
||||
is_enclosure = True
|
||||
maxmacs = 42
|
||||
https_supported = True
|
||||
DEFAULT_USER = 'admin'
|
||||
DEFAULT_PASS = 'admin'
|
||||
|
||||
def __init__(self, info, configmanager):
|
||||
self.trieddefault = None
|
||||
self.targuser = None
|
||||
self.curruser = None
|
||||
self.currpass = None
|
||||
self.targpass = None
|
||||
self.nodename = None
|
||||
self.xauthtoken = None
|
||||
self.atdefault = True
|
||||
super(NodeHandler, self).__init__(info, configmanager)
|
||||
|
||||
def get_firmware_default_account_info(self):
|
||||
return (self.DEFAULT_USER, self.DEFAULT_PASS)
|
||||
|
||||
async def scan(self):
|
||||
await self.get_https_cert()
|
||||
try:
|
||||
wc = webclient.WebConnection(
|
||||
self.ipaddr, 443, verifycallback=self.validate_cert)
|
||||
root = await wc.grab_json_response('/redfish/v1/')
|
||||
uuid = root.get('UUID', None)
|
||||
if uuid:
|
||||
self.info['uuid'] = uuid.lower()
|
||||
|
||||
# Read chassis info for model/serial
|
||||
chassis_url = root.get('Chassis', {}).get('@odata.id')
|
||||
if chassis_url:
|
||||
try:
|
||||
chassis_coll = await wc.grab_json_response(chassis_url)
|
||||
members = chassis_coll.get('Members', [])
|
||||
if members:
|
||||
cinfo = await wc.grab_json_response(members[0]['@odata.id'])
|
||||
if cinfo.get('Model'):
|
||||
self.info['modelname'] = cinfo['Model']
|
||||
if cinfo.get('SerialNumber'):
|
||||
self.info['serialnumber'] = cinfo['SerialNumber']
|
||||
if cinfo.get('Manufacturer'):
|
||||
self.info['modelnumber'] = cinfo['Manufacturer']
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def _get_wc(self):
|
||||
await self.get_https_cert()
|
||||
defuser, defpass = self.get_firmware_default_account_info()
|
||||
wc = webclient.WebConnection(
|
||||
self.ipaddr, 443, verifycallback=self.validate_cert)
|
||||
wc.set_header('Content-Type', 'application/json')
|
||||
wc.set_header('Accept', 'application/json')
|
||||
wc.set_header('OData-Version', '4.0')
|
||||
|
||||
if not self.trieddefault:
|
||||
# Attempt authentication with default credentials
|
||||
wc.set_basic_credentials(defuser, defpass)
|
||||
body, status, headers = await wc.grab_response_with_status(
|
||||
'/redfish/v1/SessionService/Sessions',
|
||||
{'UserName': defuser, 'Password': defpass})
|
||||
if status == 201:
|
||||
token = headers.get('X-Auth-Token')
|
||||
if token:
|
||||
self.curruser = defuser
|
||||
self.currpass = defpass
|
||||
self.xauthtoken = token
|
||||
if 'Authorization' in wc.stdheaders:
|
||||
del wc.stdheaders['Authorization']
|
||||
wc.stdheaders['X-Auth-Token'] = token
|
||||
return wc
|
||||
elif status == 401:
|
||||
self.trieddefault = True
|
||||
try:
|
||||
errinfo = util.json_loads(body)
|
||||
for msg in errinfo.get('@Message.ExtendedInfo', []):
|
||||
if 'PasswordChangeRequired' in msg.get('MessageId', ''):
|
||||
chgurl = msg.get('MessageArgs', [None])[0]
|
||||
if chgurl and self.targpass and self.targpass != defpass:
|
||||
wc.set_basic_credentials(defuser, defpass)
|
||||
wc.set_header('If-Match', '*')
|
||||
rsp, chgstatus = await wc.grab_json_response_with_status(
|
||||
chgurl,
|
||||
{'Password': self.targpass},
|
||||
method='PATCH')
|
||||
if chgstatus >= 200 and chgstatus < 300:
|
||||
body, status, headers = \
|
||||
await wc.grab_response_with_status(
|
||||
'/redfish/v1/SessionService/Sessions',
|
||||
{'UserName': defuser,
|
||||
'Password': self.targpass})
|
||||
if status == 201:
|
||||
token = headers.get('X-Auth-Token')
|
||||
if token:
|
||||
self.curruser = defuser
|
||||
self.currpass = self.targpass
|
||||
self.xauthtoken = token
|
||||
if 'Authorization' in wc.stdheaders:
|
||||
del wc.stdheaders['Authorization']
|
||||
wc.stdheaders['X-Auth-Token'] = token
|
||||
return wc
|
||||
except Exception:
|
||||
pass
|
||||
# If no password change scenario, try target password
|
||||
if self.targuser and self.targpass:
|
||||
wc.set_basic_credentials(self.targuser, self.targpass)
|
||||
body, status, headers = await wc.grab_response_with_status(
|
||||
'/redfish/v1/SessionService/Sessions',
|
||||
{'UserName': self.targuser, 'Password': self.targpass})
|
||||
if status == 201:
|
||||
token = headers.get('X-Auth-Token')
|
||||
if token:
|
||||
self.curruser = self.targuser
|
||||
self.currpass = self.targpass
|
||||
self.xauthtoken = token
|
||||
if 'Authorization' in wc.stdheaders:
|
||||
del wc.stdheaders['Authorization']
|
||||
wc.stdheaders['X-Auth-Token'] = token
|
||||
return wc
|
||||
self.trieddefault = True
|
||||
return None
|
||||
|
||||
# We have previously established credentials, try to reuse
|
||||
if self.curruser and self.currpass:
|
||||
wc.set_basic_credentials(self.curruser, self.currpass)
|
||||
body, status, headers = await wc.grab_response_with_status(
|
||||
'/redfish/v1/SessionService/Sessions',
|
||||
{'UserName': self.curruser, 'Password': self.currpass})
|
||||
if status == 201:
|
||||
token = headers.get('X-Auth-Token')
|
||||
if token:
|
||||
self.xauthtoken = token
|
||||
if 'Authorization' in wc.stdheaders:
|
||||
del wc.stdheaders['Authorization']
|
||||
wc.stdheaders['X-Auth-Token'] = token
|
||||
return wc
|
||||
|
||||
# Try target credentials
|
||||
if self.targuser and self.targpass:
|
||||
wc.set_basic_credentials(self.targuser, self.targpass)
|
||||
body, status, headers = await wc.grab_response_with_status(
|
||||
'/redfish/v1/SessionService/Sessions',
|
||||
{'UserName': self.targuser, 'Password': self.targpass})
|
||||
if status == 201:
|
||||
token = headers.get('X-Auth-Token')
|
||||
if token:
|
||||
self.curruser = self.targuser
|
||||
self.currpass = self.targpass
|
||||
self.xauthtoken = token
|
||||
if 'Authorization' in wc.stdheaders:
|
||||
del wc.stdheaders['Authorization']
|
||||
wc.stdheaders['X-Auth-Token'] = token
|
||||
return wc
|
||||
|
||||
return None
|
||||
|
||||
async def config(self, nodename):
|
||||
self.nodename = nodename
|
||||
creds = self.configmanager.get_node_attributes(
|
||||
nodename,
|
||||
['secret.hardwaremanagementuser',
|
||||
'secret.hardwaremanagementpassword'],
|
||||
True)
|
||||
cd = creds.get(nodename, {})
|
||||
defuser, defpass = self.get_firmware_default_account_info()
|
||||
user, passwd, _ = self.get_node_credentials(
|
||||
nodename, creds, defuser, defpass)
|
||||
user = util.stringify(user)
|
||||
passwd = util.stringify(passwd)
|
||||
self.targuser = user
|
||||
self.targpass = passwd
|
||||
|
||||
wc = await self._get_wc()
|
||||
if wc is None:
|
||||
raise Exception('Unable to authenticate to EUREKA chassis')
|
||||
|
||||
# Update credentials if they differ from current
|
||||
authupdate = {}
|
||||
if user != self.curruser:
|
||||
authupdate['UserName'] = user
|
||||
if passwd != self.currpass:
|
||||
authupdate['Password'] = passwd
|
||||
if authupdate:
|
||||
srvroot = await wc.grab_json_response('/redfish/v1/')
|
||||
asrv = srvroot.get('AccountService', {}).get('@odata.id')
|
||||
if asrv:
|
||||
acctinfo = await wc.grab_json_response(asrv)
|
||||
acctsurl = acctinfo.get('Accounts', {}).get('@odata.id')
|
||||
if acctsurl:
|
||||
accts = await wc.grab_json_response(acctsurl)
|
||||
for acctref in accts.get('Members', []):
|
||||
accturl = acctref.get('@odata.id', '')
|
||||
if accturl:
|
||||
acctdata, acctstatus = \
|
||||
await wc.grab_json_response_with_status(accturl)
|
||||
if acctdata.get('UserName') == self.curruser:
|
||||
wc.set_header('If-Match', '*')
|
||||
rsp, status = await wc.grab_json_response_with_status(
|
||||
accturl, authupdate, method='PATCH')
|
||||
if status >= 200 and status < 300:
|
||||
self.curruser = user
|
||||
self.currpass = passwd
|
||||
break
|
||||
|
||||
# Store link-local address if applicable
|
||||
if self.ipaddr.startswith('fe80::'):
|
||||
await self.configmanager.set_node_attributes(
|
||||
{nodename: {'hardwaremanagement.manager': self.ipaddr}})
|
||||
@@ -119,6 +119,9 @@ def _process_snoop(peer, rsp, mac, known_peers, newmacs, peerbymacaddress, byeha
|
||||
elif value.endswith('/DeviceDescription.json'):
|
||||
targurl = '/DeviceDescription.json'
|
||||
targtype = 'lenovo-xcc'
|
||||
elif value.endswith('/redfish/v1/'):
|
||||
targurl = '/redfish/v1/'
|
||||
targtype = 'redfish-server'
|
||||
else:
|
||||
return
|
||||
if handler:
|
||||
@@ -527,12 +530,26 @@ async def check_fish(urldata, port=443, verifycallback=None):
|
||||
data['services'] = ['lenovo-smm3']
|
||||
data['attributes'] = peerinfo
|
||||
return data
|
||||
# NOTE: Fallback for generic redfish services without DeviceDescription.json or other explicit identifier
|
||||
try:
|
||||
peerinfo = await wc.grab_json_response('/redfish/v1/')
|
||||
if peerinfo and 'UUID' in peerinfo:
|
||||
data['services'] = [targtype]
|
||||
data['uuid'] = peerinfo['UUID'].lower()
|
||||
return data
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
url = '/redfish/v1/'
|
||||
peerinfo = await wc.grab_json_response('/redfish/v1/')
|
||||
if url == '/redfish/v1/':
|
||||
if 'UUID' in peerinfo:
|
||||
data['services'] = [targtype]
|
||||
vendor = peerinfo.get('Vendor', '')
|
||||
# NOTE: Specific path for EUREKA MEGWARE Chassis
|
||||
if vendor == 'Megware':
|
||||
data['services'] = ['megware-chassis']
|
||||
else:
|
||||
data['services'] = [targtype]
|
||||
data['uuid'] = peerinfo['UUID'].lower()
|
||||
return data
|
||||
return None
|
||||
|
||||
@@ -230,12 +230,35 @@ def get_conn_params(node, configdata):
|
||||
else:
|
||||
bmc = node
|
||||
bmc = bmc.split('/', 1)[0]
|
||||
# TODO(jbjohnso): check if the end has some number after a : without []
|
||||
# for non default port
|
||||
# NOTE (tselle/gosforthcross): Set default port and try to get port via ':' split notation if available, handles IPv6 and IPv4, does not handle malformed IPs well,
|
||||
# but does strip whitespace for possible problems with whitespace in IPs.
|
||||
bmc = bmc.strip()
|
||||
port = 443
|
||||
if bmc.startswith('['):
|
||||
bracket_end = bmc.find(']')
|
||||
if bracket_end > 0:
|
||||
if len(bmc) > bracket_end + 1 and bmc[bracket_end + 1] == ':':
|
||||
try:
|
||||
port = int(bmc[bracket_end + 2:])
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
bmc = bmc[:bracket_end + 1]
|
||||
elif bmc.count(':') == 1:
|
||||
hostpart, _, portstr = bmc.rpartition(':')
|
||||
try:
|
||||
port = int(portstr)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
bmc = hostpart
|
||||
if not 0 < port <= 65535:
|
||||
# NOTE: Fallback to 443 if port read is not valid
|
||||
port = 443
|
||||
|
||||
return {
|
||||
'username': username,
|
||||
'passphrase': passphrase,
|
||||
'bmc': bmc,
|
||||
'port': port,
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +407,8 @@ class IpmiHandler:
|
||||
persistent_ipmicmds[(node, tenant)] = await IpmiCommandWrapper.create(
|
||||
node, cfg, bmc=connparams['bmc'],
|
||||
userid=connparams['username'],
|
||||
password=connparams['passphrase'])
|
||||
password=connparams['passphrase'],
|
||||
port=connparams['port'])
|
||||
self.loggedin = True
|
||||
self.ipmicmd = persistent_ipmicmds[(node, tenant)]
|
||||
except socket.gaierror as ge:
|
||||
|
||||
@@ -34,6 +34,7 @@ setup(
|
||||
'aiohmi/redfish/oem/dell',
|
||||
'aiohmi/redfish/oem/lenovo',
|
||||
'aiohmi/redfish/oem/ami',
|
||||
'aiohmi/redfish/oem/megware',
|
||||
'aiohmi/util'],
|
||||
scripts=['bin/confluent', 'bin/confluent_selfcheck', 'bin/confluentdbutil', 'bin/collective', 'bin/osdeploy'],
|
||||
data_files=[('/etc/init.d', ['sysvinit/confluent']),
|
||||
|
||||
Reference in New Issue
Block a user