2
0
mirror of https://opendev.org/x/pyghmi synced 2025-08-23 19:40:19 +00:00

Implement firmware update for ThinkSystem SMM

Provide similar support for ThinkSystem SMM as the XCC
devices.

Change-Id: I9173ef1c509b2965469ee610cca58590fe42cb69
This commit is contained in:
Jarrod Johnson
2017-08-04 16:30:00 -04:00
parent a8e523cc11
commit c341814267
3 changed files with 112 additions and 6 deletions

View File

@@ -154,6 +154,8 @@ class OEMHandler(generic.OEMHandler):
self.immhandler = imm.XCCClient(ipmicmd)
elif self.has_imm:
self.immhandler = imm.IMMClient(ipmicmd)
elif self.is_fpc:
self.smmhandler = nextscale.SMMClient(ipmicmd)
@property
def _megarac_eth_index(self):
@@ -832,6 +834,9 @@ class OEMHandler(generic.OEMHandler):
if self.has_xcc:
return self.immhandler.update_firmware(
filename, data=data, progress=progress)
if self.is_fpc:
return self.smmhandler.update_firmware(
filename, data=data, progress=progress)
super(OEMHandler, self).update_firmware(filename, data=data,
progress=progress)

View File

@@ -16,8 +16,14 @@
import pyghmi.constants as pygconst
import pyghmi.exceptions as pygexc
import pyghmi.ipmi.private.session as ipmisession
import pyghmi.ipmi.sdr as sdr
import pyghmi.util.webclient as webclient
import struct
import urllib
import weakref
from xml.etree.ElementTree import fromstring
import zipfile
try:
range = xrange
@@ -224,3 +230,89 @@ def get_sensor_reading(name, ipmicmd, sz):
'type': sensor['type']},
sensor['units'])
raise Exception('Sensor not found: ' + name)
class SMMClient(object):
def __init__(self, ipmicmd):
self.ipmicmd = weakref.proxy(ipmicmd)
self.smm = ipmicmd.bmc
self.username = ipmicmd.ipmi_session.userid
self.password = ipmicmd.ipmi_session.password
self._wc = None
def get_webclient(self):
cv = self.ipmicmd.certverify
wc = webclient.SecureHTTPConnection(self.smm, 443, verifycallback=cv)
wc.connect()
loginform = urllib.urlencode({'user': self.username,
'password': self.password})
wc.request('POST', '/data/login', loginform)
rsp = wc.getresponse()
if rsp.status != 200:
raise Exception(rsp.read())
authdata = rsp.read()
authdata = fromstring(authdata)
for data in authdata.findall('authResult'):
if int(data.text) != 0:
raise Exception("Firmware update already in progress")
self.st1 = None
self.st2 = None
for data in authdata.findall('st1'):
self.st1 = data.text
for data in authdata.findall('st2'):
self.st2 = data.text
wc.set_header('ST2', self.st2)
return wc
def update_firmware(self, filename, data=None, progress=None):
if progress is None:
progress = lambda x: True
if not data and zipfile.is_zipfile(filename):
z = zipfile.ZipFile(filename)
for tmpname in z.namelist():
if tmpname.endswith('.rom'):
filename = tmpname
data = z.open(filename)
break
progress({'phase': 'upload', 'progress': 0.0})
url = self.wc
url = '/fwupload/fwupload.esp?ST1={0}'.format(self.st1)
self.wc.upload(url, filename, data, formname='fileUpload',
otherfields={'preConfig': 'on'})
progress({'phase': 'validating', 'progress': 0.0})
url = '/data'
self.wc.request('POST', url, 'get=fwVersion,spfwInfo')
rsp = self.wc.getresponse()
rsp.read()
if rsp.status != 200:
raise Exception('Error validating firmware')
progress({'phase': 'apply', 'progress': 0.0})
self.wc.request('POST', '/data', 'set=fwUpdate:1')
rsp = self.wc.getresponse()
rsp.read()
complete = False
while not complete:
ipmisession.Session.pause(3)
self.wc.request('POST', '/data', 'get=fwProgress,fwUpdate')
rsp = self.wc.getresponse()
progdata = rsp.read()
if rsp.status != 200:
raise Exception('Error applying firmware')
progdata = fromstring(progdata)
percent = float(progdata.findall('fwProgress')[0].text)
progress({'phase': 'apply',
'progress': percent})
complete = percent >= 100.0
def logout(self):
self.wc.request('POST', '/data/logout', None)
rsp = self.wc.getresponse()
rsp.read()
self._wc = None
@property
def wc(self):
if not self._wc:
self._wc = self.get_webclient()
return self._wc

View File

@@ -1,4 +1,4 @@
# Copyright 2015 Lenovo
# Copyright 2015-2017 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -39,15 +39,23 @@ BND = 'TbqbLUSn0QFjx9gxiQLtgBK4Zu6ehLqtLs4JOBS50EgxXJ2yoRMhTrmRXxO1lkoAQdZx16'
uploadforms = {}
def get_upload_form(filename, data):
def get_upload_form(filename, data, formname, otherfields):
if not formname:
formname = filename
try:
return uploadforms[filename]
except KeyError:
if isinstance(data, file):
try:
data = data.read()
except AttributeError:
pass
form = '--' + BND + '\r\nContent-Disposition: form-data; ' \
'name="{0}"; filename="{0}"\r\n'.format(filename)
'name="{0}"; filename="{1}"\r\n'.format(formname,
filename)
form += 'Content-Type: application/octet-stream\r\n\r\n' + data
for ofield in otherfields:
form += '\r\n--' + BND + '\r\nContent-Disposition: form-data; ' \
'name="{0}"\r\n\r\n{1}'.format(ofield, otherfields[ofield])
form += '\r\n--' + BND + '--\r\n'
uploadforms[filename] = form
return form
@@ -115,7 +123,8 @@ class SecureHTTPConnection(httplib.HTTPConnection, object):
rsp.read()
return {}
def upload(self, url, filename, data=None):
def upload(self, url, filename, data=None, formname=None,
otherfields=()):
"""Upload a file to the url
:param url:
@@ -126,7 +135,7 @@ class SecureHTTPConnection(httplib.HTTPConnection, object):
"""
if data is None:
data = open(filename, 'rb')
form = get_upload_form(filename, data)
form = get_upload_form(filename, data, formname, otherfields)
ulheaders = self.stdheaders.copy()
ulheaders['Content-Type'] = 'multipart/form-data; boundary=' + BND
self.request('POST', url, form, ulheaders)