2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-04-07 01:11:29 +00:00

Fix XCC/XCC2 discovery for async branch

This commit is contained in:
Jarrod Johnson
2024-08-08 14:25:47 -04:00
parent 67a61c5012
commit 536aa7f212
3 changed files with 71 additions and 77 deletions

View File

@@ -1213,7 +1213,7 @@ async def eval_node(cfg, handler, info, nodename, manual=False):
handler.probe() # unicast interrogation as possible to get more data
# switch concurrently
# do some preconfig, for example, to bring a SMM online if applicable
handler.preconfig(nodename)
await handler.preconfig(nodename)
except Exception as e:
unknown_info[info['hwaddr']] = info
info['discostatus'] = 'unidentified'
@@ -1634,10 +1634,8 @@ async def blocking_scan():
global scanner
slpscan = util.spawn(slp.active_scan(safe_detected, slp))
ssdpscan = util.spawn(ssdp.active_scan(safe_detected, ssdp))
print("beign slpscan")
await slpscan
await ssdpscan
print("end scan")
#ssdpscan.wait()
scanner = None

View File

@@ -70,7 +70,7 @@ class NodeHandler(object):
# serial number and uuid to flesh out data as needed
return
def preconfig(self, possiblenode):
async def preconfig(self, possiblenode):
return
def discoverable_by_switch(self, macs):

View File

