sushy-oem-idrac/sushy_oem_idrac/resources/manager/manager.py

176 lines
5.5 KiB
Python

# 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 logging
import time
import sushy
from sushy.resources import base
from sushy.resources import common
from sushy.resources.oem import base as oem_base
from sushy_oem_idrac import asynchronous
from sushy_oem_idrac import constants
from sushy_oem_idrac import utils
LOG = logging.getLogger(__name__)
class DellManagerActionsField(base.CompositeField):
import_system_configuration = common.ActionField(
lambda key, **kwargs: key.endswith(
'#OemManager.ImportSystemConfiguration'))
class DellManagerExtension(oem_base.OEMResourceBase):
_actions = DellManagerActionsField('Actions')
ACTION_DATA = {
'ShareParameters': {
'Target': 'ALL'
},
'ImportBuffer': None
}
# NOTE(etingof): iDRAC job would fail if this XML has
# insignificant whitespaces
IDRAC_CONFIG_CD = """\
<SystemConfiguration>\
<Component FQDD="%s">\
<Attribute Name="ServerBoot.1#BootOnce">\
%s\
</Attribute>\
<Attribute Name="ServerBoot.1#FirstBootDevice">\
VCD-DVD\
</Attribute>\
</Component>\
</SystemConfiguration>\
"""
IDRAC_CONFIG_FLOPPY = """\
<SystemConfiguration>\
<Component FQDD="%s">\
<Attribute Name="ServerBoot.1#BootOnce">\
%s\
</Attribute>\
<Attribute Name="ServerBoot.1#FirstBootDevice">\
VFDD\
</Attribute>\
</Component>\
</SystemConfiguration>\
"""
IDRAC_MEDIA_TYPES = {
sushy.VIRTUAL_MEDIA_FLOPPY: IDRAC_CONFIG_FLOPPY,
sushy.VIRTUAL_MEDIA_CD: IDRAC_CONFIG_CD
}
RETRY_COUNT = 10
RETRY_DELAY = 15
@property
def import_system_configuration_uri(self):
return self._actions.import_system_configuration.target_uri
def set_virtual_boot_device(self, device, persistent=False,
manager=None, system=None):
"""Set boot device for a node.
Dell iDRAC Redfish implementation does not support setting
boot device to virtual media via standard Redfish means.
However, this still can be done via an OEM extension.
:param device: Boot device. Values are vendor-specific.
:param persistent: Whether to set next-boot, or make the change
permanent. Default: False.
:raises: InvalidParameterValue if Dell OEM extension can't
be used.
:raises: ExtensionError on failure to perform requested
operation
"""
try:
idrac_media = self.IDRAC_MEDIA_TYPES[device]
except KeyError:
raise sushy.exceptions.InvalidParameterValue(
error='Unknown or unsupported device %s' % device)
idrac_media = idrac_media % (
manager.identity, 'Disabled' if persistent else 'Enabled')
action_data = dict(self.ACTION_DATA, ImportBuffer=idrac_media)
# TODO(etingof): figure out if on-time or persistent boot can at
# all be implemented via this OEM call
attempts = self.RETRY_COUNT
rebooted = False
while True:
try:
response = asynchronous.http_call(
self._conn, 'post',
self.import_system_configuration_uri,
data=action_data,
sushy_task_poll_period=1)
LOG.info("Set boot device to %(device)s via "
"Dell OEM magic spell (%(retries)d "
"retries)", {'device': device,
'retries': self.RETRY_COUNT - attempts})
return response
except (sushy.exceptions.ServerSideError,
sushy.exceptions.BadRequestError) as exc:
LOG.warning(
'Dell OEM set boot device failed (attempts left '
'%d): %s', attempts, exc)
errors = exc.body and exc.body.get(
'@Message.ExtendedInfo') or []
for error in errors:
message_id = error.get('MessageId')
LOG.warning('iDRAC error: %s',
error.get('Message', 'Unknown error'))
if message_id == constants.IDRAC_CONFIG_PENDING:
if not rebooted:
LOG.warning(
'Let\'s try to turn it off and on again... '
'This may consume one-time boot settings if '
'set previously!')
utils.reboot_system(system)
rebooted = True
break
elif message_id == constants.IDRAC_JOB_RUNNING:
pass
else:
time.sleep(self.RETRY_DELAY)
if not attempts:
LOG.error('Too many (%d) retries, bailing '
'out.', self.RETRY_COUNT)
raise
attempts -= 1
def get_extension(*args, **kwargs):
return DellManagerExtension