diff --git a/pyghmi/ipmi/command.py b/pyghmi/ipmi/command.py index 1def11fe..9660994e 100644 --- a/pyghmi/ipmi/command.py +++ b/pyghmi/ipmi/command.py @@ -1860,8 +1860,30 @@ class Command(object): :param password: Password for endpoint to use when accessing the URL. """ self.oem_init() - self._oem.attach_remote_media(url, username, password) + return self._oem.attach_remote_media(url, username, password) def detach_remote_media(self): self.oem_init() - self._oem.detach_remote_media() + return self._oem.detach_remote_media() + + def upload_media(self, filename, progress=None): + """Upload a file to be hosted on the target BMC + + This will upload the specified data to + the BMC so that it will make it available to the system as an emulated + USB device. + + :param filename: The filename to use, the basename of the parameter + will be given to the bmc. + :param filename: Optional callback for progress updates + """ + self.oem_init() + return self._oem.upload_media(filename, progress) + + def list_media(self): + """List attached remote media + + :returns: An iterable list of attached media + """ + self.oem_init() + return self._oem.list_media() diff --git a/pyghmi/ipmi/oem/generic.py b/pyghmi/ipmi/oem/generic.py index d3294b0f..59adc749 100644 --- a/pyghmi/ipmi/oem/generic.py +++ b/pyghmi/ipmi/oem/generic.py @@ -245,6 +245,12 @@ class OEMHandler(object): def attach_remote_media(self, imagename, username, password): raise exc.UnsupportedFunctionality() + def upload_media(self, filename, progress): + raise exc.UnsupportedFunctionality() + + def list_media(self): + raise exc.UnsupportedFunctionality() + def set_identify(self, on, duration): """Provide an OEM override for set_identify diff --git a/pyghmi/ipmi/oem/lenovo/handler.py b/pyghmi/ipmi/oem/lenovo/handler.py index 99e73ef8..fdfd540e 100755 --- a/pyghmi/ipmi/oem/lenovo/handler.py +++ b/pyghmi/ipmi/oem/lenovo/handler.py @@ -903,3 +903,13 @@ class OEMHandler(generic.OEMHandler): self.ipmicmd.xraw_command( netfn=0x32, command=0x9f, data=(8, 10, 0, 0)) self.ipmicmd.xraw_command(netfn=0x32, command=0x9f, data=(8, 11)) + + def upload_media(self, filename, progress): + if self.has_xcc: + return self.immhandler.upload_media(filename, progress) + return super(OEMHandler, self).upload_media(filename, progress) + + def list_media(self): + if self.has_xcc: + return self.immhandler.list_media() + return super(OEMHandler, self).list_media() diff --git a/pyghmi/ipmi/oem/lenovo/imm.py b/pyghmi/ipmi/oem/lenovo/imm.py index 84e82404..5dc3afe2 100644 --- a/pyghmi/ipmi/oem/lenovo/imm.py +++ b/pyghmi/ipmi/oem/lenovo/imm.py @@ -24,6 +24,7 @@ import pyghmi.ipmi.oem.lenovo.energy as energy import pyghmi.ipmi.private.session as ipmisession import pyghmi.ipmi.private.util as util import pyghmi.ipmi.sdr as sdr +import pyghmi.media as media import pyghmi.storage as storage import pyghmi.util.webclient as webclient import random @@ -797,6 +798,7 @@ class XCCClient(IMMClient): '{}') if 'return' not in rt or rt['return'] != 0: raise Exception('Unhandled return: ' + repr(rt)) + self.weblogout() def get_firmware_inventory(self, bmcver): # First we fetch the system firmware found in imm properties @@ -887,9 +889,61 @@ class XCCClient(IMMClient): for slot in slots: rt = self.wc.grab_json_response( '/api/providers/rp_vm_remote_unmount', - json.dumps({'Slot': slot})) + json.dumps({'Slot': str(slot)})) if 'return' not in rt or rt['return'] != 0: raise Exception("Unrecognized return: " + repr(rt)) + rdocs = self.wc.grab_json_response('/api/providers/rp_rdoc_imagelist') + for rdoc in rdocs['items']: + filename = rdoc['filename'] + rt = self.wc.grab_json_response('/api/providers/rp_rdoc_unmount', + {'ImageName': filename}) + if rt.get('return', 1) != 0: + raise Exception("Unrecognized return: " + repr(rt)) + self.weblogout() + + def list_media(self): + rt = self.wc.grab_json_response('/api/providers/rp_vm_remote_getdisk') + if 'items' in rt: + for mt in rt['items']: + yield media.Media(mt['filename'], mt['remotepath']) + rt = self.wc.grab_json_response('/api/providers/rp_rdoc_imagelist') + if 'items' in rt: + for mt in rt['items']: + yield media.Media(mt['filename']) + self.weblogout() + + def upload_media(self, filename, progress=None): + xid = random.randint(0, 1000000000) + uploadthread = FileUploader(self.wc, + '/upload?X-Progress-ID={0}'.format(xid), + filename, None) + uploadthread.start() + while uploadthread.isAlive(): + uploadthread.join(3) + rsp = self.wc.grab_json_response( + '/upload/progress?X-Progress-ID={0}'.format(xid)) + if progress and rsp['state'] == 'uploading': + progress({'phase': 'upload', + 'progress': 100.0 * rsp['received'] / rsp['size']}) + self._refresh_token() + rsp = json.loads(uploadthread.rsp) + if progress: + progress({'phase': 'complete'}) + thepath = rsp['items'][0]['path'] + thename = rsp['items'][0]['name'] + writeable = 1 if filename.lower().endswith('.img') else 0 + addfile = {"Url": thepath, "Protocol": 6, "Write": writeable, + "Credential": ":", "Option": "", "Domain": "", + "WebUploadName": thename} + rsp = self.wc.grab_json_response('/api/providers/rp_rdoc_addfile', + addfile) + if rsp['return'] != 0: + raise Exception('Unrecognized return: ' + repr(rsp)) + rsp = self.wc.grab_json_response('/api/providers/rp_rdoc_mountall', + {}) + if rsp['return'] != 0: + raise Exception('Unrecognized return: ' + repr(rsp)) + self.weblogout() def update_firmware(self, filename, data=None, progress=None, bank=None): result = None diff --git a/pyghmi/media.py b/pyghmi/media.py new file mode 100644 index 00000000..2443a577 --- /dev/null +++ b/pyghmi/media.py @@ -0,0 +1,25 @@ + +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 IBM Corporation +# 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. +# 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. + +# These are the objects returned by list_media + + +class Media(object): + def __init__(self, name, url=None): + self.name = name + self.url = url