@@ -157,12 +157,12 @@ class NodeHandler(immhandler.NodeHandler):
# We cannot try to enable SMM here without risking real credentials
# on the wire to untrusted parties
return
wc.grab_json_response('/api/providers/logout')
await wc.grab_json_response('/api/providers/logout')
wc.set_basic_credentials(self._currcreds[0], self._currcreds[1])
rsp = wc.grab_json_response('/redfish/v1/Managers/1/NetworkProtocol')
rsp = await wc.grab_json_response('/redfish/v1/Managers/1/NetworkProtocol')
if not rsp.get('IPMI', {}).get('ProtocolEnabled', True):
disableipmi = True
_, _ = wc.grab_json_response_with_status(
_, _ = await wc.grab_json_response_with_status(
'/redfish/v1/Managers/1/NetworkProtocol',
{'IPMI': {'ProtocolEnabled': True}}, method='PATCH')
ipmicmd = None
@@ -175,13 +175,13 @@ class NodeHandler(immhandler.NodeHandler):
str(e) != 'Session no longer connected'):
# raise an issue if anything other than to be expected
if disableipmi:
_, _ = wc.grab_json_response_with_status(
_, _ = await wc.grab_json_response_with_status(
'/redfish/v1/Managers/1/NetworkProtocol',
{'IPMI': {'ProtocolEnabled': False}}, method='PATCH')
raise
self.trieddefault = True
if disableipmi:
_, _ = wc.grab_json_response_with_status(
_, _ = await wc.grab_json_response_with_status(
'/redfish/v1/Managers/1/NetworkProtocol',
{'IPMI': {'ProtocolEnabled': False}}, method='PATCH')
#TODO: decide how to clean out if important
@@ -196,7 +196,7 @@ class NodeHandler(immhandler.NodeHandler):
return util.cert_matches(fprint, certificate)
async def get_webclient(self, username, password, newpassword):
wc = self._wc.dupe()
wc = self._wc # .dupe()
pwdchanged = False
adata = {'username': util.stringify(username),
'password': util.stringify(password)}
@@ -207,15 +207,16 @@ class NodeHandler(immhandler.NodeHandler):
if status == 200:
nonce = rsp.get('nonce', None)
headers['Content-Security-Policy'] = 'nonce={0}'.format(nonce)
rspdata = await wc.grab_json_response('/api/login', adata, headers=headers)
if rspdata and password == 'PASSW0RD':
rspdata, status = await wc.grab_json_response_with_status('/api/login', adata, headers=headers)
if status != 200 and password == 'PASSW0RD':
rspdata = json.loads(rspdata)
if rspdata.get('locktime', 0) > 0:
raise LockedUserException(
'The user "{0}" has been locked out for too many incorrect password attempts'.format(username))
adata = json.dumps({
adata = {
'username': username,
'password': newpassword,
})
}
headers = {'Connection': 'keep-alive',
'Content-Type': 'application/json'}
if nonce:
@@ -236,7 +237,7 @@ class NodeHandler(immhandler.NodeHandler):
raise LockedUserException(
'The user "{0}" has been locked out for too many incorrect password attempts'.format(username))
return (None, rspdata)
if rsp.status == 200:
if status == 200:
self._currcreds = (username, password)
wc.set_basic_credentials(username, password)
wc.set_header('Content-Type', 'application/json')
@@ -247,24 +248,23 @@ class NodeHandler(immhandler.NodeHandler):
if newpassword is None:
# a normal login hit expired condition
tmppassword = 'Tmp42' + password[5:]
wc.request('POST', '/api/function', json.dumps(
{'USER_UserPassChange': '1,{0}'.format(tmppassword)}))
rsp = wc.getresponse()
rsp.read()
await wc.grab_json_response(
'/api/function',
{'USER_UserPassChange': '1,{0}'.format(tmppassword)})
# We must step down change interval and reusecycle to restore password
wc.grab_json_response('/api/dataset', {'USER_GlobalMinPassChgInt': '0', 'USER_GlobalMinPassReuseCycle': '0'})
wc.request('POST', '/api/function', json.dumps(
{'USER_UserPassChange': '1,{0}'.format(password)}))
rsp = wc.getresponse()
rsp.read()
await wc.grab_json_response(
'/api/dataset',
{'USER_GlobalMinPassChgInt': '0', 'USER_GlobalMinPassReuseCycle': '0'})
await wc.grab_json_response(
'/api/function',
{'USER_UserPassChange': '1,{0}'.format(password)})
return (wc, {})
wc.request('POST', '/api/function', json.dumps(
{'USER_UserPassChange': '1,{0}'.format(newpassword)}))
rsp = wc.getresponse()
rsp.read()
if rsp.status != 200:
rsp, status = await wc.grab_json_response_with_status(
'/api/function',
{'USER_UserPassChange': '1,{0}'.format(newpassword)})
if status != 200:
return (None, None)
wc.grab_json_response_with_status('/api/providers/logout')
await wc.grab_json_response_with_status('/api/providers/logout')
self._currcreds = (username, newpassword)
wc.set_basic_credentials(username, newpassword)
pwdchanged = True
@@ -274,7 +274,7 @@ class NodeHandler(immhandler.NodeHandler):
# Remove the minimum change interval, to allow sane
# password changes after provisional changes
wc = await self.get_wc()
self.set_password_policy('', wc)
await self.set_password_policy('', wc)
return (wc, pwdchanged)
elif rspdata.get('locktime', 0) > 0:
raise LockedUserException(
@@ -286,7 +286,7 @@ class NodeHandler(immhandler.NodeHandler):
isdefault = True
errinfo = {}
if self._wc is None:
ip, port = self.get_web_port_and_ip()
ip, port = await self.get_web_port_and_ip()
await self.get_https_cert()
self._wc = webclient.WebConnection(
ip, port, verifycallback=self.validate_cert)
@@ -355,7 +355,7 @@ class NodeHandler(immhandler.NodeHandler):
raise Exception('The stored confluent password for user "{}" was not accepted by the XCC'.format(user))
raise Exception('Error connecting to webservice: ' + repr(errinfo))
def set_password_policy(self, strruleset, wc):
async def set_password_policy(self, strruleset, wc):
ruleset = {'USER_GlobalMinPassChgInt': '0'}
for rule in strruleset.split(','):
if '=' not in rule:
@@ -376,20 +376,20 @@ class NodeHandler(immhandler.NodeHandler):
if name.lower() == 'reuse':
ruleset['USER_GlobalMinPassReuseCycle'] = value
try:
wc.grab_json_response('/api/dataset', ruleset)
await wc.grab_json_response('/api/dataset', ruleset)
except Exception as e:
print(repr(e))
pass
def _get_next_userid(self, wc):
userinfo = wc.grab_json_response('/api/dataset/imm_users')
async def _get_next_userid(self, wc):
userinfo = await wc.grab_json_response('/api/dataset/imm_users')
userinfo = userinfo['items'][0]['users']
for user in userinfo:
if user['users_user_name'] == '':
return user['users_user_id']
async def _setup_xcc_account(self, username, passwd, wc):
userinfo = wc.grab_json_response('/api/dataset/imm_users')
userinfo = await wc.grab_json_response('/api/dataset/imm_users')
uid = None
for user in userinfo['items'][0]['users']:
if user['users_user_name'] == username:
@@ -404,26 +404,26 @@ class NodeHandler(immhandler.NodeHandler):
raise Exception("XCC has neither the default user nor configured user")
# The following will work if the password is force change or normal..
if self._needpasswordchange and self.tmppasswd != passwd:
wc.grab_json_response('/api/function',
await wc.grab_json_response('/api/function',
{'USER_UserPassChange': '{0},{1}'.format(uid, passwd)})
if username != 'USERID':
rsp, status = wc.grab_json_response_with_status(
rsp, status = await wc.grab_json_response_with_status(
'/api/function',
{'USER_UserModify': '{0},{1},,1,4,0,0,0,0,,8,'.format(uid, username)})
if status == 200 and rsp.get('return', 0) == 762:
rsp, status = wc.grab_json_response_with_status(
rsp, status = await wc.grab_json_response_with_status(
'/api/function',
{'USER_UserModify': '{0},{1},,1,Administrator,0,0,0,0,,8,'.format(uid, username)})
elif status == 200 and rsp.get('return', 0) == 13:
rsp, status = wc.grab_json_response_with_status(
rsp, status = await wc.grab_json_response_with_status(
'/api/function',
{'USER_UserModify': '{0},{1},,1,4,0,0,0,0,,8,,,'.format(uid, username)})
if status == 200 and rsp.get('return', 0) == 13:
wc.grab_json_response('/api/providers/logout')
await wc.grab_json_response('/api/providers/logout')
wc.set_basic_credentials(self._currcreds[0], self._currcreds[1])
status = 503
while status != 200:
rsp, status = wc.grab_json_response_with_status(
rsp, status = await wc.grab_json_response_with_status(
'/redfish/v1/AccountService/Accounts/{0}'.format(uid),
{'UserName': username}, method='PATCH')
if status != 200:
@@ -436,12 +436,12 @@ class NodeHandler(immhandler.NodeHandler):
self._currcreds = (username, passwd)
return
self.tmppasswd = None
wc.grab_json_response('/api/providers/logout')
await wc.grab_json_response('/api/providers/logout')
self._currcreds = (username, passwd)
async def _convert_sha256account(self, user, passwd, wc):
# First check if the specified user is sha256...
userinfo = wc.grab_json_response('/api/dataset/imm_users')
userinfo = await wc.grab_json_response('/api/dataset/imm_users')
curruser = None
uid = None
user = util.stringify(user)
@@ -453,59 +453,55 @@ class NodeHandler(immhandler.NodeHandler):
if curruser.get('users_pass_is_sha256', 0):
self._wc = None
wc = await self.get_wc()
nwc = wc.dupe()
nwc = wc # .dupe()
# Have to convert it for being useful with most Lenovo automation tools
# This requires deleting the account entirely and trying again
tmpuid = self._get_next_userid(wc)
tmpuid = await self._get_next_userid(wc)
try:
tpass = base64.b64encode(os.urandom(9)) + 'Iw47$'
userparams = "{0},6pmu0ezczzcp,{1},1,4,0,0,0,0,,8,".format(tmpuid, tpass)
result = wc.grab_json_response('/api/function', {'USER_UserCreate': userparams})
wc.grab_json_response('/api/providers/logout')
adata = json.dumps({
result = await wc.grab_json_response('/api/function', {'USER_UserCreate': userparams})
await wc.grab_json_response('/api/providers/logout')
adata = {
'username': '6pmu0ezczzcp',
'password': tpass,
})
}
headers = {'Connection': 'keep-alive', 'Content-Type': 'application/json'}
wc.request('POST', '/api/providers/get_nonce', '{}')
rsp = wc.getresponse()
tokbody = rsp.read()
if rsp.status == 200:
rsp = json.loads(tokbody)
rsp, status = await wc.grab_json_response('/api_providers/get_nonce', {})
if status == 200:
nonce = rsp.get('nonce', None)
headers['Content-Security-Policy'] = 'nonce={0}'.format(nonce)
nwc.request('POST', '/api/login', adata, headers)
rsp = nwc.getresponse()
if rsp.status == 200:
rspdata = json.loads(rsp.read())
rsp, status = await nwc.grab_json_response_with_status('/api/login', adata, headers=headers)
if status == 200:
rspdata = rsp
nwc.set_header('Content-Type', 'application/json')
nwc.set_header('Authorization', 'Bearer ' + rspdata['access_token'])
if '_csrf_token' in wc.cookies:
nwc.set_header('X-XSRF-TOKEN', wc.cookies['_csrf_token'])
if rspdata.get('reason', False):
newpass = base64.b64encode(os.urandom(9)) + 'q4J$'
nwc.grab_json_response(
await nwc.grab_json_response(
'/api/function',
{'USER_UserPassChange': '{0},{1}'.format(tmpuid, newpass)})
nwc.grab_json_response('/api/function', {'USER_UserDelete': "{0},{1}".format(curruser['users_user_id'], user)})
await nwc.grab_json_response('/api/function', {'USER_UserDelete': "{0},{1}".format(curruser['users_user_id'], user)})
userparams = "{0},{1},{2},1,4,0,0,0,0,,8,".format(curruser['users_user_id'], user, tpass)
nwc.grab_json_response('/api/function', {'USER_UserCreate': userparams})
nwc.grab_json_response('/api/providers/logout')
await nwc.grab_json_response('/api/function', {'USER_UserCreate': userparams})
await nwc.grab_json_response('/api/providers/logout')
nwc, pwdchanged = await self.get_webclient(user, tpass, passwd)
if not nwc:
if not pwdchanged:
pwdchanged = 'Unknown'
raise Exception('Error converting from sha356account: ' + repr(pwdchanged))
if not pwdchanged:
nwc.grab_json_response(
await nwc.grab_json_response(
'/api/function',
{'USER_UserPassChange': '{0},{1}'.format(curruser['users_user_id'], passwd)})
nwc.grab_json_response('/api/providers/logout')
await nwc.grab_json_response('/api/providers/logout')
finally:
self._wc = None
wc = await self.get_wc()
wc.grab_json_response('/api/function', {'USER_UserDelete': "{0},{1}".format(tmpuid, '6pmu0ezczzcp')})
wc.grab_json_response('/api/providers/logout')
await wc.grab_json_response('/api/function', {'USER_UserDelete': "{0},{1}".format(tmpuid, '6pmu0ezczzcp')})
await wc.grab_json_response('/api/providers/logout')
async def config(self, nodename, reset=False):
self.nodename = nodename
@@ -531,7 +527,7 @@ class NodeHandler(immhandler.NodeHandler):
self.nodename, ['secret.hardwaremanagementuser',
'secret.hardwaremanagementpassword'], decrypt=True)
user, passwd, isdefault = self.get_node_credentials(nodename, creds, 'USERID', 'PASSW0RD')
self.set_password_policy(strruleset, wc)
await self.set_password_policy(strruleset, wc)
if self._atdefaultcreds:
if isdefault and self.tmppasswd:
raise Exception(
@@ -542,16 +538,16 @@ class NodeHandler(immhandler.NodeHandler):
await self._convert_sha256account(user, passwd, wc)
if (cd.get('hardwaremanagement.method', {}).get('value', 'ipmi') != 'redfish'
or cd.get('console.method', {}).get('value', None) == 'ipmi'):
nwc = wc.dupe()
nwc = wc # wc.dupe()
nwc.set_basic_credentials(self._currcreds[0], self._currcreds[1])
rsp = nwc.grab_json_response('/redfish/v1/Managers/1/NetworkProtocol')
rsp = await nwc.grab_json_response('/redfish/v1/Managers/1/NetworkProtocol')
if not rsp.get('IPMI', {}).get('ProtocolEnabled', True):
# User has indicated IPMI support, but XCC is currently disabled
# change XCC to be consistent
_, _ = nwc.grab_json_response_with_status(
_, _ = await nwc.grab_json_response_with_status(
'/redfish/v1/Managers/1/NetworkProtocol',
{'IPMI': {'ProtocolEnabled': True}}, method='PATCH')
rsp, status = nwc.grab_json_response_with_status(
rsp, status = await nwc.grab_json_response_with_status(
'/redfish/v1/AccountService/Accounts/1')
if status == 200:
allowable = rsp.get('AccountTypes@Redfish.AllowableValues', [])
@@ -562,7 +558,7 @@ class NodeHandler(immhandler.NodeHandler):
'AccountTypes': current,
'Password': self._currcreds[1]
}
rsp, status = nwc.grab_json_response_with_status(
rsp, status = await nwc.grab_json_response_with_status(
'/redfish/v1/AccountService/Accounts/1',
updateinf, method='PATCH')
if targbmc and not targbmc.startswith('fe80::'):
@@ -573,7 +569,7 @@ class NodeHandler(immhandler.NodeHandler):
raise exc.NotImplementedException('IPv6 remote config TODO')
netconfig = netutil.get_nic_config(self.configmanager, nodename, ip=targbmc)
newmask = netutil.cidr_to_mask(netconfig['prefix'])
currinfo = wc.grab_json_response('/api/providers/logoninfo')
currinfo = await wc.grab_json_response('/api/providers/logoninfo')
currip = currinfo.get('items', [{}])[0].get('ipv4_address', '')
# do not change the ipv4_config if the current config looks right already
if currip != newip:
@@ -585,7 +581,7 @@ class NodeHandler(immhandler.NodeHandler):
statargs['ENET_IPv4GatewayIPAddr'] = netconfig['ipv4_gateway']
elif not netutil.address_is_local(newip):
raise exc.InvalidArgumentException('Will not remotely configure a device with no gateway')
netset, status = wc.grab_json_response_with_status('/api/dataset', statargs)
netset, status = await wc.grab_json_response_with_status('/api/dataset', statargs)
print(repr(netset))
print(repr(status))
@@ -595,7 +591,7 @@ class NodeHandler(immhandler.NodeHandler):
else:
raise exc.TargetEndpointUnreachable(
'hardwaremanagement.manager must be set to desired address (No IPv6 Link Local detected)')
wc.grab_json_response('/api/providers/logout')
await wc.grab_json_response('/api/providers/logout')
ff = self.info.get('attributes', {}).get('enclosure-form-factor', '')
if ff not in ('dense-computing', [u'dense-computing']):
return