diff --git a/pyghmi/redfish/command.py b/pyghmi/redfish/command.py index 4ca81be9..50285ad4 100644 --- a/pyghmi/redfish/command.py +++ b/pyghmi/redfish/command.py @@ -51,22 +51,6 @@ powerstates = { 'boot': None, } -boot_devices_write = { - 'net': 'Pxe', - 'network': 'Pxe', - 'pxe': 'Pxe', - 'hd': 'Hdd', - 'usb': 'Usb', - 'cd': 'Cd', - 'cdrom': 'Cd', - 'optical': 'Cd', - 'dvd': 'Cd', - 'floppy': 'Floppy', - 'default': 'None', - 'setup': 'BiosSetup', - 'bios': 'BiosSetup', - 'f1': 'BiosSetup', -} boot_devices_read = { 'BiosSetup': 'setup', @@ -684,31 +668,7 @@ class Command(object): :raises: PyghmiException on an error. :returns: dict or True -- If callback is not provided, the response """ - reqbootdev = bootdev - if (bootdev not in boot_devices_write - and bootdev not in boot_devices_read): - raise exc.InvalidParameterValue('Unsupported device %s' - % repr(bootdev)) - bootdev = boot_devices_write.get(bootdev, bootdev) - if bootdev == 'None': - payload = {'Boot': {'BootSourceOverrideEnabled': 'Disabled'}} - else: - payload = {'Boot': { - 'BootSourceOverrideEnabled': 'Continuous' if persist - else 'Once', - 'BootSourceOverrideTarget': bootdev, - }} - if uefiboot is not None: - uefiboot = 'UEFI' if uefiboot else 'Legacy' - payload['BootSourceOverrideMode'] = uefiboot - try: - self._do_web_request(self.sysurl, payload, method='PATCH') - return {'bootdev': reqbootdev} - except Exception: - del payload['BootSourceOverrideMode'] - thetag = self.sysinfo.get('@odata.etag', None) - self._do_web_request(self.sysurl, payload, method='PATCH', etag=thetag) - return {'bootdev': reqbootdev} + return self.oem.set_bootdev(bootdev, persist, uefiboot, self) @property def _biosurl(self): diff --git a/pyghmi/redfish/oem/dell/__init__.py b/pyghmi/redfish/oem/dell/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyghmi/redfish/oem/dell/idrac.py b/pyghmi/redfish/oem/dell/idrac.py new file mode 100644 index 00000000..3ec0df01 --- /dev/null +++ b/pyghmi/redfish/oem/dell/idrac.py @@ -0,0 +1,47 @@ +# Copyright 2022 Lenovo Corporation +# +# 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. + +import pyghmi.redfish.oem.generic as generic + + +class OEMHandler(generic.OEMHandler): + + def __init__(self, sysinfo, sysurl, webclient, cache, gpool=None): + super(OEMHandler, self).__init__(sysinfo, sysurl, webclient, cache, + gpool) + + def set_bootdev(self, bootdev, persist=False, uefiboot=None, + fishclient=None): + # gleaned from web console, under configuration, system settings, + # hardware, first boot device. iDrac presumes that the standard + # explicitly refers only to physical devices. I think the intent + # is the exact opposite for 'removable' media, and thus redirect + # the 'physical' standard to the vFDD/VCD-DVD seen in the idrac + # web gui + if bootdev not in ('floppy', 'cd'): + return super(OEMHandler, self).set_bootdev(bootdev, persist, + uefiboot, fishclient) + payload = {'Attributes': {}} + if persist: + payload['Attributes']['ServerBoot.1.BootOnce'] = 'Disabled' + else: + payload['Attributes']['ServerBoot.1.BootOnce'] = 'Enabled' + if bootdev == 'floppy': + payload['Attributes']['ServerBoot.1.FirstBootDevice'] = 'vFDD' + elif bootdev == 'cd': + payload['Attributes']['ServerBoot.1.FirstBootDevice'] = 'VCD-DVD' + fishclient._do_web_request( + '/redfish/v1/Managers/iDRAC.Embedded.1/Attributes', + payload, method='PATCH') + return {'bootdev': bootdev} diff --git a/pyghmi/redfish/oem/dell/main.py b/pyghmi/redfish/oem/dell/main.py new file mode 100644 index 00000000..2e47baab --- /dev/null +++ b/pyghmi/redfish/oem/dell/main.py @@ -0,0 +1,20 @@ +# Copyright 2022 Lenovo Corporation +# +# 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. + +from pyghmi.redfish.oem.dell import idrac + + +def get_handler(sysinfo, sysurl, webclient, cache, cmd): + return idrac.OEMHandler(sysinfo, sysurl, webclient, cache, + gpool=cmd._gpool) diff --git a/pyghmi/redfish/oem/generic.py b/pyghmi/redfish/oem/generic.py index ca587c6d..db17d930 100644 --- a/pyghmi/redfish/oem/generic.py +++ b/pyghmi/redfish/oem/generic.py @@ -1,4 +1,4 @@ -# Copyright 2019 Lenovo Corporation +# Copyright 2019-2022 Lenovo Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +18,34 @@ import os import pyghmi.exceptions as exc import pyghmi.media as media +boot_devices_write = { + 'net': 'Pxe', + 'network': 'Pxe', + 'pxe': 'Pxe', + 'hd': 'Hdd', + 'usb': 'Usb', + 'cd': 'Cd', + 'cdrom': 'Cd', + 'optical': 'Cd', + 'dvd': 'Cd', + 'floppy': 'Floppy', + 'default': 'None', + 'setup': 'BiosSetup', + 'bios': 'BiosSetup', + 'f1': 'BiosSetup', +} + +boot_devices_read = { + 'BiosSetup': 'setup', + 'Cd': 'optical', + 'Floppy': 'floppy', + 'Hdd': 'hd', + 'None': 'default', + 'Pxe': 'network', + 'Usb': 'usb', + 'SDCard': 'sdcard', +} + class OEMHandler(object): hostnic = None @@ -29,6 +57,54 @@ class OEMHandler(object): self._urlcache = cache self.webclient = webclient + def set_bootdev(self, bootdev, persist=False, uefiboot=None, + fishclient=None): + """Set boot device to use on next reboot + + :param bootdev: + *network -- Request network boot + *hd -- Boot from hard drive + *safe -- Boot from hard drive, requesting 'safe mode' + *optical -- boot from CD/DVD/BD drive + *setup -- Boot into setup utility + *default -- remove any directed boot device request + :param persist: If true, ask that system firmware use this device + beyond next boot. Be aware many systems do not honor + this + :param uefiboot: If true, request UEFI boot explicitly. If False, + request BIOS style boot. + None (default) does not modify the boot mode. + :raises: PyghmiException on an error. + :returns: dict or True -- If callback is not provided, the response + """ + reqbootdev = bootdev + if (bootdev not in boot_devices_write + and bootdev not in boot_devices_read): + raise exc.InvalidParameterValue('Unsupported device %s' + % repr(bootdev)) + bootdev = boot_devices_write.get(bootdev, bootdev) + if bootdev == 'None': + payload = {'Boot': {'BootSourceOverrideEnabled': 'Disabled'}} + else: + payload = {'Boot': { + 'BootSourceOverrideEnabled': 'Continuous' if persist + else 'Once', + 'BootSourceOverrideTarget': bootdev, + }} + if uefiboot is not None: + uefiboot = 'UEFI' if uefiboot else 'Legacy' + payload['BootSourceOverrideMode'] = uefiboot + try: + fishclient._do_web_request(self.sysurl, payload, + method='PATCH') + return {'bootdev': reqbootdev} + except Exception: + del payload['BootSourceOverrideMode'] + thetag = fishclient.sysinfo.get('@odata.etag', None) + fishclient._do_web_request(fishclient.sysurl, payload, method='PATCH', + etag=thetag) + return {'bootdev': reqbootdev} + def _get_cache(self, url): now = os.times()[4] cachent = self._urlcache.get(url, None) diff --git a/pyghmi/redfish/oem/lookup.py b/pyghmi/redfish/oem/lookup.py index 2b692fe0..8c78f6b8 100644 --- a/pyghmi/redfish/oem/lookup.py +++ b/pyghmi/redfish/oem/lookup.py @@ -12,11 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import pyghmi.redfish.oem.dell.main as dell import pyghmi.redfish.oem.generic as generic import pyghmi.redfish.oem.lenovo.main as lenovo OEMMAP = { 'Lenovo': lenovo, + 'Dell': dell, } diff --git a/setup.py.tmpl b/setup.py.tmpl index 654e2c6b..4ec295c7 100644 --- a/setup.py.tmpl +++ b/setup.py.tmpl @@ -26,6 +26,6 @@ setuptools.setup( packages=['pyghmi', 'pyghmi.util', 'pyghmi.ipmi', 'pyghmi.cmd', 'pyghmi.redfish', 'pyghmi.ipmi.private', 'pyghmi.ipmi.oem', 'pyghmi.ipmi.oem.lenovo', 'pyghmi.redfish.oem', - 'pyghmi.redfish.oem.lenovo'], + 'pyghmi.redfish.oem.dell', 'pyghmi.redfish.oem.lenovo'], license='Apache License, Version 2.0')