diff --git a/pyghmi/ipmi/oem/lenovo/handler.py b/pyghmi/ipmi/oem/lenovo/handler.py index a70f2f59..8f0e3ddf 100755 --- a/pyghmi/ipmi/oem/lenovo/handler.py +++ b/pyghmi/ipmi/oem/lenovo/handler.py @@ -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) diff --git a/pyghmi/ipmi/oem/lenovo/nextscale.py b/pyghmi/ipmi/oem/lenovo/nextscale.py index cdb337ca..f20ed8bc 100644 --- a/pyghmi/ipmi/oem/lenovo/nextscale.py +++ b/pyghmi/ipmi/oem/lenovo/nextscale.py @@ -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 diff --git a/pyghmi/util/webclient.py b/pyghmi/util/webclient.py index 19503d42..3bd7057d 100644 --- a/pyghmi/util/webclient.py +++ b/pyghmi/util/webclient.py @@ -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)