mirror of
https://opendev.org/x/pyghmi
synced 2025-01-27 19:37:44 +00:00
Provide attach remote media function
Provide a function for indicating a remote media by URI. Implements the underpinniings with respect to MegaRAC underpinnings. Change-Id: Icbaa8f91fad26632834335cc29eff57d14ee6450
This commit is contained in:
parent
ddbbe53f78
commit
7d782e7cd5
@ -42,3 +42,9 @@ class InvalidParameterValue(PyghmiException):
|
||||
class BmcErrorException(IpmiException):
|
||||
# This denotes when library detects an invalid BMC behavior
|
||||
pass
|
||||
|
||||
|
||||
class UnsupportedFunctionality(PyghmiException):
|
||||
# Indicates when functionality is requested that is not supported by
|
||||
# current endpoint
|
||||
pass
|
||||
|
@ -1709,3 +1709,18 @@ class Command(object):
|
||||
"""Get graphical console launcher"""
|
||||
self.oem_init()
|
||||
return self._oem.get_graphical_console()
|
||||
|
||||
def attach_remote_media(self, url, username=None, password=None):
|
||||
"""Attach remote media by url
|
||||
|
||||
Given a url, attach remote media (cd/usb image) to the target system.
|
||||
|
||||
:param url: URL to indicate where to find image (protocol support
|
||||
varies by BMC)
|
||||
:param username: Username for endpoint to use when accessing the URL.
|
||||
If applicable, 'domain' would be indicated by '@' or
|
||||
'\' syntax.
|
||||
:param password: Password for endpoint to use when accessing the URL.
|
||||
"""
|
||||
self.oem_init()
|
||||
self._oem.attach_remote_media(url, username, password)
|
||||
|
@ -14,6 +14,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pyghmi.exceptions as exc
|
||||
|
||||
|
||||
class OEMHandler(object):
|
||||
"""Handler class for OEM capabilities.
|
||||
@ -221,3 +223,6 @@ class OEMHandler(object):
|
||||
:param netdata: Dictionary to store additional network data
|
||||
"""
|
||||
return
|
||||
|
||||
def attach_remote_media(self, imagename, username, password):
|
||||
raise exc.UnsupportedFunctionality()
|
||||
|
@ -40,6 +40,7 @@ from pyghmi.ipmi.oem.lenovo import raid_drive
|
||||
import pyghmi.util.webclient as wc
|
||||
|
||||
import socket
|
||||
import struct
|
||||
|
||||
inventory.register_inventory_category(cpu)
|
||||
inventory.register_inventory_category(dimm)
|
||||
@ -114,6 +115,12 @@ led_status = {
|
||||
led_status_default = "Blink"
|
||||
|
||||
|
||||
def _megarac_abbrev_image(name):
|
||||
if len(name) <= 16:
|
||||
return name
|
||||
return name[:16] + name[-4:]
|
||||
|
||||
|
||||
class OEMHandler(generic.OEMHandler):
|
||||
# noinspection PyUnusedLocal
|
||||
def __init__(self, oemid, ipmicmd):
|
||||
@ -121,6 +128,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
# variations. For example System X versus Thinkserver
|
||||
self.oemid = oemid
|
||||
self.ipmicmd = ipmicmd
|
||||
self._has_megarac = None
|
||||
self.oem_inventory_info = None
|
||||
|
||||
def get_video_launchdata(self):
|
||||
@ -543,3 +551,128 @@ class OEMHandler(generic.OEMHandler):
|
||||
ipv6str = ':'.join([ipv6str[x:x+4] for x in xrange(0, 32, 4)])
|
||||
netdata['ipv6_addresses'] = [
|
||||
'{0}/{1}'.format(ipv6str, ipv6_prefix)]
|
||||
|
||||
@property
|
||||
def has_megarac(self):
|
||||
# if there is functionality that is the same for tsm or generic
|
||||
# megarac, then this is appropriate. If there's a TSM specific
|
||||
# preferred, use has_tsm first
|
||||
if self._has_megarac is not None:
|
||||
return self._has_megarac
|
||||
self._has_megarac = False
|
||||
try:
|
||||
rsp = self.ipmicmd.xraw_command(netfn=0x32, command=0x7e)
|
||||
# We don't have a handy classify-only, so use get sel policy
|
||||
# rsp should have a length of one, and be either '\x00' or '\x01'
|
||||
if len(rsp['data'][:]) == 1 and rsp['data'][0] in ('\x00', '\x01'):
|
||||
self._has_megarac = True
|
||||
except pygexc.IpmiException:
|
||||
pass # Means that it's not going to be a megarac
|
||||
return self._has_megarac
|
||||
|
||||
def _set_short_ris_string(self, selector, value):
|
||||
data = (1, selector, 0) + struct.unpack('{0}B'.format(len(value)),
|
||||
value)
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0x9f, data=data)
|
||||
|
||||
def _set_ris_string(self, selector, value):
|
||||
if len(value) > 256:
|
||||
raise pygexc.UnsupportedFunctionality(
|
||||
'Value exceeds 256 characters: {0}'.format(value))
|
||||
padded = value + (256 - len(value)) * '\x00'
|
||||
padded = list(struct.unpack('256B', padded))
|
||||
# 8 = RIS, 4 = hd, 2 = fd, 1 = cd
|
||||
try: # try and clear in-progress if left incomplete
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0x9f,
|
||||
data=(1, selector, 0, 0))
|
||||
except pygexc.IpmiException:
|
||||
pass
|
||||
# set in-progress
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0x9f,
|
||||
data=(1, selector, 0, 1))
|
||||
# now do the set
|
||||
for x in xrange(0, 256, 64):
|
||||
currdata = padded[x:x+64]
|
||||
currchunk = x // 64 + 1
|
||||
cmddata = [1, selector, currchunk] + currdata
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0x9f, data=cmddata)
|
||||
# unset in-progress
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0x9f,
|
||||
data=(1, selector, 0, 0))
|
||||
|
||||
def _megarac_fetch_image_shortnames(self):
|
||||
rsp = self.ipmicmd.xraw_command(netfn=0x32, command=0xd8,
|
||||
data=(7, 1, 0))
|
||||
imgnames = rsp['data'][1:]
|
||||
shortnames = []
|
||||
for idx in xrange(0, len(imgnames), 22):
|
||||
shortnames.append(imgnames[idx+2:idx+22].rstrip('\0'))
|
||||
return shortnames
|
||||
|
||||
def _megarac_media_waitforready(self, imagename):
|
||||
# first, we have, sadly, a 10 second grace period for some invisible
|
||||
# async activity to get far enough long to monitor
|
||||
self.ipmicmd.ipmi_session.pause(10)
|
||||
risenabled = '\x00'
|
||||
mountok = '\xff'
|
||||
while risenabled != '\x01':
|
||||
risenabled = self.ipmicmd.xraw_command(
|
||||
netfn=0x32, command=0x9e, data=(8, 10))['data'][2]
|
||||
while mountok == '\xff':
|
||||
mountok = self.ipmicmd.xraw_command(
|
||||
netfn=0x32, command=0x9e, data=(1, 8))['data'][2]
|
||||
targshortname = _megarac_abbrev_image(imagename)
|
||||
shortnames = self._megarac_fetch_image_shortnames()
|
||||
while targshortname not in shortnames:
|
||||
self.ipmicmd.wait_for_rsp(1)
|
||||
shortnames = self._megarac_fetch_image_shortnames()
|
||||
self.ipmicmd.ipmi_session.pause(10)
|
||||
try:
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0xa0, data=(1, 0))
|
||||
self.ipmicmd.ipmi_session.pause(5)
|
||||
except pygexc.IpmiException:
|
||||
pass
|
||||
|
||||
def _megarac_attach_media(self, proto, username, password, imagename,
|
||||
domain, path, host):
|
||||
# First we must ensure that the RIS is actually enabled
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0x9f, data=(8, 10, 0, 1))
|
||||
if username is not None:
|
||||
self._set_ris_string(3, username)
|
||||
if password is not None:
|
||||
self._set_short_ris_string(4, password)
|
||||
if domain is not None:
|
||||
self._set_ris_string(6, domain)
|
||||
self._set_ris_string(1, path)
|
||||
ip = util.get_ipv4(host)[0]
|
||||
self._set_short_ris_string(2, ip)
|
||||
self._set_short_ris_string(5, proto)
|
||||
# now to restart RIS to have changes take effect...
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0x9f, data=(8, 11))
|
||||
# now to kick off the requested mount
|
||||
self._megarac_media_waitforready(imagename)
|
||||
self._set_ris_string(0, imagename)
|
||||
self.ipmicmd.xraw_command(netfn=0x32, command=0xa0,
|
||||
data=(1, 1))
|
||||
|
||||
def attach_remote_media(self, url, username, password):
|
||||
if self.has_megarac:
|
||||
proto, host, path = util.urlsplit(url)
|
||||
if proto == 'smb':
|
||||
proto = 'cifs'
|
||||
domain = None
|
||||
path, imagename = path.rsplit('/', 1)
|
||||
if username is not None and '@' in username:
|
||||
username, domain = username.split('@', 1)
|
||||
elif username is not None and '\\' in username:
|
||||
domain, username = username.split('\\', 1)
|
||||
try:
|
||||
self._megarac_attach_media(proto, username, password,
|
||||
imagename, domain, path, host)
|
||||
except pygexc.IpmiException as ie:
|
||||
if ie.ipmicode in (0x92, 0x99):
|
||||
# if starting from scratch, this can happen...
|
||||
self._megarac_attach_media(proto, username, password,
|
||||
imagename, domain, path, host)
|
||||
else:
|
||||
raise
|
||||
|
@ -21,6 +21,8 @@ import pyghmi.ipmi.oem.lenovo.handler as lenovo
|
||||
oemmap = {
|
||||
20301: lenovo, # IBM x86 (and System X at Lenovo)
|
||||
19046: lenovo, # Lenovo x86 (e.g. Thinkserver)
|
||||
7154: lenovo, # Technically, standard IPMI, but give lenovo a chance
|
||||
# to check for MegaRAC
|
||||
}
|
||||
|
||||
|
||||
|
@ -678,7 +678,7 @@ class Session(object):
|
||||
raise exc.IpmiException('Session no longer connected')
|
||||
return lastresponse
|
||||
|
||||
def _send_ipmi_net_payload(self, netfn=None, command=None, data=[], code=0,
|
||||
def _send_ipmi_net_payload(self, netfn=None, command=None, data=(), code=0,
|
||||
bridge_request=None,
|
||||
retry=None, delay_xmit=None, timeout=None):
|
||||
if retry is None:
|
||||
@ -966,6 +966,12 @@ class Session(object):
|
||||
self._initsession()
|
||||
self._get_channel_auth_cap()
|
||||
|
||||
@classmethod
|
||||
def pause(cls, timeout):
|
||||
starttime = _monotonic_time()
|
||||
while _monotonic_time() - starttime < timeout:
|
||||
cls.wait_for_rsp(timeout - (_monotonic_time() - starttime))
|
||||
|
||||
@classmethod
|
||||
def wait_for_rsp(cls, timeout=None, callout=True):
|
||||
"""IPMI Session Event loop iteration
|
||||
|
@ -14,6 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import socket
|
||||
import struct
|
||||
|
||||
|
||||
@ -29,3 +30,28 @@ def decode_wireformat_uuid(rawguid):
|
||||
bebytes = struct.unpack_from('>HHI', buffer(rawguid[8:]))
|
||||
return '{0:04X}-{1:02X}-{2:02X}-{3:02X}-{4:02X}{5:04X}'.format(
|
||||
lebytes[0], lebytes[1], lebytes[2], bebytes[0], bebytes[1], bebytes[2])
|
||||
|
||||
|
||||
def urlsplit(url):
|
||||
"""Split an arbitrary url into protocol, host, rest
|
||||
|
||||
The standard urlsplit does not want to provide 'netloc' for arbitrary
|
||||
protocols, this works around that.
|
||||
|
||||
:param url: The url to split into component parts
|
||||
"""
|
||||
proto, rest = url.split(':', 1)
|
||||
host = ''
|
||||
if rest[:2] == '//':
|
||||
host, rest = rest[2:].split('/', 1)
|
||||
rest = '/' + rest
|
||||
return proto, host, rest
|
||||
|
||||
|
||||
def get_ipv4(hostname):
|
||||
"""Get list of ipv4 addresses for hostname
|
||||
|
||||
"""
|
||||
addrinfo = socket.getaddrinfo(hostname, None, socket.AF_INET,
|
||||
socket.SOCK_STREAM)
|
||||
return [addrinfo[x][4][0] for x in xrange(len(addrinfo))]
|
||||
|
Loading…
x
Reference in New Issue
Block a user